mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 22:25:55 +08:00
Emboss shape with scale
This commit is contained in:
parent
92115754c4
commit
4778b8d26c
@ -1,39 +1,30 @@
|
|||||||
#include "NSVGUtils.hpp"
|
#include "NSVGUtils.hpp"
|
||||||
#include "ClipperUtils.hpp"
|
#include "ClipperUtils.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
using namespace Slic3r; // Polygon + Vec2f
|
||||||
|
/// <summary>
|
||||||
|
/// Convert cubic curve to lines
|
||||||
|
/// Inspired by nanosvgrast.h function nsvgRasterize->nsvg__flattenShape
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="polygon">Result points</param>
|
||||||
|
/// <param name="tessTol">Tesselation tolerance</param>
|
||||||
|
/// <param name="p1">Curve point</param>
|
||||||
|
/// <param name="p2">Curve point</param>
|
||||||
|
/// <param name="p3">Curve point</param>
|
||||||
|
/// <param name="p4">Curve point</param>
|
||||||
|
/// <param name="level">Actual depth of recursion</param>
|
||||||
|
/// <param name="scale">Scale of point - multiplicator
|
||||||
|
/// NOTE: increase preccission by number greater than 1.</param>
|
||||||
|
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<Point::coord_type>(std::round(val * scale)); }
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
// inspired by nanosvgrast.h function nsvgRasterize -> nsvg__flattenShape -> nsvg__flattenCubicBez
|
Polygons to_polygons(NSVGimage *image, float tessTol, int max_level, float scale, bool is_y_negative)
|
||||||
// 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)
|
|
||||||
{
|
{
|
||||||
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) {
|
for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) {
|
||||||
if (!(shape->flags & NSVG_FLAGS_VISIBLE))
|
if (!(shape->flags & NSVG_FLAGS_VISIBLE))
|
||||||
@ -44,12 +35,17 @@ Polygons to_polygons(NSVGimage *image, float tessTol, int max_level, bool is_y_n
|
|||||||
Polygon polygon;
|
Polygon polygon;
|
||||||
for (NSVGpath *path = shape->paths; path != NULL; path = path->next) {
|
for (NSVGpath *path = shape->paths; path != NULL; path = path->next) {
|
||||||
// Flatten path
|
// 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<size_t>(path->npts - 1) : 0;
|
size_t path_size = (path->npts > 1) ? static_cast<size_t>(path->npts - 1) : 0;
|
||||||
for (size_t i = 0; i < path_size; i += 3) {
|
for (size_t i = 0; i < path_size; i += 3) {
|
||||||
const float *p = &path->pts[i * 2];
|
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]);
|
Vec2f p1(p[0], p[1]);
|
||||||
flatten_cubic_bez(polygon, tessTol, p1, p2, p3, p4, max_level);
|
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()) {
|
if (path->closed && !polygon.empty()) {
|
||||||
polygons.push_back(polygon);
|
polygons.push_back(polygon);
|
||||||
@ -59,12 +55,57 @@ Polygons to_polygons(NSVGimage *image, float tessTol, int max_level, bool is_y_n
|
|||||||
if (!polygon.empty())
|
if (!polygon.empty())
|
||||||
polygons.push_back(polygon);
|
polygons.push_back(polygon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_y_negative)
|
||||||
|
for (Polygon &polygon : polygons)
|
||||||
|
for (Point &p : polygon.points)
|
||||||
|
p.y() = -p.y();
|
||||||
|
|
||||||
return polygons;
|
return polygons;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExPolygons to_expolygons(NSVGimage *image, float tessTol, int max_level, bool 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));
|
||||||
return union_ex(to_polygons(image, tessTol, max_level, is_y_negative));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // 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
|
@ -8,29 +8,19 @@
|
|||||||
// Helper function to work with nano svg
|
// Helper function to work with nano svg
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert cubic curve to lines
|
|
||||||
/// Inspired by nanosvgrast.h function nsvgRasterize->nsvg__flattenShape
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="polygon">Result points</param>
|
|
||||||
/// <param name="tessTol">Tesselation tolerance</param>
|
|
||||||
/// <param name="p1">Curve point</param>
|
|
||||||
/// <param name="p2">Curve point</param>
|
|
||||||
/// <param name="p3">Curve point</param>
|
|
||||||
/// <param name="p4">Curve point</param>
|
|
||||||
/// <param name="level"></param>
|
|
||||||
void flatten_cubic_bez(Polygon &polygon, float tessTol, Vec2f p1, Vec2f p2, Vec2f p3, Vec2f p4, int level);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert .svg opened by nanoSvg to Polygons
|
/// Convert .svg opened by nanoSvg to Polygons
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="image">Opened file</param>
|
/// <param name="image">Opened file</param>
|
||||||
/// <param name="tessTol">Tesselation tolerance</param>
|
/// <param name="tessTol">Tesselation tolerance
|
||||||
|
/// NOTE: Value is in image scale</param>
|
||||||
/// <param name="max_level">Maximal depth</param>
|
/// <param name="max_level">Maximal depth</param>
|
||||||
|
/// <param name="scale">Multiplicator of point coors
|
||||||
|
/// NOTE: Every point coor from image(float) is multiplied by scale and rounded to integer</param>
|
||||||
/// <param name="is_y_negative">Flag is y negative, when true than y coor is multiplied by -1</param>
|
/// <param name="is_y_negative">Flag is y negative, when true than y coor is multiplied by -1</param>
|
||||||
/// <returns>Polygons extracted from svg</returns>
|
/// <returns>Polygons extracted from svg</returns>
|
||||||
Polygons to_polygons(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, 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
|
} // namespace Slic3r
|
||||||
#endif // slic3r_NSVGUtils_hpp_
|
#endif // slic3r_NSVGUtils_hpp_
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "GLGizmoSVG.hpp"
|
#include "GLGizmoSVG.hpp"
|
||||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||||
#include "slic3r/GUI/GUI_App.hpp"
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
#include "slic3r/GUI/GUI_ObjectList.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())
|
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());
|
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")) {
|
if (ImGui::Button("change file")) {
|
||||||
auto data = create_emboss_data_base(m_job_cancel);
|
auto data = create_emboss_data_base(m_job_cancel);
|
||||||
std::string file = choose_svg_file();
|
std::string file = choose_svg_file();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
draw_model_type();
|
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<float>(-2 * projection.depth);
|
||||||
|
float max_distance = static_cast<float>(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<float> distance_inch;
|
||||||
|
if (m_distance.has_value()) distance_inch = (*m_distance * ObjectManipulation::mm_to_in);
|
||||||
|
min_distance = static_cast<float>(min_distance * ObjectManipulation::mm_to_in);
|
||||||
|
max_distance = static_cast<float>(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<float>(-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()
|
void GLGizmoSVG::draw_model_type()
|
||||||
{
|
{
|
||||||
assert(m_volume != nullptr);
|
assert(m_volume != nullptr);
|
||||||
@ -740,12 +836,25 @@ EmbossShape select_shape()
|
|||||||
if (shape.svg_file_path.empty())
|
if (shape.svg_file_path.empty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
NSVGimage *image = nsvgParseFromFile(shape.svg_file_path.c_str(), "mm", 96.0f);
|
// select units
|
||||||
ScopeGuard sg([image]() { nsvgDelete(image); });
|
bool use_inch = wxGetApp().app_config->get_bool("use_inches");
|
||||||
shape.shapes = to_expolygons(image);
|
const char *unit_mm{"mm"};
|
||||||
|
const char *unit_in{"in"};
|
||||||
|
const char *unit = use_inch ?unit_in : unit_mm;
|
||||||
|
|
||||||
// TODO: get scale from file
|
// common used DPI is 96 or 72
|
||||||
shape.scale = 1.;
|
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<float>(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 !!!
|
// Must contain some shapes !!!
|
||||||
if (shape.shapes.empty())
|
if (shape.shapes.empty())
|
||||||
|
@ -98,6 +98,8 @@ private:
|
|||||||
bool process();
|
bool process();
|
||||||
void close();
|
void close();
|
||||||
void draw_window();
|
void draw_window();
|
||||||
|
void draw_distance();
|
||||||
|
void draw_rotation();
|
||||||
void draw_model_type();
|
void draw_model_type();
|
||||||
|
|
||||||
// process mouse event
|
// process mouse event
|
||||||
@ -142,6 +144,8 @@ private:
|
|||||||
// Rotation gizmo
|
// Rotation gizmo
|
||||||
GLGizmoRotate m_rotate_gizmo;
|
GLGizmoRotate m_rotate_gizmo;
|
||||||
std::optional<float> m_angle;
|
std::optional<float> m_angle;
|
||||||
|
std::optional<float> m_distance;
|
||||||
|
|
||||||
// Value is set only when dragging rotation to calculate actual angle
|
// Value is set only when dragging rotation to calculate actual angle
|
||||||
std::optional<float> m_rotate_start_angle;
|
std::optional<float> m_rotate_start_angle;
|
||||||
|
|
||||||
@ -152,6 +156,7 @@ private:
|
|||||||
// When true keep up vector otherwise relative rotation
|
// When true keep up vector otherwise relative rotation
|
||||||
bool m_keep_up = true;
|
bool m_keep_up = true;
|
||||||
|
|
||||||
|
|
||||||
// setted only when wanted to use - not all the time
|
// setted only when wanted to use - not all the time
|
||||||
std::optional<ImVec2> m_set_window_offset;
|
std::optional<ImVec2> m_set_window_offset;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user