diff --git a/src/libslic3r/NSVGUtils.cpp b/src/libslic3r/NSVGUtils.cpp
index e56333d0a3..53dfda3cb2 100644
--- a/src/libslic3r/NSVGUtils.cpp
+++ b/src/libslic3r/NSVGUtils.cpp
@@ -1,40 +1,31 @@
#include "NSVGUtils.hpp"
#include "ClipperUtils.hpp"
+
+namespace {
+using namespace Slic3r; // Polygon + Vec2f
+///
+/// Convert cubic curve to lines
+/// Inspired by nanosvgrast.h function nsvgRasterize->nsvg__flattenShape
+///
+/// Result points
+/// Tesselation tolerance
+/// Curve point
+/// Curve point
+/// Curve point
+/// Curve point
+/// Actual depth of recursion
+/// Scale of point - multiplicator
+/// NOTE: increase preccission by number greater than 1.
+void flatten_cubic_bez(Polygon &polygon, float tessTol, const Vec2f& p1, const Vec2f& p2, const Vec2f& p3, const Vec2f& p4, int level, float scale);
+
+Point::coord_type to_coor(float val, float scale) { return static_cast(std::round(val * scale)); }
+
+} // namespace
+
namespace Slic3r {
-// inspired by nanosvgrast.h function nsvgRasterize -> nsvg__flattenShape -> nsvg__flattenCubicBez
-// https://github.com/memononen/nanosvg/blob/f0a3e1034dd22e2e87e5db22401e44998383124e/src/nanosvgrast.h#L335
-void flatten_cubic_bez(Polygon &polygon, float tessTol, Vec2f p1, Vec2f p2, Vec2f p3, Vec2f p4, int level)
+Polygons to_polygons(NSVGimage *image, float tessTol, int max_level, float scale, bool is_y_negative)
{
- 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);
-}
-
-Polygons to_polygons(NSVGimage *image, float tessTol, int max_level, bool is_y_negative)
-{
- const float y_sign = is_y_negative ? -1.f : 1.f;
- Polygons polygons;
+ Polygons polygons;
for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) {
if (!(shape->flags & NSVG_FLAGS_VISIBLE))
continue;
@@ -44,27 +35,77 @@ Polygons to_polygons(NSVGimage *image, float tessTol, int max_level, bool is_y_n
Polygon polygon;
for (NSVGpath *path = shape->paths; path != NULL; path = path->next) {
// Flatten path
- polygon.points.emplace_back(path->pts[0], y_sign * path->pts[1]);
+ Point::coord_type x = to_coor(path->pts[0], scale);
+ Point::coord_type y = to_coor(path->pts[1], scale);
+ polygon.points.emplace_back(x, y);
size_t path_size = (path->npts > 1) ? static_cast(path->npts - 1) : 0;
for (size_t i = 0; i < path_size; i += 3) {
const 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);
+ Vec2f p1(p[0], p[1]);
+ Vec2f p2(p[2], p[3]);
+ Vec2f p3(p[4], p[5]);
+ Vec2f p4(p[6], p[7]);
+ flatten_cubic_bez(polygon, tessTol, p1, p2, p3, p4, max_level, scale);
}
if (path->closed && !polygon.empty()) {
polygons.push_back(polygon);
polygon = Polygon();
}
}
- if (!polygon.empty())
+ if (!polygon.empty())
polygons.push_back(polygon);
}
+
+ if (is_y_negative)
+ for (Polygon &polygon : polygons)
+ for (Point &p : polygon.points)
+ p.y() = -p.y();
+
return polygons;
}
-ExPolygons to_expolygons(NSVGimage *image, float tessTol, int max_level, bool is_y_negative)
-{
- return union_ex(to_polygons(image, tessTol, max_level, is_y_negative));
+ExPolygons to_expolygons(NSVGimage *image, float tessTol, int max_level, float scale, bool is_y_negative){
+ return union_ex(to_polygons(image, tessTol, max_level, scale, is_y_negative));
}
-} // namespace Slic3r
\ No newline at end of file
+} // namespace Slic3r
+
+namespace {
+// inspired by nanosvgrast.h function nsvgRasterize -> nsvg__flattenShape -> nsvg__flattenCubicBez
+// https://github.com/memononen/nanosvg/blob/f0a3e1034dd22e2e87e5db22401e44998383124e/src/nanosvgrast.h#L335
+void flatten_cubic_bez(Polygon &polygon, float tessTol, const Vec2f& p1, const Vec2f& p2, const Vec2f& p3, const Vec2f& p4, int level, float scale)
+{
+ // f .. first
+ // s .. second
+ auto det = [](const Vec2f &f, const Vec2f &s) {
+ return std::fabs(f.x() * s.y() - f.y() * s.x());
+ };
+
+ Vec2f pd = p4 - p1;
+ Vec2f pd2 = p2 - p4;
+ float d2 = det(pd2, pd);
+ Vec2f pd3 = p3 - p4;
+ float d3 = det(pd3, pd);
+ float d23 = d2 + d3;
+
+ if ((d23 * d23) < tessTol * pd.squaredNorm()) {
+ Point::coord_type x = to_coor(p4.x(), scale);
+ Point::coord_type y = to_coor(p4.y(), scale);
+ polygon.points.emplace_back(x, y);
+ return;
+ }
+
+ --level;
+ if (level == 0)
+ return;
+
+ 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 p234 = (p23 + p34) * 0.5f;
+ Vec2f p1234 = (p123 + p234) * 0.5f;
+ flatten_cubic_bez(polygon, tessTol, p1, p12, p123, p1234, level, scale);
+ flatten_cubic_bez(polygon, tessTol, p1234, p234, p34, p4, level, scale);
+}
+} // namespace
\ No newline at end of file
diff --git a/src/libslic3r/NSVGUtils.hpp b/src/libslic3r/NSVGUtils.hpp
index 83a83cd8e4..d379e90908 100644
--- a/src/libslic3r/NSVGUtils.hpp
+++ b/src/libslic3r/NSVGUtils.hpp
@@ -6,31 +6,21 @@
#include "nanosvg/nanosvg.h" // load SVG file
// Helper function to work with nano svg
-namespace Slic3r {
-
-///
-/// Convert cubic curve to lines
-/// Inspired by nanosvgrast.h function nsvgRasterize->nsvg__flattenShape
-///
-/// Result points
-/// Tesselation tolerance
-/// Curve point
-/// Curve point
-/// Curve point
-/// Curve point
-///
-void flatten_cubic_bez(Polygon &polygon, float tessTol, Vec2f p1, Vec2f p2, Vec2f p3, Vec2f p4, int level);
+namespace Slic3r {
///
/// Convert .svg opened by nanoSvg to Polygons
///
/// Opened file
-/// Tesselation tolerance
+/// Tesselation tolerance
+/// NOTE: Value is in image scale
/// Maximal depth
+/// Multiplicator of point coors
+/// NOTE: Every point coor from image(float) is multiplied by scale and rounded to integer
/// Flag is y negative, when true than y coor is multiplied by -1
/// Polygons extracted from svg
-Polygons to_polygons(NSVGimage *image, float tessTol = 10., int max_level = 10, bool is_y_negative = true);
-ExPolygons to_expolygons(NSVGimage *image, float tessTol = 10., int max_level = 10, bool is_y_negative = true);
+Polygons to_polygons(NSVGimage *image, float tessTol = 10., int max_level = 10, float scale = 1.f, bool is_y_negative = true);
+ExPolygons to_expolygons(NSVGimage *image, float tessTol = 10., int max_level = 10, float scale = 1.f, bool is_y_negative = true);
} // namespace Slic3r
#endif // slic3r_NSVGUtils_hpp_
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp
index 55bbc18a31..46744d03b3 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp
@@ -1,4 +1,4 @@
-#include "GLGizmoSVG.hpp"
+#include "GLGizmoSVG.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
@@ -555,14 +555,110 @@ void GLGizmoSVG::draw_window()
{
if (m_volume != nullptr && m_volume->emboss_shape.has_value())
ImGui::Text("SVG file path is %s", m_volume->emboss_shape->svg_file_path.c_str());
+ //draw_use_surface();
+ draw_distance();
+ draw_rotation();
if (ImGui::Button("change file")) {
auto data = create_emboss_data_base(m_job_cancel);
std::string file = choose_svg_file();
}
+
+ ImGui::Separator();
draw_model_type();
}
+void GLGizmoSVG::draw_distance()
+{
+ if (m_volume == nullptr)
+ return;
+
+ const EmbossProjection& projection = m_volume->emboss_shape->projection;
+ bool use_surface = projection.use_surface;
+ bool allowe_surface_distance = !use_surface && !m_volume->is_the_only_one_part();
+
+ float prev_distance = m_distance.value_or(.0f);
+ float min_distance = static_cast(-2 * projection.depth);
+ float max_distance = static_cast(2 * projection.depth);
+
+ m_imgui->disabled_begin(!allowe_surface_distance);
+ ScopeGuard sg([imgui = m_imgui]() { imgui->disabled_end(); });
+
+ ImGuiWrapper::text(_L("distance"));
+ bool use_inch = wxGetApp().app_config->get_bool("use_inches");
+ const wxString move_tooltip = _L("Distance of the center of the text to the model surface.");
+ bool is_moved = false;
+ if (use_inch) {
+ std::optional distance_inch;
+ if (m_distance.has_value()) distance_inch = (*m_distance * ObjectManipulation::mm_to_in);
+ min_distance = static_cast(min_distance * ObjectManipulation::mm_to_in);
+ max_distance = static_cast(max_distance * ObjectManipulation::mm_to_in);
+ if (m_imgui->slider_optional_float("##distance", m_distance, min_distance, max_distance, "%.3f in", 1.f, false, move_tooltip)) {
+ if (distance_inch.has_value()) {
+ m_distance = *distance_inch * ObjectManipulation::in_to_mm;
+ } else {
+ m_distance.reset();
+ }
+ is_moved = true;
+ }
+ } else {
+ if (m_imgui->slider_optional_float("##distance", m_distance, min_distance, max_distance, "%.2f mm", 1.f, false, move_tooltip))
+ is_moved = true;
+ }
+
+ float undo_offset = ImGui::GetStyle().FramePadding.x;
+ ImGui::SameLine(undo_offset);
+ if (ImGui::Button("R##distance_reset")){
+ m_distance.reset();
+ is_moved = true;
+ } else if (ImGui::IsItemHovered())
+ ImGui::SetTooltip("%s", _u8L("Reset distance to zero value"));
+
+ if (is_moved)
+ do_local_z_move(m_parent, m_distance.value_or(.0f) - prev_distance);
+}
+
+void GLGizmoSVG::draw_rotation()
+{
+ if (m_volume == nullptr)
+ return;
+ // slider for Clock-wise angle in degress
+ // stored angle is optional CCW and in radians
+ // Convert stored value to degress
+ // minus create clock-wise roation from CCW
+ float angle = m_angle.value_or(0.f);
+ float angle_deg = static_cast(-angle * 180 / M_PI);
+ if (m_imgui->slider_float("##angle", &angle, limits.angle.min, limits.angle.max, u8"%.2f DEG", 1.f, false, _L("Rotate text Clock-wise."))){
+ // convert back to radians and CCW
+ double angle_rad = -angle_deg * M_PI / 180.0;
+ Geometry::to_range_pi_pi(angle_rad);
+
+ double diff_angle = angle_rad - angle;
+ do_local_z_rotate(m_parent, diff_angle);
+
+ // calc angle after rotation
+ const GLVolume *gl_volume = get_selected_gl_volume(m_parent.get_selection());
+ m_angle = calc_up(gl_volume->world_matrix(), Slic3r::GUI::up_limit);
+
+ // recalculate for surface cut
+ if (m_volume->emboss_shape->projection.use_surface)
+ process();
+ }
+
+ // Keep up - lock button icon
+ //ImGui::SameLine(m_gui_cfg->lock_offset);
+ //const IconManager::Icon &icon = get_icon(m_icons, m_keep_up ? IconType::lock : IconType::unlock, IconState::activable);
+ //const IconManager::Icon &icon_hover = get_icon(m_icons, m_keep_up ? IconType::lock_bold : IconType::unlock_bold, IconState::activable);
+ //const IconManager::Icon &icon_disable = get_icon(m_icons, m_keep_up ? IconType::lock : IconType::unlock, IconState::disabled);
+ //if (button(icon, icon_hover, icon_disable))
+ // m_keep_up = !m_keep_up;
+ //if (ImGui::IsItemHovered())
+ // ImGui::SetTooltip("%s", (m_keep_up?
+ // _u8L("Unlock the text's rotation when moving text along the object's surface."):
+ // _u8L("Lock the text's rotation when moving text along the object's surface.")
+ // ).c_str());
+}
+
void GLGizmoSVG::draw_model_type()
{
assert(m_volume != nullptr);
@@ -740,12 +836,25 @@ EmbossShape select_shape()
if (shape.svg_file_path.empty())
return {};
- NSVGimage *image = nsvgParseFromFile(shape.svg_file_path.c_str(), "mm", 96.0f);
- ScopeGuard sg([image]() { nsvgDelete(image); });
- shape.shapes = to_expolygons(image);
+ // select units
+ bool use_inch = wxGetApp().app_config->get_bool("use_inches");
+ const char *unit_mm{"mm"};
+ const char *unit_in{"in"};
+ const char *unit = use_inch ?unit_in : unit_mm;
- // TODO: get scale from file
- shape.scale = 1.;
+ // common used DPI is 96 or 72
+ float dpi = 96.0f;
+ NSVGimage *image = nsvgParseFromFile(shape.svg_file_path.c_str(), unit, dpi);
+ ScopeGuard sg([image]() { nsvgDelete(image); });
+
+
+ shape.scale = 1e-2; // loaded in mm
+
+ constexpr float tesselation_tolerance = 1e-2f;
+ int max_level = 10;
+ float scale = static_cast(1 / shape.scale);
+ bool is_y_negative = true;
+ shape.shapes = to_expolygons(image, tesselation_tolerance, max_level, scale, is_y_negative);
// Must contain some shapes !!!
if (shape.shapes.empty())
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp
index eb4ef06ac5..dbd80e6192 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp
@@ -98,6 +98,8 @@ private:
bool process();
void close();
void draw_window();
+ void draw_distance();
+ void draw_rotation();
void draw_model_type();
// process mouse event
@@ -142,6 +144,8 @@ private:
// Rotation gizmo
GLGizmoRotate m_rotate_gizmo;
std::optional m_angle;
+ std::optional m_distance;
+
// Value is set only when dragging rotation to calculate actual angle
std::optional m_rotate_start_angle;
@@ -151,6 +155,7 @@ private:
// When true keep up vector otherwise relative rotation
bool m_keep_up = true;
+
// setted only when wanted to use - not all the time
std::optional m_set_window_offset;