PrusaSlicer-NonPlainar/tests/libslic3r/test_emboss.cpp
2022-02-23 13:29:26 +01:00

240 lines
8.0 KiB
C++

#include <catch2/catch.hpp>
#include <libslic3r/Emboss.hpp>
#include <libslic3r/SVG.hpp> // only debug visualization
#include <optional>
#include <libslic3r/AABBTreeIndirect.hpp>
using namespace Slic3r;
namespace Private{
// calculate multiplication of ray dir to intersect - inspired by
// segment_segment_intersection when ray dir is normalized retur distance from
// ray point to intersection No value mean no intersection
std::optional<double> ray_segment_intersection(const Vec2d &r_point,
const Vec2d &r_dir,
const Vec2d &s0,
const Vec2d &s1)
{
auto denominate = [](const Vec2d &v0, const Vec2d &v1) -> double {
return v0.x() * v1.y() - v1.x() * v0.y();
};
Vec2d segment_dir = s1 - s0;
double d = denominate(segment_dir, r_dir);
if (std::abs(d) < std::numeric_limits<double>::epsilon())
// Line and ray are collinear.
return {};
Vec2d s12 = s0 - r_point;
double s_number = denominate(r_dir, s12);
bool change_sign = false;
if (d < 0.) {
change_sign = true;
d = -d;
s_number = -s_number;
}
if (s_number < 0. || s_number > d)
// Intersection outside of segment.
return {};
double r_number = denominate(segment_dir, s12);
if (change_sign) r_number = -r_number;
if (r_number < 0.)
// Intersection before ray start.
return {};
return r_number / d;
}
Vec2d get_intersection(const Vec2d & point,
const Vec2d & dir,
const std::array<Vec2d, 3> &triangle)
{
std::optional<double> t;
for (size_t i = 0; i < 3; ++i) {
size_t i2 = i + 1;
if (i2 == 3) i2 = 0;
if (!t.has_value()) {
t = ray_segment_intersection(point, dir, triangle[i],
triangle[i2]);
continue;
}
// small distance could be preccission inconsistance
std::optional<double> t2 = ray_segment_intersection(point, dir,
triangle[i],
triangle[i2]);
if (t2.has_value() && *t2 > *t) t = t2;
}
assert(t.has_value()); // Not found intersection.
return point + dir * (*t);
}
Vec3d calc_hit_point(const igl::Hit & h,
const Vec3i & triangle,
const std::vector<Vec3f> &vertices)
{
double c1 = h.u;
double c2 = h.v;
double c0 = 1.0 - c1 - c2;
Vec3d v0 = vertices[triangle[0]].cast<double>();
Vec3d v1 = vertices[triangle[1]].cast<double>();
Vec3d v2 = vertices[triangle[2]].cast<double>();
return v0 * c0 + v1 * c1 + v2 * c2;
}
Vec3d calc_hit_point(const igl::Hit &h, indexed_triangle_set &its)
{
return calc_hit_point(h, its.indices[h.id], its.vertices);
}
} // namespace Private
TEST_CASE("Emboss text - Times MacOs", "[Emboss]") {
std::string font_path = "C:/Users/filip/Downloads/Times.ttc";
char letter = 'C';
float flatness = 2.;
auto font = Emboss::load_font(font_path.c_str());
REQUIRE(font != nullptr);
std::optional<Emboss::Glyph> glyph = Emboss::letter2glyph(*font, letter, flatness);
REQUIRE(glyph.has_value());
ExPolygons shape = glyph->shape;
REQUIRE(!shape.empty());
}
#include <libslic3r/Utils.hpp>
TEST_CASE("Emboss text", "[Emboss]")
{
std::string font_path = std::string(TEST_DATA_DIR) + "/fonts/NotoSans-Regular.ttf";
char letter = '%';
float flatness = 2.;
auto font = Emboss::load_font(font_path.c_str());
REQUIRE(font != nullptr);
std::optional<Emboss::Glyph> glyph = Emboss::letter2glyph(*font, letter, flatness);
REQUIRE(glyph.has_value());
ExPolygons shape = glyph->shape;
REQUIRE(!shape.empty());
float z_depth = 1.f;
Emboss::ProjectZ projection(z_depth);
indexed_triangle_set its = Emboss::polygons2model(shape, projection);
CHECK(!its.indices.empty());
}
TEST_CASE("Test hit point", "[AABBTreeIndirect]")
{
indexed_triangle_set its;
its.vertices = {
Vec3f(1, 1, 1),
Vec3f(2, 10, 2),
Vec3f(10, 0, 2),
};
its.indices = {Vec3i(0, 2, 1)};
auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(
its.vertices, its.indices);
Vec3d ray_point(8, 1, 0);
Vec3d ray_dir(0, 0, 1);
igl::Hit hit;
AABBTreeIndirect::intersect_ray_first_hit(its.vertices, its.indices, tree,
ray_point, ray_dir, hit);
Vec3d hp = Private::calc_hit_point(hit, its);
CHECK(abs(hp.x() - ray_point.x()) < .1);
CHECK(abs(hp.y() - ray_point.y()) < .1);
}
TEST_CASE("ray segment intersection", "[MeshBoolean]")
{
Vec2d r_point(1, 1);
Vec2d r_dir(1, 0);
// colinear
CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(0, 0), Vec2d(2, 0)).has_value());
CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(2, 0), Vec2d(0, 0)).has_value());
// before ray
CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(0, 0), Vec2d(0, 2)).has_value());
CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(0, 2), Vec2d(0, 0)).has_value());
// above ray
CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(2, 2), Vec2d(2, 3)).has_value());
CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(2, 3), Vec2d(2, 2)).has_value());
// belove ray
CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(2, 0), Vec2d(2, -1)).has_value());
CHECK(!Private::ray_segment_intersection(r_point, r_dir, Vec2d(2, -1), Vec2d(2, 0)).has_value());
// intersection at [2,1] distance 1
auto t1 = Private::ray_segment_intersection(r_point, r_dir, Vec2d(2, 0), Vec2d(2, 2));
REQUIRE(t1.has_value());
auto t2 = Private::ray_segment_intersection(r_point, r_dir, Vec2d(2, 2), Vec2d(2, 0));
REQUIRE(t2.has_value());
CHECK(abs(*t1 - *t2) < std::numeric_limits<double>::epsilon());
}
TEST_CASE("triangle intersection", "[]")
{
Vec2d point(1, 1);
Vec2d dir(-1, 0);
std::array<Vec2d, 3> triangle = {Vec2d(0, 0), Vec2d(5, 0), Vec2d(0, 5)};
Vec2d i = Private::get_intersection(point, dir, triangle);
CHECK(abs(i.x()) < std::numeric_limits<double>::epsilon());
CHECK(abs(i.y() - 1.) < std::numeric_limits<double>::epsilon());
}
#ifndef __APPLE__
#include <string>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
TEST_CASE("Italic check", "[]")
{
std::queue<std::string> dir_paths;
#ifdef _WIN32
dir_paths.push("C:/Windows/Fonts");
#elif defined(__linux__)
dir_paths.push("/usr/share/fonts");
#endif
bool exist_italic = false;
bool exist_non_italic = false;
while (!dir_paths.empty()) {
std::string dir_path = dir_paths.front();
dir_paths.pop();
for (const auto &entry : fs::directory_iterator(dir_path)) {
const fs::path &act_path = entry.path();
if (entry.is_directory()) {
dir_paths.push(act_path.u8string());
continue;
}
std::string ext = act_path.extension().u8string();
std::transform(ext.begin(), ext.end(), ext.begin(),
[](unsigned char c) { return std::tolower(c); });
if (ext != ".ttf") continue;
std::string path_str = act_path.u8string();
auto font_opt = Emboss::load_font(path_str.c_str());
if (font_opt == nullptr) continue;
if (Emboss::is_italic(*font_opt))
exist_italic = true;
else
exist_non_italic = true;
//std::cout << ((Emboss::is_italic(*font_opt)) ? "[yes] " : "[no ] ") << entry.path() << std::endl;
}
}
CHECK(exist_italic);
CHECK(exist_non_italic);
}
#endif // not __APPLE__