1
0

refactor parse + unit test

This commit is contained in:
2026-02-21 15:44:33 +02:00
parent 6a3a7541ea
commit 306633d01e
4 changed files with 454 additions and 303 deletions

View File

@@ -25,6 +25,7 @@ add_executable(wavefront_tests
tests/unit/trim_test.cpp
tests/unit/settings_test.cpp
tests/unit/scan_test.cpp
tests/unit/parse_test.cpp
${APP_SOURCES}
)
target_link_libraries(wavefront_tests PRIVATE

View File

@@ -55,313 +55,15 @@ namespace wavefront {
wavefront_face_data_result_t parse_face_data(const scan_result &scan_data);
template<typename FloatType>
coordinate_data_t<FloatType> parse_coordinate_data(const scan_result &scan_data, const wavefront_face_data_result_t &face_data) {
using namespace std::string_literals;
coordinate_data_t<FloatType> result;
const auto &position_data = scan_data.category_map.at("v"s);
for (const auto &position_index : face_data.index_position_set) {
std::array<FloatType, 3> position_coordinates;
const auto &position_line_index = position_data.at(position_index);
const auto &position_line = scan_data.line_data.at(position_line_index);
decltype(position_line.find_first_of(' ')) number_begin_pos = 2, number_end_pos;
for (std::size_t position_coordinate_index = 0; position_coordinate_index < 3; position_coordinate_index++) {
if (number_begin_pos >= position_line.size()) {
throw parse_error(std::format(
"[{}]: {}",
position_line_index,
std::format("Line \"{}\" must contain at least {} numbers", "v", 3)
));
}
number_end_pos = std::min(position_line.find_first_of(' ', number_begin_pos), position_line.size());
auto [number_end, conversion_error] = std::from_chars(
position_line.data() + number_begin_pos,
position_line.data() + number_end_pos,
position_coordinates[position_coordinate_index],
std::chars_format::fixed
);
if (conversion_error != std::errc() || position_line.data() + number_end_pos != number_end) {
throw parse_error(std::format(
"[{}]: {}",
position_line_index,
std::format("Unable to parse \"{}\" floating point value", "v")
));
}
number_begin_pos = std::min(position_line.find_first_not_of(' ', number_end_pos), position_line.size());
}
if (number_begin_pos < position_line.size()) {
if (position_line.find_first_not_of("0123456789.-", number_begin_pos)) {
throw parse_error(std::format(
"[{}]: {}",
position_line_index,
std::format("Additional data in \"{}\" line: expected {} only", "v", "<x> <y> <z> [weight]")
));
}
}
result.position_coordinates[position_line_index] = position_coordinates;
}
if (face_data.index_normal_set.size() > 0 && scan_data.category_map.contains("vn"s)) {
const auto &normal_data = scan_data.category_map.at("vn"s);
for (const auto &normal_index : face_data.index_normal_set) {
std::array<FloatType, 3> normal_coordinates;
const auto &normal_line_index = normal_data.at(normal_index);
const auto &normal_line = scan_data.line_data.at(normal_line_index);
decltype(normal_line.find_first_of(' ')) number_begin_pos = 3, number_end_pos;
for (std::size_t normal_coordinate_index = 0; normal_coordinate_index < 3; normal_coordinate_index++) {
if (number_begin_pos >= normal_line.size()) {
throw parse_error(std::format(
"[{}]: {}",
normal_line_index,
std::format("Line \"{}\" must contain exactly {} numbers", "vn", 3)
));
}
number_end_pos = std::min(normal_line.find_first_of(' ', number_begin_pos), normal_line.size());
auto [number_end, conversion_error] = std::from_chars(
normal_line.data() + number_begin_pos,
normal_line.data() + number_end_pos,
normal_coordinates[normal_coordinate_index],
std::chars_format::fixed
);
if (conversion_error != std::errc() || normal_line.data() + number_end_pos != number_end) {
throw parse_error(std::format(
"[{}]: {}",
normal_line_index,
std::format("Unable to parse \"{}\" floating point value", "vn")
));
}
number_begin_pos = std::min(normal_line.find_first_not_of(' ', number_end_pos), normal_line.size());
}
if (number_end_pos != normal_line.size()) {
throw parse_error(std::format(
"[{}]: {}",
normal_line_index,
std::format("Additional data in \"{}\" line: expected {} only", "vn", "<x> <y> <z>")
));
}
result.normal_coordinates[normal_line_index] = normal_coordinates;
}
}
if (face_data.index_texcoord_set.size() > 0 && scan_data.category_map.contains("vt"s)) {
using insert_texcoord_data_t = std::function<void()>;
const auto &texcoord_data = scan_data.category_map.at("vt"s);
for (const auto &texcoord_index : face_data.index_texcoord_set) {
std::array<FloatType, 3> texcoord_coordinates;
const auto &texcoord_line_index = texcoord_data.at(texcoord_index);
const auto &texcoord_line = scan_data.line_data.at(texcoord_line_index);
decltype(texcoord_line.find_first_of(' ')) number_begin_pos = 3, number_end_pos;
std::size_t texcoord_coordinate_index = 0;
while (number_begin_pos < texcoord_line.size()) {
if (texcoord_coordinate_index >= 3) {
throw parse_error(std::format(
"[{}]: {}",
texcoord_line_index,
std::format("Line \"{}\" must contain maximum {} numbers", "vt", 3)
));
}
number_end_pos = std::min(texcoord_line.find_first_of(' ', number_begin_pos), texcoord_line.size());
auto [number_end, conversion_error] = std::from_chars(
texcoord_line.data() + number_begin_pos,
texcoord_line.data() + number_end_pos,
texcoord_coordinates[texcoord_coordinate_index++],
std::chars_format::fixed
);
if (conversion_error != std::errc() || texcoord_line.data() + number_end_pos != number_end) {
throw parse_error(std::format(
"[{}]: {}",
texcoord_line_index,
std::format("Unable to parse \"{}\" floating point value", "vt")
));
}
number_begin_pos = std::min(texcoord_line.find_first_not_of(' ', number_end_pos), texcoord_line.size());
}
if (texcoord_coordinate_index == 0) [[unlikely]] {
throw parse_error(std::format(
"[{}]: {}",
texcoord_line_index,
std::format("Unable to parse \"{}\" line: expected minimum one number", "vt")
));
}
if (result.texture_coordinates.valueless_by_exception() || result.texture_coordinates.index() == 0) [[unlikely]] {
if (texcoord_coordinate_index == 1) {
result.texture_coordinates = std::map<file_line_t, FloatType>{};
} else if (texcoord_coordinate_index == 2) {
result.texture_coordinates = std::map<file_line_t, std::array<FloatType, 2>>{};
} else if (texcoord_coordinate_index == 3) {
result.texture_coordinates = std::map<file_line_t, std::array<FloatType, 3>>{};
} else {
throw parse_error(std::format(
"[{}]: {}",
texcoord_line_index,
std::format("Line \"{}\" must contain maximum {} numbers", "vt", 3)
));
}
}
if (texcoord_coordinate_index == 0) [[unlikely]] {
throw parse_error(std::format(
"[{}]: {}",
texcoord_line_index,
std::format("Unable to parse \"{}\" line: expected minimum one number", "vt")
));
}
try {
if (texcoord_coordinate_index == 1) {
std::get<1>(result.texture_coordinates)[texcoord_line_index] = texcoord_coordinates[0];
} else if (texcoord_coordinate_index == 2) {
std::get<2>(result.texture_coordinates)[texcoord_line_index] = std::array<FloatType, 2>{texcoord_coordinates[0], texcoord_coordinates[1]};
} else if (texcoord_coordinate_index == 3) {
std::get<3>(result.texture_coordinates)[texcoord_line_index] = texcoord_coordinates;
}
} catch (std::bad_variant_access&) {
throw parse_error(std::format(
"[{}]: {}",
texcoord_line_index,
std::format("Line \"{}\": {}D texture coordinates expected, got {}D texture coordinates", "vt", result.texture_coordinates.index(), texcoord_coordinate_index)
));
}
}
}
return result;
}
coordinate_data_t<FloatType> parse_coordinate_data(const scan_result &scan_data, const wavefront_face_data_result_t &face_data);
template<typename FloatType>
std::vector<FloatType> create_number_list(const coordinate_data_t<FloatType> &coordinate_data) {
numset_t<FloatType> number_set;
for (const auto& [line_number, coordinates] : coordinate_data.position_coordinates) {
for (const auto &coordinate : coordinates) {
number_set.emplace(coordinate);
}
}
for (const auto& [line_number, coordinates] : coordinate_data.normal_coordinates) {
for (const auto &coordinate : coordinates) {
number_set.emplace(coordinate);
}
}
std::visit([&](const auto &texture_coordinate_map) {
if constexpr (std::ranges::range<std::decay_t<decltype(texture_coordinate_map)>>) {
for (const auto& [line_number, coordinates] : texture_coordinate_map) {
if constexpr (std::ranges::range<std::decay_t<decltype(coordinates)>>) {
for (const auto &coordinate : coordinates) {
number_set.emplace(coordinate);
}
} else {
number_set.emplace(coordinates);
}
}
}
}, coordinate_data.texture_coordinates);
number_set.emplace(std::numeric_limits<FloatType>::quiet_NaN());
std::vector<FloatType> number_list;
number_list.reserve(number_set.size());
std::copy(number_set.begin(), number_set.end(), std::back_inserter(number_list));
return number_list;
}
std::vector<FloatType> create_number_list(const coordinate_data_t<FloatType> &coordinate_data);
template<typename FloatType, typename IndexType>
coordinate_data_t<IndexType> create_coordinate_index(const coordinate_data_t<FloatType> &coordinate_data, const std::vector<FloatType> &float_list) {
coordinate_data_t<IndexType> coordinate_index_data;
static const auto float_compare = float_is_equal<FloatType>{};
static const auto float_less = less_with_nan_first_and_nearly_equal<FloatType>{};
for (const auto& [line_number, coordinates] : coordinate_data.position_coordinates) {
std::array<IndexType, coordinates.size()> position_coordinate_indices;
for (decltype(coordinates.size()) dim = 0; dim < coordinates.size(); ++dim) {
auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates[dim], float_less);
assert(iterator != float_list.end());
assert(float_compare(*iterator, coordinates[dim]));
auto index = std::distance(float_list.begin(), iterator);
assert(index != 0);
position_coordinate_indices[dim] = index;
}
coordinate_index_data.position_coordinates[line_number] = position_coordinate_indices;
}
for (const auto& [line_number, coordinates] : coordinate_data.normal_coordinates) {
std::array<IndexType, coordinates.size()> normal_coordinate_indices;
for (decltype(coordinates.size()) dim = 0; dim < coordinates.size(); ++dim) {
auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates[dim], float_less);
assert(iterator != float_list.end());
assert(float_compare(*iterator, coordinates[dim]));
auto index = std::distance(float_list.begin(), iterator);
assert(index != 0);
normal_coordinate_indices[dim] = index;
}
coordinate_index_data.normal_coordinates[line_number] = normal_coordinate_indices;
}
if (!coordinate_data.texture_coordinates.valueless_by_exception() && coordinate_data.texture_coordinates.index() > 0) {
std::visit([&](const auto &texture_coordinate_map) {
if constexpr (std::ranges::range<std::decay_t<decltype(texture_coordinate_map)>>) {
if constexpr (std::ranges::range<typename std::decay_t<decltype(texture_coordinate_map)>::mapped_type>) {
coordinate_index_data.texture_coordinates = std::map<
typename std::decay_t<decltype(texture_coordinate_map)>::key_type,
std::array<
IndexType,
std::tuple_size_v<typename std::decay_t<decltype(texture_coordinate_map)>::mapped_type>
>
>{};
} else {
coordinate_index_data.texture_coordinates = std::map<
typename std::decay_t<decltype(texture_coordinate_map)>::key_type,
IndexType
>{};
}
for (const auto& [line_number, coordinates] : texture_coordinate_map) {
if constexpr (std::ranges::range<std::decay_t<decltype(coordinates)>>) {
std::array<IndexType, coordinates.size()> texcoord_coordinate_indices;
for (std::size_t dim = 0; dim < coordinates.size(); ++dim) {
auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates[dim], float_less);
assert(iterator != float_list.end());
assert(float_compare(*iterator, coordinates[dim]));
auto index = std::distance(float_list.begin(), iterator);
assert(index != 0);
texcoord_coordinate_indices[dim] = index;
}
std::visit([&](auto &target_data) {
if constexpr (std::is_same_v<
std::map<
file_line_t,
std::array<
IndexType,
std::tuple_size_v<typename std::decay_t<decltype(texture_coordinate_map)>::mapped_type>
>
>,
typename std::decay_t<decltype(target_data)>
>) {
target_data[line_number] = texcoord_coordinate_indices;
}
}, coordinate_index_data.texture_coordinates);
} else {
auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates, float_less);
assert(iterator != float_list.end());
assert(float_compare(*iterator, coordinates));
auto coordinate = std::distance(float_list.begin(), iterator);
assert(coordinate != 0);
std::visit([&](auto &target_data) {
if constexpr (std::is_same_v<
std::map<file_line_t, IndexType>,
typename std::decay_t<decltype(target_data)>
>) {
target_data[line_number] = coordinate;
}
}, coordinate_index_data.texture_coordinates);
}
}
}
int j = 0;
}, coordinate_data.texture_coordinates);
coordinate_data_t<IndexType> create_coordinate_index(const coordinate_data_t<FloatType> &coordinate_data, const std::vector<FloatType> &float_list);
}
return coordinate_index_data;
}
}
#include "parse.tpp"
#endif // __WAVEFRONT_PARSE_HPP__

