PrusaSlicer-NonPlainar/src/libslic3r/Emboss.hpp
2022-06-09 15:07:44 +02:00

302 lines
11 KiB
C++

#ifndef slic3r_Emboss_hpp_
#define slic3r_Emboss_hpp_
#include <vector>
#include <set>
#include <optional>
#include <memory>
#include <admesh/stl.h> // indexed_triangle_set
#include "Polygon.hpp"
#include "ExPolygon.hpp"
#include "TextConfiguration.hpp"
namespace Slic3r {
/// <summary>
/// class with only static function add ability to engraved OR raised
/// text OR polygons onto model surface
/// </summary>
class Emboss
{
public:
Emboss() = delete;
// every glyph's shape point is divided by SHAPE_SCALE - increase precission of fixed point value
static double SHAPE_SCALE;
/// <summary>
/// Collect fonts registred inside OS
/// </summary>
/// <returns>OS registred TTF font files(full path) with names</returns>
static FontList get_font_list();
#ifdef _WIN32
static FontList get_font_list_by_register();
static FontList get_font_list_by_enumeration();
static FontList get_font_list_by_folder();
#endif
/// <summary>
/// OS dependent function to get location of font by its name descriptor
/// </summary>
/// <param name="font_face_name">Unique identificator for font</param>
/// <returns>File path to font when found</returns>
static std::optional<std::wstring> get_font_path(const std::wstring &font_face_name);
// description of one letter
struct Glyph
{
// NOTE: shape is scaled by SHAPE_SCALE
// to be able store points without floating points
ExPolygons shape;
// values are in font points
int advance_width=0, left_side_bearing=0;
};
// cache for glyph by unicode
using Glyphs = std::map<int, Glyph>;
/// <summary>
/// keep information from file about font
/// (store file data itself)
/// + cache data readed from buffer
/// </summary>
struct FontFile
{
// loaded data from font file
// must store data size for imgui rasterization
// To not store data on heap and To prevent unneccesary copy
// data are stored inside unique_ptr
std::unique_ptr<std::vector<unsigned char>> data;
struct Info
{
// vertical position is "scale*(ascent - descent + lineGap)"
int ascent, descent, linegap;
// for convert font units to pixel
int unit_per_em;
};
// info for each font in data
std::vector<Info> infos;
FontFile(std::unique_ptr<std::vector<unsigned char>> data,
std::vector<Info> &&infos)
: data(std::move(data)), infos(std::move(infos))
{
assert(this->data != nullptr);
assert(!this->data->empty());
}
bool operator==(const FontFile &other) const {
if (data->size() != other.data->size())
return false;
//if(*data != *other.data) return false;
for (size_t i = 0; i < infos.size(); i++)
if (infos[i].ascent != other.infos[i].ascent ||
infos[i].descent == other.infos[i].descent ||
infos[i].linegap == other.infos[i].linegap)
return false;
return true;
}
};
/// <summary>
/// Add caching for shape of glyphs
/// </summary>
struct FontFileWithCache
{
// Pointer on data of the font file
std::shared_ptr<const FontFile> font_file;
// Cache for glyph shape
// IMPORTANT: accessible only in plater job thread !!!
// main thread only clear cache by set to another shared_ptr
std::shared_ptr<Emboss::Glyphs> cache;
FontFileWithCache() : font_file(nullptr), cache(nullptr) {}
FontFileWithCache(std::unique_ptr<FontFile> font_file)
: font_file(std::move(font_file))
, cache(std::make_shared<Emboss::Glyphs>())
{}
bool has_value() const { return font_file != nullptr && cache != nullptr; }
};
/// <summary>
/// Load font file into buffer
/// </summary>
/// <param name="file_path">Location of .ttf or .ttc font file</param>
/// <returns>Font object when loaded.</returns>
static std::unique_ptr<FontFile> create_font_file(const char *file_path);
// data = raw file data
static std::unique_ptr<FontFile> create_font_file(std::unique_ptr<std::vector<unsigned char>> data);
#ifdef _WIN32
// fix for unknown pointer HFONT
using HFONT = void*;
static void * can_load(HFONT hfont);
static std::unique_ptr<FontFile> create_font_file(HFONT hfont);
#endif // _WIN32
/// <summary>
/// convert letter into polygons
/// </summary>
/// <param name="font">Define fonts</param>
/// <param name="font_index">Index of font in collection</param>
/// <param name="letter">One character defined by unicode codepoint</param>
/// <param name="flatness">Precision of lettter outline curve in conversion to lines</param>
/// <returns>inner polygon cw(outer ccw)</returns>
static std::optional<Glyph> letter2glyph(const FontFile &font, unsigned int font_index, int letter, float flatness);
/// <summary>
/// Convert text into polygons
/// </summary>
/// <param name="font">Define fonts + cache, which could extend</param>
/// <param name="text">Characters to convert</param>
/// <param name="font_prop">User defined property of the font</param>
/// <returns>Inner polygon cw(outer ccw)</returns>
static ExPolygons text2shapes(FontFileWithCache &font, const char *text, const FontProp &font_prop);
/// <summary>
/// Use data from font property to modify transformation
/// </summary>
/// <param name="font_prop">Z-move as surface distance(FontProp::distance)
/// Z-rotation as angle to Y axis(FontProp::angle)</param>
/// <param name="transformation">In / Out transformation to modify by property</param>
static void apply_transformation(const FontProp &font_prop, Transform3d &transformation);
/// <summary>
/// Read information from naming table of font file
/// search for italic (or oblique), bold italic (or bold oblique)
/// </summary>
/// <param name="font">Selector of font</param>
/// <param name="font_index">Index of font in collection</param>
/// <returns>True when the font description contains italic/obligue otherwise False</returns>
static bool is_italic(const FontFile &font, unsigned int font_index);
/// <summary>
/// Create unique character set from string with filtered from text with only character from font
/// </summary>
/// <param name="text">Source vector of glyphs</param>
/// <param name="font">Font descriptor</param>
/// <param name="font_index">Define font in collection</param>
/// <param name="exist_unknown">True when text contain glyph unknown in font</param>
/// <returns>Unique set of character from text contained in font</returns>
static std::string create_range_text(const std::string &text, const FontFile &font, unsigned int font_index, bool* exist_unknown = nullptr);
/// <summary>
/// calculate scale for glyph shape convert from shape points to mm
/// </summary>
/// <param name="fp"></param>
/// <param name="ff"></param>
/// <returns>Conversion to mm</returns>
static double get_shape_scale(const FontProp &fp, const FontFile &ff);
/// <summary>
/// Project spatial point
/// </summary>
class IProject3f
{
public:
virtual ~IProject3f() = default;
/// <summary>
/// Move point with respect to projection direction
/// e.g. Orthogonal projection will move with point by direction
/// e.g. Spherical projection need to use center of projection
/// </summary>
/// <param name="point">Spatial point coordinate</param>
/// <returns>Projected spatial point</returns>
virtual Vec3f project(const Vec3f &point) const = 0;
};
/// <summary>
/// Project 2d point into space
/// Could be plane, sphere, cylindric, ...
/// </summary>
class IProjection : public IProject3f
{
public:
virtual ~IProjection() = default;
/// <summary>
/// convert 2d point to 3d points
/// </summary>
/// <param name="p">2d coordinate</param>
/// <returns>
/// first - front spatial point
/// second - back spatial point
/// </returns>
virtual std::pair<Vec3f, Vec3f> create_front_back(const Point &p) const = 0;
};
/// <summary>
/// Create triangle model for text
/// </summary>
/// <param name="shape2d">text or image</param>
/// <param name="projection">Define transformation from 2d to 3d(orientation, position, scale, ...)</param>
/// <returns>Projected shape into space</returns>
static indexed_triangle_set polygons2model(const ExPolygons &shape2d, const IProjection& projection);
/// <summary>
/// Create transformation for emboss text object to lay on surface point
/// </summary>
/// <param name="position">Position of surface point</param>
/// <param name="normal">Normal of surface point</param>
/// <param name="up_limit">Is compared with normal.z to suggest up direction</param>
/// <returns>Transformation onto surface point</returns>
static Transform3d create_transformation_onto_surface(
const Vec3f &position, const Vec3f &normal, float up_limit = 0.9f);
class ProjectZ : public IProjection
{
public:
ProjectZ(float depth) : m_depth(depth) {}
// Inherited via IProject
std::pair<Vec3f, Vec3f> create_front_back(const Point &p) const override;
Vec3f project(const Vec3f &point) const override;
float m_depth;
};
class ProjectScale : public IProjection
{
std::unique_ptr<IProjection> core;
float m_scale;
public:
ProjectScale(std::unique_ptr<IProjection> core, float scale)
: core(std::move(core)), m_scale(scale)
{}
// Inherited via IProject
std::pair<Vec3f, Vec3f> create_front_back(const Point &p) const override
{
auto res = core->create_front_back(p);
return std::make_pair(res.first * m_scale, res.second * m_scale);
}
Vec3f project(const Vec3f &point) const override{
return core->project(point);
}
};
class OrthoProject3f : public Emboss::IProject3f
{
// size and direction of emboss for ortho projection
Vec3f m_direction;
public:
OrthoProject3f(Vec3f direction) : m_direction(direction) {}
Vec3f project(const Vec3f &point) const override{ return point + m_direction;}
};
class OrthoProject: public Emboss::IProjection {
Transform3d m_matrix;
// size and direction of emboss for ortho projection
Vec3f m_direction;
public:
OrthoProject(Transform3d matrix, Vec3f direction)
: m_matrix(matrix), m_direction(direction)
{}
// Inherited via IProject
std::pair<Vec3f, Vec3f> create_front_back(const Point &p) const override;
Vec3f project(const Vec3f &point) const override;
};
};
} // namespace Slic3r
#endif // slic3r_Emboss_hpp_