Port Emboss & SVG gizmo from PrusaSlicer (#2819)

* Rework UI jobs to make them more understandable and flexible.

* Update Orca specific jobs

* Fix progress issue

* Fix dark mode and window radius

* Update cereal version from 1.2.2 to 1.3.0

(cherry picked from commit prusa3d/PrusaSlicer@057232a275)

* Initial port of Emboss gizmo

* Bump up CGAL version to 5.4

(cherry picked from commit prusa3d/PrusaSlicer@1bf9dee3e7)

* Fix text rotation

* Fix test dragging

* Add text gizmo to right click menu

* Initial port of SVG gizmo

* Fix text rotation

* Fix Linux build

* Fix "from surface"

* Fix -90 rotation

* Fix icon path

* Fix loading font with non-ascii name

* Fix storing non-utf8 font descriptor in 3mf file

* Fix filtering with non-utf8 characters

* Emboss: Use Orca style input dialog

* Fix build on macOS

* Fix tooltip color in light mode

* InputText: fixed incorrect padding when FrameBorder > 0. (ocornut/imgui#4794, ocornut/imgui#3781)
InputTextMultiline: fixed vertical tracking with large values of FramePadding.y. (ocornut/imgui#3781, ocornut/imgui#4794)

(cherry picked from commit ocornut/imgui@072caa4a90)
(cherry picked from commit ocornut/imgui@bdd2a94315)

* SVG: Use Orca style input dialog

* Fix job progress update

* Fix crash when select editing text in preview screen

* Use Orca checkbox style

* Fix issue that toolbar icons are kept regenerated

* Emboss: Fix text & icon alignment

* SVG: Fix text & icon alignment

* Emboss: fix toolbar icon mouse hover state

* Add a simple subtle outline effect by drawing back faces using wireframe mode

* Disable selection outlines

* Show outline in white if the model color is too dark

* Make the outline algorithm more reliable

* Enable cull face, which fix render on Linux

* Fix `disable_cullface`

* Post merge fix

* Optimize selection rendering

* Fix scale gizmo

* Emboss: Fix text rotation if base object is scaled

* Fix volume synchronize

* Fix emboss rotation

* Emboss: Fix advance toggle

* Fix text position after reopened the project

* Make font style preview darker

* Make font style preview selector height shorter

---------

Co-authored-by: tamasmeszaros <meszaros.q@gmail.com>
Co-authored-by: ocornut <omarcornut@gmail.com>
Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
Noisyfox 2023-12-09 22:46:18 +08:00 committed by GitHub
parent 7a8e1929ee
commit 933aa3050b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
197 changed files with 27190 additions and 2454 deletions

View file

@ -520,9 +520,24 @@ void ImGuiWrapper::render()
m_new_frame_open = false;
}
ImVec2 ImGuiWrapper::calc_text_size(std::string_view text,
bool hide_text_after_double_hash,
float wrap_width)
{
return ImGui::CalcTextSize(text.data(), text.data() + text.length(),
hide_text_after_double_hash, wrap_width);
}
ImVec2 ImGuiWrapper::calc_text_size(const std::string& text,
bool hide_text_after_double_hash,
float wrap_width)
{
return ImGui::CalcTextSize(text.c_str(), NULL, hide_text_after_double_hash, wrap_width);
}
ImVec2 ImGuiWrapper::calc_text_size(const wxString &text,
bool hide_text_after_double_hash,
float wrap_width) const
float wrap_width)
{
auto text_utf8 = into_u8(text);
ImVec2 size = ImGui::CalcTextSize(text_utf8.c_str(), NULL, hide_text_after_double_hash, wrap_width);
@ -585,8 +600,8 @@ bool ImGuiWrapper::bbl_combo_with_filter(const char* label, const std::string& p
static char pattern_buffer[256] = { 0 };
auto simple_match = [](const char* pattern, const char* str) {
wxString sub_str = wxString(pattern).Lower();
wxString main_str = wxString(str).Lower();
wxString sub_str = wxString::FromUTF8(pattern).Lower();
wxString main_str = wxString::FromUTF8(str).Lower();
return main_str.Find(sub_str);
};
@ -911,13 +926,13 @@ void ImGuiWrapper::text(const char *label)
void ImGuiWrapper::text(const std::string &label)
{
this->text(label.c_str());
ImGuiWrapper::text(label.c_str());
}
void ImGuiWrapper::text(const wxString &label)
{
auto label_utf8 = into_u8(label);
this->text(label_utf8.c_str());
ImGuiWrapper::text(label_utf8.c_str());
}
void ImGuiWrapper::text_colored(const ImVec4& color, const char* label)
@ -927,13 +942,13 @@ void ImGuiWrapper::text_colored(const ImVec4& color, const char* label)
void ImGuiWrapper::text_colored(const ImVec4& color, const std::string& label)
{
this->text_colored(color, label.c_str());
ImGuiWrapper::text_colored(color, label.c_str());
}
void ImGuiWrapper::text_colored(const ImVec4& color, const wxString& label)
{
auto label_utf8 = into_u8(label);
this->text_colored(color, label_utf8.c_str());
ImGuiWrapper::text_colored(color, label_utf8.c_str());
}
void ImGuiWrapper::text_wrapped(const char *label, float wrap_width)
@ -1940,6 +1955,364 @@ ColorRGBA ImGuiWrapper::from_ImVec4(const ImVec4& color)
return { color.x, color.y, color.z, color.w };
}
template <typename T, typename Func>
static bool input_optional(std::optional<T> &v, Func& f, std::function<bool(const T&)> is_default, const T& def_val)
{
if (v.has_value()) {
if (f(*v)) {
if (is_default(*v)) v.reset();
return true;
}
} else {
T val = def_val;
if (f(val)) {
if (!is_default(val)) v = val;
return true;
}
}
return false;
}
bool ImGuiWrapper::input_optional_int(const char * label,
std::optional<int>& v,
int step,
int step_fast,
ImGuiInputTextFlags flags,
int def_val)
{
auto func = [&](int &value) {
return ImGui::InputInt(label, &value, step, step_fast, flags);
};
std::function<bool(const int &)> is_default =
[def_val](const int &value) -> bool { return value == def_val; };
return input_optional(v, func, is_default, def_val);
}
bool ImGuiWrapper::input_optional_float(const char * label,
std::optional<float> &v,
float step,
float step_fast,
const char * format,
ImGuiInputTextFlags flags,
float def_val)
{
auto func = [&](float &value) {
return ImGui::InputFloat(label, &value, step, step_fast, format, flags);
};
std::function<bool(const float &)> is_default =
[def_val](const float &value) -> bool {
return std::fabs(value-def_val) <= std::numeric_limits<float>::epsilon();
};
return input_optional(v, func, is_default, def_val);
}
bool ImGuiWrapper::drag_optional_float(const char * label,
std::optional<float> &v,
float v_speed,
float v_min,
float v_max,
const char * format,
float power,
float def_val)
{
auto func = [&](float &value) {
return ImGui::DragFloat(label, &value, v_speed, v_min, v_max, format, power);
};
std::function<bool(const float &)> is_default =
[def_val](const float &value) -> bool {
return std::fabs(value-def_val) <= std::numeric_limits<float>::epsilon();
};
return input_optional(v, func, is_default, def_val);
}
bool ImGuiWrapper::slider_optional_float(const char *label,
std::optional<float> &v,
float v_min,
float v_max,
const char *format,
float power,
bool clamp,
const wxString &tooltip,
bool show_edit_btn,
float def_val)
{
auto func = [&](float &value) {
return slider_float(label, &value, v_min, v_max, format, power, clamp, tooltip, show_edit_btn);
};
std::function<bool(const float &)> is_default =
[def_val](const float &value) -> bool {
return std::fabs(value - def_val) <= std::numeric_limits<float>::epsilon();
};
return input_optional(v, func, is_default, def_val);
}
bool ImGuiWrapper::slider_optional_int(const char *label,
std::optional<int> &v,
int v_min,
int v_max,
const char *format,
float power,
bool clamp,
const wxString &tooltip,
bool show_edit_btn,
int def_val)
{
std::optional<float> val;
if (v.has_value()) val = static_cast<float>(*v);
auto func = [&](float &value) {
return slider_float(label, &value, v_min, v_max, format, power, clamp, tooltip, show_edit_btn);
};
std::function<bool(const float &)> is_default =
[def_val](const float &value) -> bool {
return std::fabs(value - def_val) < 0.9f;
};
float default_value = static_cast<float>(def_val);
if (input_optional(val, func, is_default, default_value)) {
if (val.has_value())
v = static_cast<int>(std::round(*val));
else
v.reset();
return true;
} else return false;
}
std::optional<ImVec2> ImGuiWrapper::change_window_position(const char *window_name, bool try_to_fix) {
ImGuiWindow *window = ImGui::FindWindowByName(window_name);
// is window just created
if (window == NULL)
return {};
// position of window on screen
ImVec2 position = window->Pos;
ImVec2 size = window->SizeFull;
// screen size
ImVec2 screen = ImGui::GetMainViewport()->Size;
std::optional<ImVec2> output_window_offset;
if (position.x < 0) {
if (position.y < 0)
// top left
output_window_offset = ImVec2(0, 0);
else
// only left
output_window_offset = ImVec2(0, position.y);
} else if (position.y < 0) {
// only top
output_window_offset = ImVec2(position.x, 0);
} else if (screen.x < (position.x + size.x)) {
if (screen.y < (position.y + size.y))
// right bottom
output_window_offset = ImVec2(screen.x - size.x, screen.y - size.y);
else
// only right
output_window_offset = ImVec2(screen.x - size.x, position.y);
} else if (screen.y < (position.y + size.y)) {
// only bottom
output_window_offset = ImVec2(position.x, screen.y - size.y);
}
if (!try_to_fix && output_window_offset.has_value())
output_window_offset = ImVec2(-1, -1); // Put on default position
return output_window_offset;
}
void ImGuiWrapper::left_inputs() {
ImGui::ClearActiveID();
}
std::string ImGuiWrapper::trunc(const std::string &text,
float width,
const char * tail)
{
float text_width = ImGui::CalcTextSize(text.c_str()).x;
if (text_width < width) return text;
float tail_width = ImGui::CalcTextSize(tail).x;
assert(width > tail_width);
if (width <= tail_width) return "Error: Can't add tail and not be under wanted width.";
float allowed_width = width - tail_width;
// guess approx count of letter
float average_letter_width = calc_text_size(std::string_view("n")).x; // average letter width
unsigned count_letter = static_cast<unsigned>(allowed_width / average_letter_width);
std::string_view text_ = text;
std::string_view result_text = text_.substr(0, count_letter);
text_width = calc_text_size(result_text).x;
if (text_width < allowed_width) {
// increase letter count
while (count_letter < text.length()) {
++count_letter;
std::string_view act_text = text_.substr(0, count_letter);
text_width = calc_text_size(act_text).x;
if (text_width > allowed_width) break;
result_text = act_text;
}
} else {
// decrease letter count
while (count_letter > 1) {
--count_letter;
result_text = text_.substr(0, count_letter);
text_width = calc_text_size(result_text).x;
if (text_width < allowed_width) break;
}
}
return std::string(result_text) + tail;
}
void ImGuiWrapper::escape_double_hash(std::string &text)
{
// add space between hashes
const std::string search = "##";
const std::string replace = "# #";
size_t pos = 0;
while ((pos = text.find(search, pos)) != std::string::npos)
text.replace(pos, search.length(), replace);
}
ImVec2 ImGuiWrapper::suggest_location(const ImVec2 &dialog_size,
const Slic3r::Polygon &interest,
const ImVec2 &canvas_size)
{
// IMPROVE 1: do not select place over menu
// BoundingBox top_menu;
// GLGizmosManager &gizmo_mng = canvas->get_gizmos_manager();
// BoundingBox side_menu; // gizmo_mng.get_size();
// BoundingBox left_bottom_menu; // is permanent?
// NotificationManager *notify_mng = plater->get_notification_manager();
// BoundingBox notifications; // notify_mng->get_size();
// m_window_width, m_window_height + position
// IMPROVE 2: use polygon of interest not only bounding box
BoundingBox bb(interest.points);
Point center = bb.center(); // interest.centroid();
// area size
Point window_center(canvas_size.x / 2, canvas_size.y / 2);
// mov on side
Point bb_half_size = (bb.max - bb.min) / 2 + Point(1,1);
Point diff_center = window_center - center;
Vec2d diff_norm(diff_center.x() / (double) bb_half_size.x(),
diff_center.y() / (double) bb_half_size.y());
if (diff_norm.x() > 1.) diff_norm.x() = 1.;
if (diff_norm.x() < -1.) diff_norm.x() = -1.;
if (diff_norm.y() > 1.) diff_norm.y() = 1.;
if (diff_norm.y() < -1.) diff_norm.y() = -1.;
Vec2d abs_diff(abs(diff_norm.x()), abs(diff_norm.y()));
if (abs_diff.x() < 1. && abs_diff.y() < 1.) {
if (abs_diff.x() > abs_diff.y())
diff_norm.x() = (diff_norm.x() < 0.) ? (-1.) : 1.;
else
diff_norm.y() = (diff_norm.y() < 0.) ? (-1.) : 1.;
}
Point half_dialog_size(dialog_size.x / 2., dialog_size.y / 2.);
Point move_size = bb_half_size + half_dialog_size;
Point offseted_center = center - half_dialog_size;
Vec2d offset(offseted_center.x() + diff_norm.x() * move_size.x(),
offseted_center.y() + diff_norm.y() * move_size.y());
// move offset close to center
Points window_polygon = {offset.cast<int>(),
Point(offset.x(), offset.y() + dialog_size.y),
Point(offset.x() + dialog_size.x,
offset.y() + dialog_size.y),
Point(offset.x() + dialog_size.x, offset.y())};
// check that position by Bounding box is not intersecting
assert(Slic3r::intersection(interest, Polygon(window_polygon)).empty());
double allowed_space = 10; // in px
double allowed_space_sq = allowed_space * allowed_space;
Vec2d move_vec = (center - (offset.cast<int>() + half_dialog_size))
.cast<double>();
Vec2d result_move(0, 0);
do {
move_vec = move_vec / 2.;
Point move_point = (move_vec + result_move).cast<int>();
Points moved_polygon = window_polygon; // copy
for (Point &p : moved_polygon) p += move_point;
if (Slic3r::intersection(interest, Polygon(moved_polygon)).empty())
result_move += move_vec;
} while (move_vec.squaredNorm() >= allowed_space_sq);
offset += result_move;
return ImVec2(offset.x(), offset.y());
}
void ImGuiWrapper::draw(
const Polygon &polygon,
ImDrawList * draw_list /* = ImGui::GetOverlayDrawList()*/,
ImU32 color /* = ImGui::GetColorU32(COL_ORANGE_LIGHT)*/,
float thickness /* = 3.f*/)
{
// minimal one line consist of 2 points
if (polygon.size() < 2) return;
// need a place to draw
if (draw_list == nullptr) return;
const Point *prev_point = &polygon.points.back();
for (const Point &point : polygon.points) {
ImVec2 p1(prev_point->x(), prev_point->y());
ImVec2 p2(point.x(), point.y());
draw_list->AddLine(p1, p2, color, thickness);
prev_point = &point;
}
}
void ImGuiWrapper::draw_cross_hair(const ImVec2 &position, float radius, ImU32 color, int num_segments, float thickness) {
auto draw_list = ImGui::GetOverlayDrawList();
draw_list->AddCircle(position, radius, color, num_segments, thickness);
auto dirs = {ImVec2{0, 1}, ImVec2{1, 0}, ImVec2{0, -1}, ImVec2{-1, 0}};
for (const ImVec2 &dir : dirs) {
ImVec2 start(position.x + dir.x * 0.5 * radius, position.y + dir.y * 0.5 * radius);
ImVec2 end(position.x + dir.x * 1.5 * radius, position.y + dir.y * 1.5 * radius);
draw_list->AddLine(start, end, color, thickness);
}
}
bool ImGuiWrapper::contain_all_glyphs(const ImFont *font,
const std::string &text)
{
if (font == nullptr) return false;
if (!font->IsLoaded()) return false;
const ImFontConfig *fc = font->ConfigData;
if (fc == nullptr) return false;
if (text.empty()) return true;
return is_chars_in_ranges(fc->GlyphRanges, text.c_str());
}
bool ImGuiWrapper::is_char_in_ranges(const ImWchar *ranges,
unsigned int letter)
{
for (const ImWchar *range = ranges; range[0] && range[1]; range += 2) {
ImWchar from = range[0];
ImWchar to = range[1];
if (from <= letter && letter <= to) return true;
if (letter < to) return false; // ranges should be sorted
}
return false;
};
bool ImGuiWrapper::is_chars_in_ranges(const ImWchar *ranges,
const char *chars_ptr)
{
while (*chars_ptr) {
unsigned int c = 0;
// UTF-8 to 32-bit character need imgui_internal
int c_len = ImTextCharFromUtf8(&c, chars_ptr, NULL);
chars_ptr += c_len;
if (c_len == 0) break;
if (!is_char_in_ranges(ranges, c)) return false;
}
return true;
}
#ifdef __APPLE__
static const ImWchar ranges_keyboard_shortcuts[] =
{
@ -2204,20 +2577,20 @@ void ImGuiWrapper::push_combo_style(const float scale)
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0f * scale);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * scale);
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BG_DARK);
ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.68f, 0.26f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.68f, 0.26f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_BorderActive, COL_ORCA);
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, to_ImVec4(to_rgba(ColorRGB::ORCA(), 0.5f)));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, COL_ORCA);
ImGui::PushStyleColor(ImGuiCol_Header, COL_ORCA);
ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImGuiWrapper::COL_WINDOW_BG_DARK);
ImGui::PushStyleColor(ImGuiCol_Button, {1.00f, 1.00f, 1.00f, 0.0f});
} else {
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0f * scale);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * scale);
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BG);
ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.68f, 0.26f, 0.5f));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.68f, 0.26f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_BorderActive, COL_ORCA);
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, to_ImVec4(to_rgba(ColorRGB::ORCA(), 0.5f)));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, COL_ORCA);
ImGui::PushStyleColor(ImGuiCol_Header, COL_ORCA);
ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImGuiWrapper::COL_WINDOW_BG);
ImGui::PushStyleColor(ImGuiCol_Button, {1.00f, 1.00f, 1.00f, 0.0f});
}
@ -2229,6 +2602,20 @@ void ImGuiWrapper::pop_combo_style()
ImGui::PopStyleColor(7);
}
void ImGuiWrapper::push_radio_style()
{
if (m_is_dark_mode) {
ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(1.00f, 1.00f, 1.00f, 1.00f));
} else {
ImGui::PushStyleColor(ImGuiCol_CheckMark, ImVec4(0.00f, 0.00f, 0.00f, 1.00f));
}
}
void ImGuiWrapper::pop_radio_style()
{
ImGui::PopStyleColor(1);
}
void ImGuiWrapper::init_font(bool compress)
{
destroy_font();