319
src/parse.tpp Normal file
View File

@@ -0,0 +1,319 @@
#ifndef __WAVEFRONT_PARSE_TPP__
#define __WAVEFRONT_PARSE_TPP__
#include <algorithm>
#include <limits>
#include <ranges>
namespace wavefront {
template<typename FloatType>
coordinate_data_t<FloatType> parse_coordinate_data(const scan_result &scan_data, const wavefront_face_data_result_t &face_data) {
using namespace std::string_literals;
coordinate_data_t<FloatType> result;
const auto &position_data = scan_data.category_map.at("v"s);
for (const auto &position_index : face_data.index_position_set) {
std::array<FloatType, 3> position_coordinates;
const auto &position_line_index = position_data.at(position_index);
const auto &position_line = scan_data.line_data.at(position_line_index);
decltype(position_line.find_first_of(' ')) number_begin_pos = 2, number_end_pos;
for (std::size_t position_coordinate_index = 0; position_coordinate_index < 3; position_coordinate_index++) {
if (number_begin_pos >= position_line.size()) {
throw parse_error(std::format(
"[{}]: {}",
position_line_index,
std::format("Line \"{}\" must contain at least {} numbers", "v", 3)
));
}
number_end_pos = std::min(position_line.find_first_of(' ', number_begin_pos), position_line.size());
auto [number_end, conversion_error] = std::from_chars(
position_line.data() + number_begin_pos,
position_line.data() + number_end_pos,
position_coordinates[position_coordinate_index],
std::chars_format::fixed
);
if (conversion_error != std::errc() || position_line.data() + number_end_pos != number_end) {
throw parse_error(std::format(
"[{}]: {}",
position_line_index,
std::format("Unable to parse \"{}\" floating point value", "v")
));
}
number_begin_pos = std::min(position_line.find_first_not_of(' ', number_end_pos), position_line.size());
}
if (number_begin_pos < position_line.size()) {
if (position_line.find_first_not_of("0123456789.-", number_begin_pos)) {
throw parse_error(std::format(
"[{}]: {}",
position_line_index,
std::format("Additional data in \"{}\" line: expected {} only", "v", "<x> <y> <z> [weight]")
));
}
}
result.position_coordinates[position_line_index] = position_coordinates;
}
if (face_data.index_normal_set.size() > 0 && scan_data.category_map.contains("vn"s)) {
const auto &normal_data = scan_data.category_map.at("vn"s);
for (const auto &normal_index : face_data.index_normal_set) {
std::array<FloatType, 3> normal_coordinates;
const auto &normal_line_index = normal_data.at(normal_index);
const auto &normal_line = scan_data.line_data.at(normal_line_index);
decltype(normal_line.find_first_of(' ')) number_begin_pos = 3, number_end_pos;
for (std::size_t normal_coordinate_index = 0; normal_coordinate_index < 3; normal_coordinate_index++) {
if (number_begin_pos >= normal_line.size()) {
throw parse_error(std::format(
"[{}]: {}",
normal_line_index,
std::format("Line \"{}\" must contain exactly {} numbers", "vn", 3)
));
}
number_end_pos = std::min(normal_line.find_first_of(' ', number_begin_pos), normal_line.size());
auto [number_end, conversion_error] = std::from_chars(
normal_line.data() + number_begin_pos,
normal_line.data() + number_end_pos,
normal_coordinates[normal_coordinate_index],
std::chars_format::fixed
);
if (conversion_error != std::errc() || normal_line.data() + number_end_pos != number_end) {
throw parse_error(std::format(
"[{}]: {}",
normal_line_index,
std::format("Unable to parse \"{}\" floating point value", "vn")
));
}
number_begin_pos = std::min(normal_line.find_first_not_of(' ', number_end_pos), normal_line.size());
}
if (number_end_pos != normal_line.size()) {
throw parse_error(std::format(
"[{}]: {}",
normal_line_index,
std::format("Additional data in \"{}\" line: expected {} only", "vn", "<x> <y> <z>")
));
}
result.normal_coordinates[normal_line_index] = normal_coordinates;
}
}
if (face_data.index_texcoord_set.size() > 0 && scan_data.category_map.contains("vt"s)) {
using insert_texcoord_data_t = std::function<void()>;
const auto &texcoord_data = scan_data.category_map.at("vt"s);
for (const auto &texcoord_index : face_data.index_texcoord_set) {
std::array<FloatType, 3> texcoord_coordinates;
const auto &texcoord_line_index = texcoord_data.at(texcoord_index);
const auto &texcoord_line = scan_data.line_data.at(texcoord_line_index);
decltype(texcoord_line.find_first_of(' ')) number_begin_pos = 3, number_end_pos;
std::size_t texcoord_coordinate_index = 0;
while (number_begin_pos < texcoord_line.size()) {
if (texcoord_coordinate_index >= 3) {
throw parse_error(std::format(
"[{}]: {}",
texcoord_line_index,
std::format("Line \"{}\" must contain maximum {} numbers", "vt", 3)
));
}
number_end_pos = std::min(texcoord_line.find_first_of(' ', number_begin_pos), texcoord_line.size());
auto [number_end, conversion_error] = std::from_chars(
texcoord_line.data() + number_begin_pos,
texcoord_line.data() + number_end_pos,
texcoord_coordinates[texcoord_coordinate_index++],
std::chars_format::fixed
);
if (conversion_error != std::errc() || texcoord_line.data() + number_end_pos != number_end) {
throw parse_error(std::format(
"[{}]: {}",
texcoord_line_index,
std::format("Unable to parse \"{}\" floating point value", "vt")
));
}
number_begin_pos = std::min(texcoord_line.find_first_not_of(' ', number_end_pos), texcoord_line.size());
}
if (texcoord_coordinate_index == 0) [[unlikely]] {
throw parse_error(std::format(
"[{}]: {}",
texcoord_line_index,
std::format("Unable to parse \"{}\" line: expected minimum one number", "vt")
));
}
if (result.texture_coordinates.valueless_by_exception() || result.texture_coordinates.index() == 0) [[unlikely]] {
if (texcoord_coordinate_index == 1) {
result.texture_coordinates = std::map<file_line_t, FloatType>{};
} else if (texcoord_coordinate_index == 2) {
result.texture_coordinates = std::map<file_line_t, std::array<FloatType, 2>>{};
} else if (texcoord_coordinate_index == 3) {
result.texture_coordinates = std::map<file_line_t, std::array<FloatType, 3>>{};
} else {
throw parse_error(std::format(
"[{}]: {}",
texcoord_line_index,
std::format("Line \"{}\" must contain maximum {} numbers", "vt", 3)
));
}
}
if (texcoord_coordinate_index == 0) [[unlikely]] {
throw parse_error(std::format(
"[{}]: {}",
texcoord_line_index,
std::format("Unable to parse \"{}\" line: expected minimum one number", "vt")
));
}
try {
if (texcoord_coordinate_index == 1) {
std::get<1>(result.texture_coordinates)[texcoord_line_index] = texcoord_coordinates[0];
} else if (texcoord_coordinate_index == 2) {
std::get<2>(result.texture_coordinates)[texcoord_line_index] = std::array<FloatType, 2>{texcoord_coordinates[0], texcoord_coordinates[1]};
} else if (texcoord_coordinate_index == 3) {
std::get<3>(result.texture_coordinates)[texcoord_line_index] = texcoord_coordinates;
}
} catch (std::bad_variant_access&) {
throw parse_error(std::format(
"[{}]: {}",
texcoord_line_index,
std::format("Line \"{}\": {}D texture coordinates expected, got {}D texture coordinates", "vt", result.texture_coordinates.index(), texcoord_coordinate_index)
));
}
}
}
return result;
}
template<typename FloatType>
std::vector<FloatType> create_number_list(const coordinate_data_t<FloatType> &coordinate_data) {
numset_t<FloatType> number_set;
for (const auto& [line_number, coordinates] : coordinate_data.position_coordinates) {
for (const auto &coordinate : coordinates) {
number_set.emplace(coordinate);
}
}
for (const auto& [line_number, coordinates] : coordinate_data.normal_coordinates) {
for (const auto &coordinate : coordinates) {
number_set.emplace(coordinate);
}
}
std::visit([&](const auto &texture_coordinate_map) {
if constexpr (std::ranges::range<std::decay_t<decltype(texture_coordinate_map)>>) {
for (const auto& [line_number, coordinates] : texture_coordinate_map) {
if constexpr (std::ranges::range<std::decay_t<decltype(coordinates)>>) {
for (const auto &coordinate : coordinates) {
number_set.emplace(coordinate);
}
} else {
number_set.emplace(coordinates);
}
}
}
}, coordinate_data.texture_coordinates);
number_set.emplace(std::numeric_limits<FloatType>::quiet_NaN());
std::vector<FloatType> number_list;
number_list.reserve(number_set.size());
std::copy(number_set.begin(), number_set.end(), std::back_inserter(number_list));
return number_list;
}
template<typename FloatType, typename IndexType>
coordinate_data_t<IndexType> create_coordinate_index(const coordinate_data_t<FloatType> &coordinate_data, const std::vector<FloatType> &float_list) {
coordinate_data_t<IndexType> coordinate_index_data;
static const auto float_compare = float_is_equal<FloatType>{};
static const auto float_less = less_with_nan_first_and_nearly_equal<FloatType>{};
for (const auto& [line_number, coordinates] : coordinate_data.position_coordinates) {
std::array<IndexType, coordinates.size()> position_coordinate_indices;
for (decltype(coordinates.size()) dim = 0; dim < coordinates.size(); ++dim) {
auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates[dim], float_less);
assert(iterator != float_list.end());
assert(float_compare(*iterator, coordinates[dim]));
auto index = std::distance(float_list.begin(), iterator);
assert(index != 0);
position_coordinate_indices[dim] = index;
}
coordinate_index_data.position_coordinates[line_number] = position_coordinate_indices;
}
for (const auto& [line_number, coordinates] : coordinate_data.normal_coordinates) {
std::array<IndexType, coordinates.size()> normal_coordinate_indices;
for (decltype(coordinates.size()) dim = 0; dim < coordinates.size(); ++dim) {
auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates[dim], float_less);
assert(iterator != float_list.end());
assert(float_compare(*iterator, coordinates[dim]));
auto index = std::distance(float_list.begin(), iterator);
assert(index != 0);
normal_coordinate_indices[dim] = index;
}
coordinate_index_data.normal_coordinates[line_number] = normal_coordinate_indices;
}
if (!coordinate_data.texture_coordinates.valueless_by_exception() && coordinate_data.texture_coordinates.index() > 0) {
std::visit([&](const auto &texture_coordinate_map) {
if constexpr (std::ranges::range<std::decay_t<decltype(texture_coordinate_map)>>) {
if constexpr (std::ranges::range<typename std::decay_t<decltype(texture_coordinate_map)>::mapped_type>) {
coordinate_index_data.texture_coordinates = std::map<
typename std::decay_t<decltype(texture_coordinate_map)>::key_type,
std::array<
IndexType,
std::tuple_size_v<typename std::decay_t<decltype(texture_coordinate_map)>::mapped_type>
>
>{};
} else {
coordinate_index_data.texture_coordinates = std::map<
typename std::decay_t<decltype(texture_coordinate_map)>::key_type,
IndexType
>{};
}
for (const auto& [line_number, coordinates] : texture_coordinate_map) {
if constexpr (std::ranges::range<std::decay_t<decltype(coordinates)>>) {
std::array<IndexType, coordinates.size()> texcoord_coordinate_indices;
for (std::size_t dim = 0; dim < coordinates.size(); ++dim) {
auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates[dim], float_less);
assert(iterator != float_list.end());
assert(float_compare(*iterator, coordinates[dim]));
auto index = std::distance(float_list.begin(), iterator);
assert(index != 0);
texcoord_coordinate_indices[dim] = index;
}
std::visit([&](auto &target_data) {
if constexpr (std::is_same_v<
std::map<
file_line_t,
std::array<
IndexType,
std::tuple_size_v<typename std::decay_t<decltype(texture_coordinate_map)>::mapped_type>
>
>,
typename std::decay_t<decltype(target_data)>
>) {
target_data[line_number] = texcoord_coordinate_indices;
}
}, coordinate_index_data.texture_coordinates);
} else {
auto iterator = std::lower_bound(float_list.begin(), float_list.end(), coordinates, float_less);
assert(iterator != float_list.end());
assert(float_compare(*iterator, coordinates));
auto coordinate = std::distance(float_list.begin(), iterator);
assert(coordinate != 0);
std::visit([&](auto &target_data) {
if constexpr (std::is_same_v<
std::map<file_line_t, IndexType>,
typename std::decay_t<decltype(target_data)>
>) {
target_data[line_number] = coordinate;
}
}, coordinate_index_data.texture_coordinates);
}
}
}
int j = 0;
}, coordinate_data.texture_coordinates);
}
return coordinate_index_data;
}
}
#endif // __WAVEFRONT_PARSE_TPP__

