PrusaSlicer/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
Filip Sykala 7f5c6b0513 Fix copy of text
Fix store/load of volume type
Fix grayed out of Menu item
Change short curt from q to t
2021-09-17 14:04:50 +02:00

814 lines
28 KiB
C++

#include "GLGizmoEmboss.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/GUI/MainFrame.hpp" // to update title when add text
#include "slic3r/GUI/NotificationManager.hpp"
#include "slic3r/GUI/Plater.hpp"
// TODO: remove include
#include "libslic3r/SVG.hpp" // debug store
#include "libslic3r/Model.hpp"
#include "nanosvg/nanosvg.h" // load SVG file
#include <wx/font.h>
#include <wx/fontdlg.h>
namespace Slic3r {
class WxFontUtils
{
public:
WxFontUtils() = delete;
// os specific load of wxFont
static std::optional<Slic3r::Emboss::Font> load_font(const wxFont &font);
// Must be in gui because of wxWidget
static std::optional<Slic3r::Emboss::Font> load_font(const FontItem &fi);
static FontItem get_font_item(const wxFont &font);
// load font used by Operating system as default GUI
static FontItem get_os_font();
static std::string get_human_readable_name(const wxFont &font);
// serialize / deserialize font
static std::string store_wxFont(const wxFont &font);
static wxFont load_wxFont(const std::string &font_descriptor);
};
class NSVGUtils
{
public:
NSVGUtils() = delete;
// inspired by nanosvgrast.h function nsvgRasterize->nsvg__flattenShape
static void flatten_cubic_bez(Polygon &polygon, float tessTol,
Vec2f p1, Vec2f p2, Vec2f p3,Vec2f p4, int level);
// convert svg image to ExPolygons
static ExPolygons to_ExPolygons(NSVGimage *image, float tessTol = 10., int max_level = 10);
};
} // namespace Slic3r
using namespace Slic3r;
using namespace Slic3r::GUI;
GLGizmoEmboss::GLGizmoEmboss(GLCanvas3D &parent)
: GLGizmoBase(parent, M_ICON_FILENAME, -2)
, m_font_selected(0)
, m_text_size(255)
, m_text(new char[m_text_size])
, m_volume(nullptr)
, m_volume_type(ModelVolumeType::MODEL_PART)
, m_is_initialized(false) // initialize on first opening gizmo
{
// TODO: suggest to use https://fontawesome.com/
// (copy & paste) unicode symbols from web
}
GLGizmoEmboss::~GLGizmoEmboss() {}
bool GLGizmoEmboss::on_init()
{
//m_grabbers.emplace_back();
m_shortcut_key = WXK_CONTROL_T;
return true;
}
std::string GLGizmoEmboss::on_get_name() const
{
return (_L("Emboss")).ToUTF8().data();
}
void GLGizmoEmboss::on_render() {}
void GLGizmoEmboss::on_render_for_picking() {}
void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit)
{
check_selection();
int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoCollapse;
m_imgui->begin(on_get_name(), flag);
draw_window();
m_imgui->end(); //
}
void GLGizmoEmboss::on_set_state()
{
// Closing gizmo. e.g. selecting another one
if (GLGizmoBase::m_state == GLGizmoBase::Off) {
// refuse outgoing during text preview
if (false) {
GLGizmoBase::m_state = GLGizmoBase::On;
auto notification_manager = wxGetApp().plater()->get_notification_manager();
notification_manager->push_notification(
NotificationType::CustomNotification,
NotificationManager::NotificationLevel::RegularNotificationLevel,
_u8L("ERROR: Wait until ends or Cancel process."));
return;
}
m_volume = nullptr;
} else if (GLGizmoBase::m_state == GLGizmoBase::On) {
if (!m_is_initialized) initialize();
Selection &s = m_parent.get_selection();
// When add Text on empty plate,
// Create object with volume
if (s.is_empty()) {
set_default_configuration();
process();
return;
}
// Try set selected volume
if (!load_configuration(get_selected_volume())) {
// No volume with text selected, create new one
set_default_configuration();
process();
}
// when open by hyperlink it needs to show up
m_parent.reload_scene(true);
}
}
void GLGizmoEmboss::initialize()
{
if (m_is_initialized) return;
m_is_initialized = true;
m_gui_cfg.emplace(GuiCfg());
m_font_list = {
{"NotoSans Regular", Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf"},
{"NotoSans CJK", Slic3r::resources_dir() + "/fonts/NotoSansCJK-Regular.ttc"}};
m_font_selected = 0;
bool is_font_loaded = load_font();
FontList fl = Emboss::get_font_list();
m_font_list.insert(m_font_list.end(), fl.begin(), fl.end());
m_font_list.emplace_back(WxFontUtils::get_os_font());
while (!is_font_loaded && !m_font_list.empty()) {
// can't load so erase it from list
m_font_list.erase(m_font_list.begin() + m_font_selected);
m_font_selected = 0; // select first
is_font_loaded = load_font();
}
sort_fonts();
set_default_configuration();
}
// IMPROVE: Do not use gizmo_event - especialy smth with prefix SLA,
// use Bind into wxGLCanvas?
bool GLGizmoEmboss::gizmo_event(SLAGizmoEventType action,
const Vec2d & mouse_position,
bool shift_down,
bool alt_down,
bool control_down)
{
/* if (action == SLAGizmoEventType::LeftUp) {
const Camera & camera = wxGetApp().plater()->get_camera();
const Selection & selection = m_parent.get_selection();
const ModelObject * mo = m_c->selection_info()->model_object();
const ModelInstance *mi = mo->instances[selection.get_instance_idx()];
const Transform3d & instance_trafo = mi->get_transformation()
.get_matrix();
// Precalculate transformations of individual meshes.
std::vector<Transform3d> trafo_matrices;
for (const ModelVolume *mv : mo->volumes)
if (mv->is_model_part())
trafo_matrices.emplace_back(instance_trafo * mv->get_matrix());
Vec3f normal = Vec3f::Zero();
Vec3f hit = Vec3f::Zero();
size_t facet = 0;
Vec3f closest_hit = Vec3f::Zero();
double closest_hit_squared_distance =
std::numeric_limits<double>::max();
size_t closest_facet = 0;
int closest_hit_mesh_id = -1;
// Cast a ray on all meshes, pick the closest hit and save it for the
// respective mesh
for (int mesh_id = 0; mesh_id < int(trafo_matrices.size());
++mesh_id) {
if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(
mouse_position, trafo_matrices[mesh_id], camera, hit,
normal, m_c->object_clipper()->get_clipping_plane(),
&facet)) {
// In case this hit is clipped, skip it.
// Is this hit the closest to the camera so far?
double hit_squared_distance = (camera.get_position() -
trafo_matrices[mesh_id] *
hit.cast<double>())
.squaredNorm();
if (hit_squared_distance < closest_hit_squared_distance) {
closest_hit_squared_distance = hit_squared_distance;
closest_facet = facet;
closest_hit_mesh_id = mesh_id;
closest_hit = hit;
}
}
}
// get intersection
}
*/
return false;
}
void GLGizmoEmboss::set_default_configuration() {
set_text(_u8L("Embossed text"));
m_font_prop = FontProp();
m_volume_type = ModelVolumeType::MODEL_PART;
// may be set default font?
}
#include "imgui/imgui_internal.h" // to unfocus input --> ClearActiveID
void GLGizmoEmboss::check_selection()
{
ModelVolume* vol = get_selected_volume();
// is same volume selected?
if (vol!= nullptr && m_volume == vol) return;
// Do not use focused input value when switch volume(it must swith value)
if (m_volume != nullptr)
ImGui::ClearActiveID();
// is select embossed volume?
if (load_configuration(vol)) {
// successfull load volume for editing
return;
}
// behave like adding new text
m_volume = nullptr;
set_default_configuration();
}
ModelVolume *GLGizmoEmboss::get_selected_volume()
{
return get_selected_volume(m_parent.get_selection(),
wxGetApp().plater()->model().objects);
}
ModelVolume *GLGizmoEmboss::get_selected_volume(const Selection &selection,
const ModelObjectPtrs objects)
{
int object_idx = selection.get_object_idx();
// is more object selected?
if (object_idx == -1) return nullptr;
auto volume_idxs = selection.get_volume_idxs();
// is more volumes selected?
if (volume_idxs.size() != 1) return nullptr;
unsigned int vol_id_gl = *volume_idxs.begin();
const GLVolume * vol_gl = selection.get_volume(vol_id_gl);
const GLVolume::CompositeID &id = vol_gl->composite_id;
if (id.object_id >= objects.size()) return nullptr;
ModelObject *object = objects[id.object_id];
if (id.volume_id >= object->volumes.size()) return nullptr;
return object->volumes[id.volume_id];
}
// create_text_volume()
bool GLGizmoEmboss::process() {
if (!m_font.has_value()) return false;
Polygons polygons = Emboss::text2polygons(*m_font, m_text.get(), m_font_prop);
if (polygons.empty()) return false;
float scale = m_font_prop.size_in_mm / m_font->ascent;
auto project = std::make_unique<Emboss::ProjectScale>(
std::make_unique<Emboss::ProjectZ>(m_font_prop.emboss / scale), scale);
indexed_triangle_set its = Emboss::polygons2model(polygons, *project);
if (its.indices.empty()) return false;
// add object
TriangleMesh tm(std::move(its));
tm.repair();
//tm.WriteOBJFile("text_preview.obj");
GUI_App &app = wxGetApp();
Plater * plater = app.plater();
std::string volume_name = create_volume_name();
plater->take_snapshot(_L("Add") + " " + volume_name);
if (m_volume == nullptr) {
const Selection &selection = m_parent.get_selection();
if (selection.is_empty()) {
// create new object
app.obj_list()->load_mesh_object(tm, volume_name);
app.mainframe->update_title();
// get new created volume
m_volume = app.obj_list()->objects()->back()->volumes.front();
m_volume->text_configuration = create_configuration();
// load mesh cause close gizmo, soo I open it again
m_parent.get_gizmos_manager().open_gizmo(GLGizmosManager::EType::Emboss);
return true;
} else {
Model &model = plater->model();
int object_idx = selection.get_object_idx();
ModelObject *obj = model.objects[object_idx];
m_volume = obj->add_volume(std::move(tm), m_volume_type);
}
} else {
m_volume->set_mesh(std::move(tm));
m_volume->set_new_unique_id();
m_volume->translate(-m_volume->source.mesh_offset);
m_volume->center_geometry_after_creation(true);
m_volume->calculate_convex_hull();
m_volume->get_object()->invalidate_bounding_box();
}
m_volume->name = volume_name;
// set a default extruder value, since user can't add it manually
m_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
m_volume->text_configuration = create_configuration();
// select new added volume
ModelObject *mo = m_volume->get_object();
// Editing object volume change its name
if (mo->volumes.size() == 1) mo->name = volume_name;
ObjectList *obj_list = app.obj_list();
const ModelObjectPtrs &objs = *obj_list->objects();
auto item = find(objs.begin(), objs.end(), mo);
assert(item != objs.end());
int object_idx = item - objs.begin();
ModelVolume *new_volume = m_volume; // copy pointer for lambda
obj_list->select_item([new_volume, object_idx, obj_list]() {
wxDataViewItemArray items = obj_list->reorder_volumes_and_get_selection(
object_idx, [new_volume](const ModelVolume *volume) { return volume == new_volume; });
if (items.IsEmpty()) return wxDataViewItem();
return items.front();
});
if (m_volume->type() == ModelVolumeType::MODEL_PART)
// update printable state on canvas
m_parent.update_instance_printable_state_for_object((size_t) object_idx);
obj_list->selection_changed();
m_parent.reload_scene(true);
return true;
}
void GLGizmoEmboss::close() {
// close gizmo == open it again
GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager();
gizmos_mgr.open_gizmo(GLGizmosManager::EType::Emboss);
}
void GLGizmoEmboss::draw_add_button() {
if (ImGui::Button(_u8L("Add").c_str())) {
wxArrayString input_files;
wxString fontDir = wxEmptyString;
wxString selectedFile = wxEmptyString;
wxFileDialog dialog(
nullptr, _L("Choose one or more files (TTF, TTC):"),
fontDir, selectedFile,
file_wildcards(FT_FONTS),
wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() == wxID_OK) dialog.GetPaths(input_files);
if (input_files.IsEmpty()) return;
FontList font_list;
font_list.reserve(input_files.size());
for (auto &input_file : input_files) {
std::string path = std::string(input_file.c_str());
size_t pos = path.find_last_of('\\');
size_t pos2 = path.find_last_of('.');
std::string name = path.substr(pos + 1, pos2 - pos-1);
font_list.emplace_back(name, path);
}
// set last added font as active
m_font_selected = m_font_list.size() + font_list.size() - 1;
add_fonts(font_list);
load_font();
}
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::Text("add file with font(.ttf, .ttc)");
ImGui::EndTooltip();
}
}
void GLGizmoEmboss::draw_window()
{
if (!m_font.has_value()) {
ImGui::Text("Warning: No font is selected. Select correct one.");
}
draw_font_list();
if (ImGui::Button(_L("choose font").c_str())) { choose_font_by_dialog(); }
ImGui::SameLine();
if (ImGui::Button(_L("use system font").c_str())) {
wxSystemSettings ss;
wxFont f = ss.GetFont(wxSYS_DEFAULT_GUI_FONT);
size_t font_index = m_font_list.size();
FontItem fi = WxFontUtils::get_font_item(f);
m_font_list.emplace_back(fi);
bool loaded = load_font(font_index);
}
ImGui::SameLine();
draw_add_button();
if (ImGui::Button("add svg")) {
std::string filePath =
"C:/Users/filip/Downloads/fontawesome-free-5.15.4-web/"
"fontawesome-free-5.15.4-web/svgs/solid/bicycle.svg";
filePath = "C:/Users/filip/Downloads/circle.svg";
NSVGimage *image = nsvgParseFromFile(filePath.c_str(), "mm", 96.0f);
ExPolygons polys = NSVGUtils::to_ExPolygons(image);
for (auto &poly : polys) poly.scale(1e5);
SVG svg("converted.svg", BoundingBox(polys.front().contour.points));
svg.draw(polys);
nsvgDelete(image);
}
if (ImGui::InputFloat("Size[in mm]", &m_font_prop.size_in_mm)) {
if (m_font_prop.size_in_mm < 0.1) m_font_prop.size_in_mm = 10;
process();
}
if (ImGui::InputFloat("Emboss[in mm]", &m_font_prop.emboss)) process();
if (ImGui::InputFloat("Flatness", &m_font_prop.flatness)) {
if (m_font.has_value()) m_font->cache.clear();
process();
}
if (ImGui::InputInt("CharGap[in font points]", &m_font_prop.char_gap))
process();
if (ImGui::InputInt("LineGap[in font points]", &m_font_prop.line_gap))
process();
// ImGui::InputFloat3("Origin", m_orientation.origin.data());
// if (ImGui::InputFloat3("Normal", m_normal.data())) m_normal.normalize();
// if (ImGui::InputFloat3("Up", m_up.data())) m_up.normalize();
ImVec2 input_size(-FLT_MIN, ImGui::GetTextLineHeight() * 6);
ImGuiInputTextFlags flags =
ImGuiInputTextFlags_::ImGuiInputTextFlags_AllowTabInput |
ImGuiInputTextFlags_::ImGuiInputTextFlags_AutoSelectAll
//| ImGuiInputTextFlags_::ImGuiInputTextFlags_CallbackResize
//|ImGuiInputTextFlags_::ImGuiInputTextFlags_CtrlEnterForNewLine
;
if (ImGui::InputTextMultiline("##Text", m_text.get(), m_text_size,
input_size, flags)) {
process();
}
// change text size
int max_text_size = static_cast<int>(m_text_size);
if (ImGui::InputInt("max text size", &max_text_size, 8, 64)) {
set_max_text_size(static_cast<size_t>(max_text_size));
}
// Option to create text volume when reselect volumes
m_imgui->disabled_begin(!m_font.has_value());
if (m_volume == nullptr) {
if (ImGui::Button("Generate preview")) process();
}
m_imgui->disabled_end();
}
void GLGizmoEmboss::draw_font_list()
{
auto &current = m_font_list[m_font_selected];
if (ImGui::BeginCombo("##font_selector", current.name.c_str())) {
for (const FontItem &f : m_font_list) {
ImGui::PushID((void *) &f.name);
std::string name =
(f.name.size() < m_gui_cfg->max_font_name) ?
f.name :
(f.name.substr(0, m_gui_cfg->max_font_name - 3) + " ..");
if (ImGui::Selectable(name.c_str(), &f == &current)) {
size_t prev_font_selected = m_font_selected;
m_font_selected = &f - &m_font_list.front();
if (!load_font()) {
m_font_selected = prev_font_selected;
} else {
process();
}
}
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::Text((f.name + " " + f.path).c_str());
ImGui::EndTooltip();
}
ImGui::PopID();
}
ImGui::EndCombo();
}
if (m_font.has_value() && m_font->count > 1) {
ImGui::SameLine();
if (ImGui::BeginCombo("##font_collection_selector",
std::to_string(m_font->index).c_str())) {
for (size_t i = 0; i < m_font->count; ++i) {
ImGui::PushID(1 << 10 + i);
if (ImGui::Selectable(std::to_string(i).c_str(),
i == m_font->index)) {
m_font->index = i;
m_font->cache.clear();
process();
}
ImGui::PopID();
}
ImGui::EndCombo();
}
}
}
bool GLGizmoEmboss::load_font(size_t font_index)
{
std::swap(font_index, m_font_selected);
bool is_loaded = load_font();
if (!is_loaded) std::swap(font_index, m_font_selected);
return is_loaded;
}
bool GLGizmoEmboss::load_font() {
if (m_font_selected >= m_font_list.size()) return false;
auto font = WxFontUtils::load_font(m_font_list[m_font_selected]);
if (!font.has_value()) return false;
m_font = font;
return true;
}
void GLGizmoEmboss::set_text(const std::string &text) {
if (text.size() > m_text_size-1)
set_max_text_size(text.size() + 1);
int index = 0;
for (const char &c : text) m_text[index++] = c;
m_text[index] = '\0';
}
void GLGizmoEmboss::set_max_text_size(size_t size) {
if (size < 4) size = 4;
std::unique_ptr<char[]> newData(new char[size]);
size_t index = 0;
while ((index + 1) < size) {
if (m_text.get()[index] == '\0') break;
newData.get()[index] = m_text.get()[index];
++index;
}
newData.get()[index] = '\0';
m_text = std::move(newData);
m_text_size = size;
}
bool GLGizmoEmboss::choose_font_by_dialog() {
// keep last selected font did not work
// static wxFontData data;
// wxFontDialog font_dialog((wxWindow *) wxGetApp().mainframe, data);
wxFontDialog font_dialog(nullptr);
font_dialog.SetTitle(_L("Select font for Emboss"));
if (font_dialog.ShowModal() != wxID_OK) return false;
wxFontData data = font_dialog.GetFontData();
wxFont font = data.GetChosenFont();
size_t font_index = m_font_list.size();
m_font_list.emplace_back(WxFontUtils::get_font_item(font));
if (!load_font(font_index)) {
m_font_list.pop_back();
return false;
}
sort_fonts();
process();
return true;
}
void GLGizmoEmboss::sort_fonts() {
// initialize original index locations
std::vector<size_t> idx(m_font_list.size());
std::iota(idx.begin(), idx.end(), 0);
std::stable_sort(idx.begin(), idx.end(),
[this](size_t i1, size_t i2) {
return m_font_list[i1].name < m_font_list[i2].name;
});
FontList font_list;
font_list.reserve(m_font_list.size());
size_t selected = 0;
for (const size_t &i : idx) {
if (i == m_font_selected) selected = &i - &idx.front();
font_list.emplace_back(m_font_list[i]);
}
m_font_list = font_list;
m_font_selected = selected;
}
void GLGizmoEmboss::add_fonts(const FontList &font_list) {
m_font_list.insert(m_font_list.end(), font_list.begin(), font_list.end());
sort_fonts();
}
TextConfiguration GLGizmoEmboss::create_configuration() {
std::string text((const char *) m_text.get());
return TextConfiguration(m_font_list[m_font_selected], m_font_prop, text);
}
bool GLGizmoEmboss::load_configuration(ModelVolume *volume)
{
if (volume == nullptr) return false;
if (!volume->text_configuration.has_value()) return false;
const TextConfiguration &configuration = *volume->text_configuration;
size_t index = m_font_list.size();
for (const auto &font_item : m_font_list) {
if (font_item.type == configuration.font_item.type &&
font_item.path == configuration.font_item.path) {
index = &font_item - &m_font_list.front();
}
}
size_t prev_font_selected = m_font_selected;
// when not in font list add to list
if (index >= m_font_list.size()) {
m_font_selected = m_font_list.size();
add_fonts({configuration.font_item});
} else {
m_font_selected = index;
}
// When can't load font
if (!load_font()) {
// remove bad loadabled font, for correct prev index
m_font_list.erase(m_font_list.begin() + m_font_selected);
m_font_selected = prev_font_selected;
return false;
}
m_font_prop = configuration.font_prop;
set_text(configuration.text);
m_volume_type = volume->type(); // not neccesary
m_volume = volume;
return true;
}
std::string GLGizmoEmboss::create_volume_name()
{
size_t max_len = 20;
std::string text((const char *)m_text.get());
if (text.size() > max_len)
text = text.substr(0, max_len - 3) + " ..";
return _u8L("Text") + " - " + text;
}
std::optional<Emboss::Font> WxFontUtils::load_font(const FontItem &fi)
{
switch (fi.type) {
case FontItem::Type::file_path:
return Emboss::load_font(fi.path.c_str());
case FontItem::Type::wx_font_descr:
return WxFontUtils::load_font(WxFontUtils::load_wxFont(fi.path));
}
return {};
}
std::optional<Emboss::Font> WxFontUtils::load_font(const wxFont &font)
{
if (!font.IsOk()) return {};
#ifdef _WIN32
return Slic3r::Emboss::load_font(font.GetHFONT());
#elif __linux__
// use file path
return {};
#elif __APPLE__
const wxNativeFontInfo *info = font.GetNativeFontInfo();
CTFontDescriptorRef descriptor = info3->GetCTFontDescriptor();
CFDictionaryRef attribs = CTFontDescriptorCopyAttributes(descriptor);
CFStringRef url = (CFStringRef)CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute);
std::string str(CFStringGetCStringPtr(CFURLGetString(anUrl),kCFStringEncodingUTF8));
return Emboss::load_font(str);
#endif
}
FontItem WxFontUtils::get_font_item(const wxFont &font)
{
std::string name = get_human_readable_name(font);
std::string fontDesc = store_wxFont(font);
return FontItem(name, fontDesc, FontItem::Type::wx_font_descr);
}
FontItem WxFontUtils::get_os_font()
{
wxSystemFont system_font = wxSYS_DEFAULT_GUI_FONT;
wxFont font = wxSystemSettings::GetFont(system_font);
FontItem fi = get_font_item(font);
fi.name += +" (" + _u8L("OS default") + ")";
return get_font_item(font);
}
std::string WxFontUtils::get_human_readable_name(const wxFont &font)
{
if (!font.IsOk()) return "Font is NOT ok.";
// Face name is optional in wxFont
if (!font.GetFaceName().empty()) {
return std::string(font.GetFaceName().c_str());
} else {
return std::string((
font.GetFamilyString() + " " +
font.GetStyleString() + " " +
font.GetWeightString()
).c_str());
}
}
std::string WxFontUtils::store_wxFont(const wxFont &font)
{
//wxString os = wxPlatformInfo::Get().GetOperatingSystemIdName();
wxString font_descriptor = font.GetNativeFontInfoDesc();
return std::string(font_descriptor.c_str());
}
wxFont WxFontUtils::load_wxFont(const std::string &font_descriptor)
{
wxString font_descriptor_wx(font_descriptor);
return wxFont(font_descriptor_wx);
}
void NSVGUtils::flatten_cubic_bez(Polygon &polygon,
float tessTol,
Vec2f p1,
Vec2f p2,
Vec2f p3,
Vec2f p4,
int level)
{
Vec2f p12 = (p1 + p2) * 0.5f;
Vec2f p23 = (p2 + p3) * 0.5f;
Vec2f p34 = (p3 + p4) * 0.5f;
Vec2f p123 = (p12 + p23) * 0.5f;
Vec2f pd = p4 - p1;
Vec2f pd2 = p2 - p4;
float d2 = std::abs(pd2.x() * pd.y() - pd2.y() * pd.x());
Vec2f pd3 = p3 - p4;
float d3 = std::abs(pd3.x() * pd.y() - pd3.y() * pd.x());
float d23 = d2 + d3;
if ((d23 * d23) < tessTol * (pd.x() * pd.x() + pd.y() * pd.y())) {
polygon.points.emplace_back(p4.x(), p4.y());
return;
}
--level;
if (level == 0) return;
Vec2f p234 = (p23 + p34) * 0.5f;
Vec2f p1234 = (p123 + p234) * 0.5f;
flatten_cubic_bez(polygon, tessTol, p1, p12, p123, p1234, level);
flatten_cubic_bez(polygon, tessTol, p1234, p234, p34, p4, level);
}
ExPolygons NSVGUtils::to_ExPolygons(NSVGimage *image,
float tessTol,
int max_level)
{
Polygons polygons;
for (NSVGshape *shape = image->shapes; shape != NULL;
shape = shape->next) {
if (!(shape->flags & NSVG_FLAGS_VISIBLE)) continue;
Slic3r::Polygon polygon;
if (shape->fill.type != NSVG_PAINT_NONE) {
for (NSVGpath *path = shape->paths; path != NULL;
path = path->next) {
// Flatten path
polygon.points.emplace_back(path->pts[0], path->pts[1]);
for (size_t i = 0; i < path->npts - 1; i += 3) {
float *p = &path->pts[i * 2];
Vec2f
p1(p[0], p[1]),
p2(p[2], p[3]),
p3(p[4], p[5]),
p4(p[6], p[7]);
flatten_cubic_bez(polygon, tessTol, p1, p2, p3, p4, max_level);
}
if (path->closed) {
polygons.push_back(polygon);
polygon = Slic3r::Polygon();
}
}
}
polygons.push_back(polygon);
}
return union_ex(polygons);
}
// any existing icon filename to not influence GUI
const std::string GLGizmoEmboss::M_ICON_FILENAME = "cut.svg";