129
tests/unit/parse_test.cpp Normal file
View File

@@ -0,0 +1,129 @@
#include <variant>
#include <gtest/gtest.h>
#include "parse.hpp"
#include "scan.hpp"
namespace {
TEST(ParseFaceData, BuildsTrianglesAndIndexSets) {
wavefront::scan_result scan_result;
scan_result.total_lines = 26;
scan_result.line_data = {
{17u, "f 1/1/1 2/2/2 3/3/2"},
{19u, "f 4/3/2 5/2/1 6/1/2"},
{21u, "f 7/2/2 8/3/2 9/1/1"},
{23u, "f 8/1/1 5/2/2 3/3/2"},
{25u, "f 2/1/1 6/2/2 4/3/2"}
};
scan_result.category_map = {
{"f", {17u, 19u, 21u, 23u, 25u}}
};
const auto face_data = wavefront::parse_face_data(scan_result);
EXPECT_EQ(face_data.triangle_list.size(), 5u);
EXPECT_EQ(face_data.index_position_set.size(), 9u);
EXPECT_TRUE(face_data.index_position_set.contains(1));
EXPECT_TRUE(face_data.index_position_set.contains(9));
EXPECT_EQ(face_data.index_normal_set.size(), 2u);
EXPECT_TRUE(face_data.index_normal_set.contains(1));
EXPECT_TRUE(face_data.index_normal_set.contains(2));
EXPECT_EQ(face_data.index_texcoord_set.size(), 3u);
EXPECT_TRUE(face_data.index_texcoord_set.contains(1));
EXPECT_TRUE(face_data.index_texcoord_set.contains(3));
const auto &first_triangle = face_data.triangle_list.at(0);
EXPECT_EQ(first_triangle[0].face_line_number, 17u);
EXPECT_EQ(first_triangle[0].position_index, 1);
EXPECT_EQ(first_triangle[0].texcoord_index, 1);
EXPECT_EQ(first_triangle[0].normal_index, 1);
}
TEST(ParseCoordinateData, MapsCoordinatesWithoutParsingFaces) {
wavefront::scan_result scan_result;
scan_result.total_lines = 26;
scan_result.line_data = {
{3u, "v 0.1 0.2 0.3"},
{4u, "v 0.2 0.3 0.4"},
{5u, "v 0.3 0.4 0.5"},
{6u, "v 1.1 1.2 1.3"},
{7u, "v 1.2 1.3 1.4"},
{8u, "v 1.3 1.4 1.5"},
{9u, "v 2.1 2.2 2.3"},
{10u, "v 2.2 2.3 2.4"},
{11u, "v 2.3 2.4 2.5"},
{12u, "vn 0.15 0.25 0.35"},
{13u, "vn 0.25 0.35 0.45"},
{14u, "vn 0.35 0.45 0.55"},
{15u, "vt 0.9 0.8"},
{16u, "vt 0.8 0.7"},
{17u, "f 1/1/1 2/2/2 3/3/2"},
{19u, "f 4/3/2 5/2/1 6/1/2"},
{21u, "f 7/2/2 8/3/2 9/1/1"},
{23u, "f 8/1/1 5/2/2 3/3/2"},
{25u, "f 2/1/1 6/2/2 4/3/2"}
};
scan_result.category_map = {
{"v", {0u, 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u}},
{"vn", {0u, 12u, 13u, 14u}},
{"vt", {0u, 15u, 16u}},
{"f", {17u, 19u, 21u, 23u, 25u}}
};
wavefront::wavefront_face_data_result_t face_data;
face_data.index_position_set = {1, 2, 3, 4, 5, 6, 7, 8, 9};
face_data.index_normal_set = {1, 2, 3};
face_data.index_texcoord_set = {1, 2};
face_data.triangle_list = {
{{{17u, 1, 1, 1}, {17u, 2, 2, 2}, {17u, 3, 3, 2}}},
{{{19u, 4, 3, 2}, {19u, 5, 2, 1}, {19u, 6, 1, 2}}},
{{{21u, 7, 2, 2}, {21u, 8, 3, 2}, {21u, 9, 1, 1}}},
{{{23u, 8, 1, 1}, {23u, 5, 2, 2}, {23u, 3, 3, 2}}},
{{{25u, 2, 1, 1}, {25u, 6, 2, 2}, {25u, 4, 3, 2}}}
};
const auto coordinate_data = wavefront::parse_coordinate_data<float>(scan_result, face_data);
ASSERT_EQ(coordinate_data.position_coordinates.size(), 9u);
const auto &pos_line_2 = coordinate_data.position_coordinates.at(3u);
EXPECT_FLOAT_EQ(pos_line_2[0], 0.1f);
EXPECT_FLOAT_EQ(pos_line_2[1], 0.2f);
EXPECT_FLOAT_EQ(pos_line_2[2], 0.3f);
const auto &pos_line_5 = coordinate_data.position_coordinates.at(6u);
EXPECT_FLOAT_EQ(pos_line_5[0], 1.1f);
EXPECT_FLOAT_EQ(pos_line_5[1], 1.2f);
EXPECT_FLOAT_EQ(pos_line_5[2], 1.3f);
const auto &pos_line_10 = coordinate_data.position_coordinates.at(11u);
EXPECT_FLOAT_EQ(pos_line_10[0], 2.3f);
EXPECT_FLOAT_EQ(pos_line_10[1], 2.4f);
EXPECT_FLOAT_EQ(pos_line_10[2], 2.5f);
ASSERT_EQ(coordinate_data.normal_coordinates.size(), 3u);
const auto &normal_line_11 = coordinate_data.normal_coordinates.at(12u);
EXPECT_FLOAT_EQ(normal_line_11[0], 0.15f);
EXPECT_FLOAT_EQ(normal_line_11[1], 0.25f);
EXPECT_FLOAT_EQ(normal_line_11[2], 0.35f);
const auto &normal_line_13 = coordinate_data.normal_coordinates.at(14u);
EXPECT_FLOAT_EQ(normal_line_13[0], 0.35f);
EXPECT_FLOAT_EQ(normal_line_13[1], 0.45f);
EXPECT_FLOAT_EQ(normal_line_13[2], 0.55f);
ASSERT_EQ(coordinate_data.texture_coordinates.index(), 2u);
const auto &texcoords = std::get<2>(coordinate_data.texture_coordinates);
ASSERT_EQ(texcoords.size(), 2u);
const auto &tex_line_14 = texcoords.at(15u);
EXPECT_FLOAT_EQ(tex_line_14[0], 0.9f);
EXPECT_FLOAT_EQ(tex_line_14[1], 0.8f);
const auto &tex_line_15 = texcoords.at(16u);
EXPECT_FLOAT_EQ(tex_line_15[0], 0.8f);
EXPECT_FLOAT_EQ(tex_line_15[1], 0.7f);
}
}