Merge remote-tracking branch 'remote/master' into SoftFever

This commit is contained in:
SoftFever 2022-09-03 00:17:59 +08:00
commit 4fd174175c
298 changed files with 13879 additions and 6228 deletions

View file

@ -14,8 +14,8 @@ extern "C"
{
// Let the NVIDIA and AMD know we want to use their graphics card
// on a dual graphics card system.
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000000;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 0;
}
#endif /* SLIC3R_GUI */

View file

@ -45,6 +45,8 @@ typedef Eigen::Matrix<int, 3, 1, Eigen::DontAlign> stl_triangle_vertex_indices
static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect");
static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect");
typedef std::function<void(int current, int total, bool& cancel)> ImportstlProgressFn;
typedef enum {
eNormal, // normal face
eSmallOverhang, // small overhang
@ -244,7 +246,7 @@ struct indexed_triangle_set
}
};
extern bool stl_open(stl_file *stl, const char *file);
extern bool stl_open(stl_file *stl, const char *file, ImportstlProgressFn stlFn = nullptr);
extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file);
extern bool stl_print_neighbors(stl_file *stl, char *file);
extern bool stl_write_ascii(stl_file *stl, const char *file, const char *label);
@ -398,7 +400,7 @@ extern void stl_calculate_volume(stl_file *stl);
extern void stl_repair(stl_file *stl, bool fixall_flag, bool exact_flag, bool tolerance_flag, float tolerance, bool increment_flag, float increment, bool nearby_flag, int iterations, bool remove_unconnected_flag, bool fill_holes_flag, bool normal_directions_flag, bool normal_values_flag, bool reverse_all_flag, bool verbose_flag);
extern void stl_allocate(stl_file *stl);
extern void stl_read(stl_file *stl, int first_facet, bool first);
extern void stl_read(stl_file *stl, int first_facet, bool first, ImportstlProgressFn stlFn = nullptr);
extern void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first);
extern void stl_reallocate(stl_file *stl);
extern void stl_add_facet(stl_file *stl, const stl_facet *new_facet);

View file

@ -31,6 +31,7 @@
#include <boost/predef/other/endian.h>
#include "stl.h"
#include "libslic3r/Format/STL.hpp"
#include "libslic3r/LocalesUtils.hpp"
@ -42,6 +43,8 @@
extern void stl_internal_reverse_quads(char *buf, size_t cnt);
#endif /* BOOST_ENDIAN_BIG_BYTE */
const int LOAD_STL_UNIT_NUM = 5;
static FILE* stl_open_count_facets(stl_file *stl, const char *file)
{
// Open the file in binary mode first.
@ -145,7 +148,7 @@ static FILE* stl_open_count_facets(stl_file *stl, const char *file)
/* Reads the contents of the file pointed to by fp into the stl structure,
starting at facet first_facet. The second argument says if it's our first
time running this for the stl and therefore we should reset our max and min stats. */
static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first)
static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first, ImportstlProgressFn stlFn)
{
if (stl->stats.type == binary)
fseek(fp, HEADER_SIZE, SEEK_SET);
@ -153,7 +156,19 @@ static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first)
rewind(fp);
char normal_buf[3][32];
for (uint32_t i = first_facet; i < stl->stats.number_of_facets; ++ i) {
uint32_t facets_num = stl->stats.number_of_facets;
uint32_t unit = facets_num / LOAD_STL_UNIT_NUM + 1;
for (uint32_t i = first_facet; i < facets_num; ++ i) {
if ((i % unit) == 0) {
bool cb_cancel = false;
if (stlFn) {
stlFn(i, facets_num, cb_cancel);
if (cb_cancel)
return false;
}
}
stl_facet facet;
if (stl->stats.type == binary) {
@ -232,7 +247,7 @@ static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first)
return true;
}
bool stl_open(stl_file *stl, const char *file)
bool stl_open(stl_file *stl, const char *file, ImportstlProgressFn stlFn)
{
Slic3r::CNumericLocalesSetter locales_setter;
stl->clear();
@ -240,7 +255,7 @@ bool stl_open(stl_file *stl, const char *file)
if (fp == nullptr)
return false;
stl_allocate(stl);
bool result = stl_read(stl, fp, 0, true);
bool result = stl_read(stl, fp, 0, true, stlFn);
fclose(fp);
return result;
}

View file

@ -491,6 +491,7 @@ namespace ImGui
IMGUI_API bool ImageButton2(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec2& margin = ImVec2(0, 0)); // <0 frame_padding uses default frame padding settings. 0 for no padding
IMGUI_API bool ImageTextButton(const ImVec2& button_size, const char* text, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec2& margin = ImVec2(0, 0));
IMGUI_API bool ImageButton3(ImTextureID user_texture_id,ImTextureID user_texture_id_hover, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec2& margin = ImVec2(0, 0)); // <0 frame_padding uses default frame padding settings. 0 for no padding
IMGUI_API bool BBLImageButton(ImTextureID user_texture_id,ImTextureID user_texture_id_hover,ImTextureID user_texture_id_press, const ImVec2& size, bool &value, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec2& margin = ImVec2(0, 0)); // <0 frame_padding uses default frame padding settings. 0 for no padding
IMGUI_API bool Checkbox(const char* label, bool* v);
IMGUI_API bool BBLCheckbox(const char* label, bool* v);
IMGUI_API bool CheckboxFlags(const char* label, int* flags, int flags_value);

View file

@ -2554,6 +2554,7 @@ namespace ImGui
IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col);
IMGUI_API bool ImageButtonEx2(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col, const ImVec2& margin);
IMGUI_API bool ImageButtonEx3(ImGuiID id, ImTextureID texture_id,ImTextureID texture_id_hover, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col, const ImVec2& margin);
IMGUI_API bool BBLImageButtonEx(ImGuiID id, ImTextureID texture_id,ImTextureID texture_id_hover,ImTextureID texture_id_press, const ImVec2& size, bool &value, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col, const ImVec2& margin);
IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis);
IMGUI_API ImGuiID GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis);
IMGUI_API ImGuiID GetWindowResizeCornerID(ImGuiWindow* window, int n); // 0..3: corners

View file

@ -1205,6 +1205,40 @@ bool ImGui::ImageButtonEx3(ImGuiID id,ImTextureID texture_id,ImTextureID tex
return pressed;
}
bool ImGui::BBLImageButtonEx(ImGuiID id,ImTextureID texture_id,ImTextureID texture_id_hover, ImTextureID texture_id_press, const ImVec2 &size, bool &value,const ImVec2 &uv0,const ImVec2 &uv1,const ImVec2 &padding,const ImVec4 &bg_col,const ImVec4 &tint_col,const ImVec2 &margin)
{
ImGuiContext &g = *GImGui;
ImGuiWindow * window = GetCurrentWindow();
if (window->SkipItems) return false;
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2 + margin * 2);
ItemSize(bb);
if (!ItemAdd(bb, id)) return false;
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held);
if (pressed) value = !value;
// Render
const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
RenderNavHighlight(bb, id);
const float border_size = g.Style.FrameBorderSize;
if (border_size > 0.0f) {
window->DrawList->AddRect(bb.Min + ImVec2(1, 1), bb.Max + ImVec2(1, 1), col, g.Style.FrameRounding, 0, border_size);
window->DrawList->AddRect(bb.Min, bb.Max, col, g.Style.FrameRounding, 0, border_size);
}
if (bg_col.w > 0.0f) window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));
window->DrawList->AddImage(texture_id, bb.Min + padding + margin, bb.Max - padding - margin, uv0, uv1, GetColorU32(tint_col));
if (hovered)window->DrawList->AddImage(texture_id_hover, bb.Min + padding + margin, bb.Max - padding - margin, uv0, uv1, GetColorU32(tint_col));
if (value)window->DrawList->AddImage(texture_id_press, bb.Min + padding + margin, bb.Max - padding - margin, uv0, uv1, GetColorU32(tint_col));
return pressed;
}
bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col)
{
@ -1256,6 +1290,20 @@ bool ImGui::ImageButton3(ImTextureID user_texture_id,ImTextureID user_texture_id
return ImageButtonEx3(id, user_texture_id, user_texture_id_hover, size, uv0, uv1, padding, bg_col, tint_col, margin);
}
bool ImGui::BBLImageButton(ImTextureID user_texture_id,ImTextureID user_texture_id_hover, ImTextureID user_texture_id_press, const ImVec2 &size, bool &value, const ImVec2 &uv0, const ImVec2 &uv1, int frame_padding, const ImVec4 &bg_col, const ImVec4 &tint_col, const ImVec2 &margin)
{
ImGuiContext &g = *GImGui;
ImGuiWindow * window = g.CurrentWindow;
if (window->SkipItems) return false;
// Default to using texture ID as ID. User can still push string/integer prefixes.
PushID((void *) (intptr_t) user_texture_id);
const ImGuiID id = window->GetID("#image");
PopID();
const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float) frame_padding, (float) frame_padding) : g.Style.FramePadding;
return BBLImageButtonEx(id, user_texture_id, user_texture_id_hover, user_texture_id_press, size,value, uv0, uv1, padding, bg_col, tint_col, margin);
}
bool ImGui::ImageTextButton(const ImVec2& button_size, const char* text, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col, const ImVec2& margin)
{
@ -3767,16 +3815,18 @@ bool ImGui::BBLSliderScalar(const char *label, ImGuiDataType data_type, void *p_
// Render grab
if (grab_bb.Max.x > grab_bb.Min.x) {
float offset = 1.0f;
ImVec2 p1 = ImVec2((grab_bb.Min.x + grab_bb.Max.x) / 2, (grab_bb.Min.y + grab_bb.Max.y) / 2 - offset);
ImVec2 p2 = ImVec2(grab_bb.Min.x, grab_bb.Max.y);
ImVec2 p3 = ImVec2(grab_bb.Max.x, grab_bb.Max.y);
float line_high = 2.0f;
const float offset = 2.0f;
ImVec2 p1 = ImVec2((grab_bb.Min.x + grab_bb.Max.x) / 2, (grab_bb.Min.y + grab_bb.Max.y) / 2 + offset);
ImVec2 p2 = ImVec2(grab_bb.Min.x, grab_bb.Max.y + offset);
ImVec2 p3 = ImVec2(grab_bb.Max.x, grab_bb.Max.y + offset);
window->DrawList->AddTriangleFilled(p1, p2, p3, GetColorU32(ImGuiCol_SliderGrabActive));
ImVec2 start_pos = ImVec2(frame_bb.Min.x, frame_bb.GetCenter().y - offset);
ImVec2 curr_pos = ImVec2(grab_bb.GetCenter().x, frame_bb.GetCenter().y + offset);
ImVec2 end_pos = ImVec2(frame_bb.Max.x, frame_bb.GetCenter().y + offset);
window->DrawList->AddRectFilled(start_pos, curr_pos, GetColorU32(ImGuiCol_SliderGrabActive), style.GrabRounding);
ImVec2 curr_pos = ImVec2(grab_bb.GetCenter().x, frame_bb.GetCenter().y + line_high - offset);
ImVec2 end_pos = ImVec2(frame_bb.Max.x, frame_bb.GetCenter().y + line_high - offset);
window->DrawList->AddRectFilled(start_pos, end_pos, GetColorU32(ImGuiCol_SliderGrab), style.GrabRounding);
window->DrawList->AddRectFilled(start_pos, curr_pos, GetColorU32(ImGuiCol_SliderGrabActive), style.GrabRounding);
}
if (label_size.x > 0.0f) RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
@ -6244,9 +6294,9 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl
RenderFrameBorder(bb.Min, bb.Max, rounding);
else
#ifdef __APPLE__
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding,NULL,2.0f); // Color button are often in need of some sort of border
window->DrawList->AddRect(bb.Min - ImVec2(3, 3), bb.Max + ImVec2(3, 3), GetColorU32(ImGuiCol_FrameBg), rounding * 2,NULL,4.0f);; // Color button are often in need of some sort of border
#else
window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border
window->DrawList->AddRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2), GetColorU32(ImGuiCol_FrameBg), rounding * 2,NULL,3.0f); // Color button are often in need of some sort of border
#endif
}

View file

@ -67,7 +67,7 @@ struct NfpPConfig {
* the already packed items.
*
*/
std::function<double(const _Item<RawShape>&)> object_function;
std::function<double(const _Item<RawShape>&, const ItemGroup&)> object_function;
/**
* @brief The quality of search for an optimal placement.
@ -666,7 +666,7 @@ private:
// This is the kernel part of the object function that is
// customizable by the library client
std::function<double(const Item&)> _objfunc;
if(config_.object_function) _objfunc = config_.object_function;
if (config_.object_function) _objfunc = [this](const Item& item) {return config_.object_function(item, this->items_); };
else {
// Inside check has to be strict if no alignment was enabled

View file

@ -446,6 +446,57 @@ namespace detail {
}
}
// Real-time collision detection, Ericson, Chapter 5
template<typename Vector>
static inline Vector closest_point_to_triangle(const Vector &p, const Vector &a, const Vector &b, const Vector &c)
{
using Scalar = typename Vector::Scalar;
// Check if P in vertex region outside A
Vector ab = b - a;
Vector ac = c - a;
Vector ap = p - a;
Scalar d1 = ab.dot(ap);
Scalar d2 = ac.dot(ap);
if (d1 <= 0 && d2 <= 0)
return a;
// Check if P in vertex region outside B
Vector bp = p - b;
Scalar d3 = ab.dot(bp);
Scalar d4 = ac.dot(bp);
if (d3 >= 0 && d4 <= d3)
return b;
// Check if P in edge region of AB, if so return projection of P onto AB
Scalar vc = d1*d4 - d3*d2;
if (a != b && vc <= 0 && d1 >= 0 && d3 <= 0) {
Scalar v = d1 / (d1 - d3);
return a + v * ab;
}
// Check if P in vertex region outside C
Vector cp = p - c;
Scalar d5 = ab.dot(cp);
Scalar d6 = ac.dot(cp);
if (d6 >= 0 && d5 <= d6)
return c;
// Check if P in edge region of AC, if so return projection of P onto AC
Scalar vb = d5*d2 - d1*d6;
if (vb <= 0 && d2 >= 0 && d6 <= 0) {
Scalar w = d2 / (d2 - d6);
return a + w * ac;
}
// Check if P in edge region of BC, if so return projection of P onto BC
Scalar va = d3*d6 - d5*d4;
if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
Scalar w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
return b + w * (c - b);
}
// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
Scalar denom = Scalar(1.0) / (va + vb + vc);
Scalar v = vb * denom;
Scalar w = vc * denom;
return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = 1.0-v-w
};
// Nothing to do with COVID-19 social distancing.
template<typename AVertexType, typename AIndexedFaceType, typename ATreeType, typename AVectorType>
struct IndexedTriangleSetDistancer {
@ -453,74 +504,36 @@ namespace detail {
using IndexedFaceType = AIndexedFaceType;
using TreeType = ATreeType;
using VectorType = AVectorType;
using ScalarType = typename VectorType::Scalar;
const std::vector<VertexType> &vertices;
const std::vector<IndexedFaceType> &faces;
const TreeType &tree;
const VectorType origin;
inline VectorType closest_point_to_origin(size_t primitive_index,
ScalarType& squared_distance){
const auto &triangle = this->faces[primitive_index];
VectorType closest_point = closest_point_to_triangle<VectorType>(origin,
this->vertices[triangle(0)].template cast<ScalarType>(),
this->vertices[triangle(1)].template cast<ScalarType>(),
this->vertices[triangle(2)].template cast<ScalarType>());
squared_distance = (origin - closest_point).squaredNorm();
return closest_point;
}
};
// Real-time collision detection, Ericson, Chapter 5
template<typename Vector>
static inline Vector closest_point_to_triangle(const Vector &p, const Vector &a, const Vector &b, const Vector &c)
{
using Scalar = typename Vector::Scalar;
// Check if P in vertex region outside A
Vector ab = b - a;
Vector ac = c - a;
Vector ap = p - a;
Scalar d1 = ab.dot(ap);
Scalar d2 = ac.dot(ap);
if (d1 <= 0 && d2 <= 0)
return a;
// Check if P in vertex region outside B
Vector bp = p - b;
Scalar d3 = ab.dot(bp);
Scalar d4 = ac.dot(bp);
if (d3 >= 0 && d4 <= d3)
return b;
// Check if P in edge region of AB, if so return projection of P onto AB
Scalar vc = d1*d4 - d3*d2;
if (a != b && vc <= 0 && d1 >= 0 && d3 <= 0) {
Scalar v = d1 / (d1 - d3);
return a + v * ab;
}
// Check if P in vertex region outside C
Vector cp = p - c;
Scalar d5 = ab.dot(cp);
Scalar d6 = ac.dot(cp);
if (d6 >= 0 && d5 <= d6)
return c;
// Check if P in edge region of AC, if so return projection of P onto AC
Scalar vb = d5*d2 - d1*d6;
if (vb <= 0 && d2 >= 0 && d6 <= 0) {
Scalar w = d2 / (d2 - d6);
return a + w * ac;
}
// Check if P in edge region of BC, if so return projection of P onto BC
Scalar va = d3*d6 - d5*d4;
if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
Scalar w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
return b + w * (c - b);
}
// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
Scalar denom = Scalar(1.0) / (va + vb + vc);
Scalar v = vb * denom;
Scalar w = vc * denom;
return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = 1.0-v-w
};
template<typename IndexedTriangleSetDistancerType, typename Scalar>
static inline Scalar squared_distance_to_indexed_triangle_set_recursive(
IndexedTriangleSetDistancerType &distancer,
template<typename IndexedPrimitivesDistancerType, typename Scalar>
static inline Scalar squared_distance_to_indexed_primitives_recursive(
IndexedPrimitivesDistancerType &distancer,
size_t node_idx,
Scalar low_sqr_d,
Scalar up_sqr_d,
size_t &i,
Eigen::PlainObjectBase<typename IndexedTriangleSetDistancerType::VectorType> &c)
Eigen::PlainObjectBase<typename IndexedPrimitivesDistancerType::VectorType> &c)
{
using Vector = typename IndexedTriangleSetDistancerType::VectorType;
using Vector = typename IndexedPrimitivesDistancerType::VectorType;
if (low_sqr_d > up_sqr_d)
return low_sqr_d;
@ -538,13 +551,9 @@ namespace detail {
assert(node.is_valid());
if (node.is_leaf())
{
const auto &triangle = distancer.faces[node.idx];
Vector c_candidate = closest_point_to_triangle<Vector>(
distancer.origin,
distancer.vertices[triangle(0)].template cast<Scalar>(),
distancer.vertices[triangle(1)].template cast<Scalar>(),
distancer.vertices[triangle(2)].template cast<Scalar>());
set_min((c_candidate - distancer.origin).squaredNorm(), node.idx, c_candidate);
Scalar sqr_dist;
Vector c_candidate = distancer.closest_point_to_origin(node.idx, sqr_dist);
set_min(sqr_dist, node.idx, c_candidate);
}
else
{
@ -561,7 +570,7 @@ namespace detail {
{
size_t i_left;
Vector c_left = c;
Scalar sqr_d_left = squared_distance_to_indexed_triangle_set_recursive(distancer, left_node_idx, low_sqr_d, up_sqr_d, i_left, c_left);
Scalar sqr_d_left = squared_distance_to_indexed_primitives_recursive(distancer, left_node_idx, low_sqr_d, up_sqr_d, i_left, c_left);
set_min(sqr_d_left, i_left, c_left);
looked_left = true;
};
@ -569,13 +578,13 @@ namespace detail {
{
size_t i_right;
Vector c_right = c;
Scalar sqr_d_right = squared_distance_to_indexed_triangle_set_recursive(distancer, right_node_idx, low_sqr_d, up_sqr_d, i_right, c_right);
Scalar sqr_d_right = squared_distance_to_indexed_primitives_recursive(distancer, right_node_idx, low_sqr_d, up_sqr_d, i_right, c_right);
set_min(sqr_d_right, i_right, c_right);
looked_right = true;
};
// must look left or right if in box
using BBoxScalar = typename IndexedTriangleSetDistancerType::TreeType::BoundingBox::Scalar;
using BBoxScalar = typename IndexedPrimitivesDistancerType::TreeType::BoundingBox::Scalar;
if (node_left.bbox.contains(distancer.origin.template cast<BBoxScalar>()))
look_left();
if (node_right.bbox.contains(distancer.origin.template cast<BBoxScalar>()))
@ -709,10 +718,15 @@ inline bool intersect_ray_all_hits(
origin, dir, VectorType(dir.cwiseInverse()),
eps }
};
if (! tree.empty()) {
if (tree.empty()) {
hits.clear();
} else {
// Reusing the output memory if there is some memory already pre-allocated.
ray_intersector.hits = std::move(hits);
ray_intersector.hits.clear();
ray_intersector.hits.reserve(8);
detail::intersect_ray_recursive_all_hits(ray_intersector, 0);
std::swap(hits, ray_intersector.hits);
hits = std::move(ray_intersector.hits);
std::sort(hits.begin(), hits.end(), [](const auto &l, const auto &r) { return l.t < r.t; });
}
return ! hits.empty();
@ -742,7 +756,7 @@ inline typename VectorType::Scalar squared_distance_to_indexed_triangle_set(
auto distancer = detail::IndexedTriangleSetDistancer<VertexType, IndexedFaceType, TreeType, VectorType>
{ vertices, faces, tree, point };
return tree.empty() ? Scalar(-1) :
detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), std::numeric_limits<Scalar>::infinity(), hit_idx_out, hit_point_out);
detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0), std::numeric_limits<Scalar>::infinity(), hit_idx_out, hit_point_out);
}
// Decides if exists some triangle in defined radius on a 3D indexed triangle set using a pre-built AABBTreeIndirect::Tree.
@ -759,22 +773,22 @@ inline bool is_any_triangle_in_radius(
const TreeType &tree,
// Point to which the closest point on the indexed triangle set is searched for.
const VectorType &point,
// Maximum distance in which triangle is search for
typename VectorType::Scalar &max_distance)
//Square of maximum distance in which triangle is searched for
typename VectorType::Scalar &max_distance_squared)
{
using Scalar = typename VectorType::Scalar;
auto distancer = detail::IndexedTriangleSetDistancer<VertexType, IndexedFaceType, TreeType, VectorType>
{ vertices, faces, tree, point };
size_t hit_idx;
VectorType hit_point = VectorType::Ones() * (std::nan(""));
size_t hit_idx;
VectorType hit_point = VectorType::Ones() * (NaN<typename VectorType::Scalar>);
if(tree.empty())
{
return false;
}
detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), max_distance, hit_idx, hit_point);
detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0), max_distance_squared, hit_idx, hit_point);
return hit_point.allFinite();
}

View file

@ -0,0 +1,112 @@
#ifndef SRC_LIBSLIC3R_AABBTREELINES_HPP_
#define SRC_LIBSLIC3R_AABBTREELINES_HPP_
#include "libslic3r/Point.hpp"
#include "libslic3r/EdgeGrid.hpp"
#include "libslic3r/AABBTreeIndirect.hpp"
#include "libslic3r/Line.hpp"
namespace Slic3r {
namespace AABBTreeLines {
namespace detail {
template<typename ALineType, typename ATreeType, typename AVectorType>
struct IndexedLinesDistancer {
using LineType = ALineType;
using TreeType = ATreeType;
using VectorType = AVectorType;
using ScalarType = typename VectorType::Scalar;
const std::vector<LineType> &lines;
const TreeType &tree;
const VectorType origin;
inline VectorType closest_point_to_origin(size_t primitive_index,
ScalarType &squared_distance) {
VectorType nearest_point;
const LineType &line = lines[primitive_index];
squared_distance = line_alg::distance_to_squared(line, origin, &nearest_point);
return nearest_point;
}
};
}
// Build a balanced AABB Tree over a vector of float lines, balancing the tree
// on centroids of the lines.
// Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies
// during tree traversal.
template<typename LineType>
inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(
const std::vector<LineType> &lines,
//FIXME do we want to apply an epsilon?
const float eps = 0)
{
using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>;
// using CoordType = typename TreeType::CoordType;
using VectorType = typename TreeType::VectorType;
using BoundingBox = typename TreeType::BoundingBox;
struct InputType {
size_t idx() const {
return m_idx;
}
const BoundingBox& bbox() const {
return m_bbox;
}
const VectorType& centroid() const {
return m_centroid;
}
size_t m_idx;
BoundingBox m_bbox;
VectorType m_centroid;
};
std::vector<InputType> input;
input.reserve(lines.size());
const VectorType veps(eps, eps);
for (size_t i = 0; i < lines.size(); ++i) {
const LineType &line = lines[i];
InputType n;
n.m_idx = i;
n.m_centroid = (line.a + line.b) * 0.5;
n.m_bbox = BoundingBox(line.a, line.a);
n.m_bbox.extend(line.b);
n.m_bbox.min() -= veps;
n.m_bbox.max() += veps;
input.emplace_back(n);
}
TreeType out;
out.build(std::move(input));
return out;
}
// Finding a closest line, its closest point and squared distance to the closest point
// Returns squared distance to the closest point or -1 if the input is empty.
template<typename LineType, typename TreeType, typename VectorType>
inline typename VectorType::Scalar squared_distance_to_indexed_lines(
const std::vector<LineType> &lines,
const TreeType &tree,
const VectorType &point,
size_t &hit_idx_out,
Eigen::PlainObjectBase<VectorType> &hit_point_out)
{
using Scalar = typename VectorType::Scalar;
auto distancer = detail::IndexedLinesDistancer<LineType, TreeType, VectorType>
{ lines, tree, point };
return tree.empty() ?
Scalar(-1) :
AABBTreeIndirect::detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0),
std::numeric_limits<Scalar>::infinity(), hit_idx_out, hit_point_out);
}
}
}
#endif /* SRC_LIBSLIC3R_AABBTREELINES_HPP_ */

View file

@ -184,10 +184,10 @@ void AppConfig::set_defaults()
#ifdef _WIN32
#ifdef SUPPORT_3D_CONNEXION
//#ifdef SUPPORT_3D_CONNEXION
if (get("use_legacy_3DConnexion").empty())
set_bool("use_legacy_3DConnexion", false);
#endif
set_bool("use_legacy_3DConnexion", true);
//#endif
#ifdef SUPPORT_DARK_MODE
if (get("dark_color_mode").empty())

View file

@ -447,7 +447,7 @@ protected:
return std::make_tuple(score, fullbb);
}
std::function<double(const Item&)> get_objfn();
std::function<double(const Item&, const ItemGroup&)> get_objfn();
public:
AutoArranger(const TBin & bin,
@ -508,7 +508,8 @@ public:
bin_poly.contour.points.emplace_back(c0.x(), c1.y());
return bin_poly;
};
// preload fixed items (and excluded regions) on plate
m_pconf.on_preload = [this](const ItemGroup &items, PConfig &cfg) {
if (items.empty()) return;
@ -527,8 +528,12 @@ public:
}
}
cfg.object_function = [this, bb, starting_point](const Item& item) {
return fixed_overfit_topright_sliding(objfunc(item, starting_point), bb);
cfg.object_function = [this, bb, starting_point](const Item& item, const ItemGroup& packed_items) {
bool packed_are_excluded_region = std::all_of(packed_items.begin(), packed_items.end(), [](Item& itm) { return itm.is_virt_object && !itm.is_wipe_tower; });
if(packed_are_excluded_region)
return fixed_overfit_topright_sliding(objfunc(item, starting_point), bb);
else
return fixed_overfit(objfunc(item, starting_point), bb);
};
};
@ -597,11 +602,11 @@ public:
}
};
template<> std::function<double(const Item&)> AutoArranger<Box>::get_objfn()
template<> std::function<double(const Item&, const ItemGroup&)> AutoArranger<Box>::get_objfn()
{
auto origin_pack = m_pconf.starting_point == PConfig::Alignment::CENTER ? m_bin.center() : m_bin.minCorner();
return [this, origin_pack](const Item &itm) {
return [this, origin_pack](const Item &itm, const ItemGroup&) {
auto result = objfunc(itm, origin_pack);
double score = std::get<0>(result);
@ -623,11 +628,11 @@ template<> std::function<double(const Item&)> AutoArranger<Box>::get_objfn()
};
}
template<> std::function<double(const Item&)> AutoArranger<Circle>::get_objfn()
template<> std::function<double(const Item&, const ItemGroup&)> AutoArranger<Circle>::get_objfn()
{
auto bb = sl::boundingBox(m_bin);
auto origin_pack = m_pconf.starting_point == PConfig::Alignment::CENTER ? bb.center() : bb.minCorner();
return [this, origin_pack](const Item &item) {
return [this, origin_pack](const Item &item, const ItemGroup&) {
auto result = objfunc(item, origin_pack);
@ -653,11 +658,11 @@ template<> std::function<double(const Item&)> AutoArranger<Circle>::get_objfn()
// Specialization for a generalized polygon.
// Warning: this is much slower than with Box bed. Need further speedup.
template<>
std::function<double(const Item &)> AutoArranger<ExPolygon>::get_objfn()
std::function<double(const Item &, const ItemGroup&)> AutoArranger<ExPolygon>::get_objfn()
{
auto bb = sl::boundingBox(m_bin);
auto origin_pack = m_pconf.starting_point == PConfig::Alignment::CENTER ? bb.center() : bb.minCorner();
return [this, origin_pack](const Item &itm) {
return [this, origin_pack](const Item &itm, const ItemGroup&) {
auto result = objfunc(itm, origin_pack);
double score = std::get<0>(result);

View file

@ -270,7 +270,7 @@ static ExPolygons top_level_outer_brim_area(const Print &print
return diff_ex(brim_area, no_brim_area);
}
// BBS: the brims of different objs will not overlapped with each other, and are stored by objs and by extruders
// BBS: the brims of different objs will not overlapped with each other, and are stored by objs and by extruders
static ExPolygons top_level_outer_brim_area(const Print& print, const ConstPrintObjectPtrs& top_level_objects_with_brim,
const float no_brim_offset, double& brim_width_max, std::map<ObjectID, double>& brim_width_map,
std::map<ObjectID, ExPolygons>& brimAreaMap,
@ -453,7 +453,7 @@ static ExPolygons inner_brim_area(const Print &print,
return diff_ex(intersection_ex(to_polygons(std::move(brim_area)), holes), no_brim_area);
}
// BBS: the brims of different objs will not overlapped with each other, and are stored by objs and by extruders
// BBS: the brims of different objs will not overlapped with each other, and are stored by objs and by extruders
static ExPolygons inner_brim_area(const Print& print, const ConstPrintObjectPtrs& top_level_objects_with_brim,
const float no_brim_offset, std::map<ObjectID, ExPolygons>& brimAreaMap,
std::map<ObjectID, ExPolygons>& supportBrimAreaMap,
@ -875,7 +875,7 @@ static ExPolygons outer_inner_brim_area(const Print& print,
for (const auto& objectWithExtruder : objPrintVec)
brimToWrite.insert({ objectWithExtruder.first, {true,true} });
std::map<ObjectID, ExPolygons> objectIslandMap;
ExPolygons objectIslands;
for (unsigned int extruderNo : printExtruders) {
++extruderNo;
@ -906,7 +906,11 @@ static ExPolygons outer_inner_brim_area(const Print& print,
std::vector<ModelVolume*> groupVolumePtrs;
for (auto& volumeID : volumeGroup.volume_ids) {
ModelVolume* currentModelVolumePtr = nullptr;
for (auto volumePtr : object->model_object()->volumes) {
//BBS: support shared object logic
const PrintObject* shared_object = object->get_shared_object();
if (!shared_object)
shared_object = object;
for (auto volumePtr : shared_object->model_object()->volumes) {
if (volumePtr->id() == volumeID) {
currentModelVolumePtr = volumePtr;
break;
@ -968,7 +972,7 @@ static ExPolygons outer_inner_brim_area(const Print& print,
append_and_translate(brim_area, brim_area_object, instance, print, brimAreaMap);
append_and_translate(no_brim_area, no_brim_area_object, instance);
append_and_translate(holes, holes_object, instance);
append_and_translate(objectIslandMap[instance.print_object->id()], objectIsland, instance);
append_and_translate(objectIslands, objectIsland, instance);
}
if (brimAreaMap.find(object->id()) != brimAreaMap.end())
@ -1032,27 +1036,44 @@ static ExPolygons outer_inner_brim_area(const Print& print,
}
}
}
for (const PrintObject* object : print.objects()) {
for (const PrintObject* object : print.objects())
if (brimAreaMap.find(object->id()) != brimAreaMap.end()) {
brimAreaMap[object->id()] = diff_ex(brimAreaMap[object->id()], no_brim_area);
// BBS: brim should be contacted to at least one object island
if (objectIslandMap.find(object->id()) != objectIslandMap.end() && !objectIslandMap[object->id()].empty()) {
auto tempArea = brimAreaMap[object->id()];
brimAreaMap[object->id()].clear();
// the error bound is set to 2x flow width
for (auto& ta : tempArea) {
auto offsetedTa = offset_ex(ta, print.brim_flow().scaled_spacing() * 2, jtRound, SCALED_RESOLUTION);
if (!intersection_ex(offsetedTa, objectIslandMap[object->id()]).empty())
brimAreaMap[object->id()].push_back(ta);
}
}
}
if (supportBrimAreaMap.find(object->id()) != supportBrimAreaMap.end())
supportBrimAreaMap[object->id()] = diff_ex(supportBrimAreaMap[object->id()], no_brim_area);
}
//brim_area = diff_ex(brim_area, no_brim_area);
brim_area.clear();
for (const PrintObject* object : print.objects()) {
// BBS: brim should be contacted to at least one object's island or brim area
if (brimAreaMap.find(object->id()) != brimAreaMap.end()) {
// find other objects' brim area
ExPolygons otherExPolys;
for (const PrintObject* otherObject : print.objects()) {
if ((otherObject->id() != object->id()) && (brimAreaMap.find(otherObject->id()) != brimAreaMap.end())) {
expolygons_append(otherExPolys, brimAreaMap[otherObject->id()]);
}
}
auto tempArea = brimAreaMap[object->id()];
brimAreaMap[object->id()].clear();
for (int ia = 0; ia != tempArea.size(); ++ia) {
// find this object's other brim area
ExPolygons otherExPoly;
for (int iao = 0; iao != tempArea.size(); ++iao)
if (iao != ia) otherExPoly.push_back(tempArea[iao]);
auto offsetedTa = offset_ex(tempArea[ia], print.brim_flow().scaled_spacing() * 2, jtRound, SCALED_RESOLUTION);
if (!intersection_ex(offsetedTa, objectIslands).empty() ||
!intersection_ex(offsetedTa, otherExPoly).empty() ||
!intersection_ex(offsetedTa, otherExPolys).empty())
brimAreaMap[object->id()].push_back(tempArea[ia]);
}
expolygons_append(brim_area, brimAreaMap[object->id()]);
}
}
return brim_area;
}
// Flip orientation of open polylines to minimize travel distance.
@ -1066,7 +1087,7 @@ static void optimize_polylines_by_reversing(Polylines *polylines)
double dist_to_start = (next.first_point() - prev.last_point()).cast<double>().norm();
double dist_to_end = (next.last_point() - prev.last_point()).cast<double>().norm();
if (dist_to_end < dist_to_start)
if (dist_to_end < dist_to_start)
next.reverse();
}
}

View file

@ -142,10 +142,12 @@ set(lisbslic3r_sources
GCodeWriter.hpp
Geometry.cpp
Geometry.hpp
Geometry/Bicubic.hpp
Geometry/Circle.cpp
Geometry/Circle.hpp
Geometry/ConvexHull.cpp
Geometry/ConvexHull.hpp
Geometry/Curves.hpp
Geometry/MedialAxis.cpp
Geometry/MedialAxis.hpp
Geometry/Voronoi.hpp
@ -176,6 +178,8 @@ set(lisbslic3r_sources
CustomGCode.hpp
Arrange.hpp
Arrange.cpp
NormalUtils.cpp
NormalUtils.hpp
Orient.hpp
Orient.cpp
MultiPoint.cpp
@ -222,6 +226,8 @@ set(lisbslic3r_sources
QuadricEdgeCollapse.cpp
QuadricEdgeCollapse.hpp
Semver.cpp
ShortEdgeCollapse.cpp
ShortEdgeCollapse.hpp
ShortestPath.cpp
ShortestPath.hpp
SLAPrint.cpp
@ -264,6 +270,8 @@ set(lisbslic3r_sources
Thread.hpp
TriangleSelector.cpp
TriangleSelector.hpp
TriangleSetSampling.cpp
TriangleSetSampling.hpp
MTUtils.hpp
VariableWidth.cpp
VariableWidth.hpp
@ -315,7 +323,6 @@ set(lisbslic3r_sources
SLA/Clustering.hpp
SLA/Clustering.cpp
SLA/ReprojectPointsOnMesh.hpp
Arachne/BeadingStrategy/BeadingStrategy.hpp
Arachne/BeadingStrategy/BeadingStrategy.cpp
Arachne/BeadingStrategy/BeadingStrategyFactory.hpp
@ -356,6 +363,8 @@ set(lisbslic3r_sources
Arachne/SkeletalTrapezoidationJoint.hpp
Arachne/WallToolPaths.hpp
Arachne/WallToolPaths.cpp
Shape/TextShape.hpp
Shape/TextShape.cpp
)
if (APPLE)

View file

@ -158,11 +158,10 @@ double ExtrusionLoop::length() const
return len;
}
bool ExtrusionLoop::split_at_vertex(const Point &point)
bool ExtrusionLoop::split_at_vertex(const Point &point, const double scaled_epsilon)
{
for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
int idx = path->polyline.find_point(point);
if (idx != -1) {
if (int idx = path->polyline.find_point(point, scaled_epsilon); idx != -1) {
if (this->paths.size() == 1) {
// just change the order of points
Polyline p1, p2;
@ -207,46 +206,57 @@ bool ExtrusionLoop::split_at_vertex(const Point &point)
return false;
}
std::pair<size_t, Point> ExtrusionLoop::get_closest_path_and_point(const Point& point, bool prefer_non_overhang) const
ExtrusionLoop::ClosestPathPoint ExtrusionLoop::get_closest_path_and_point(const Point &point, bool prefer_non_overhang) const
{
// Find the closest path and closest point belonging to that path. Avoid overhangs, if asked for.
size_t path_idx = 0;
Point p;
{
double min = std::numeric_limits<double>::max();
Point p_non_overhang;
size_t path_idx_non_overhang = 0;
double min_non_overhang = std::numeric_limits<double>::max();
for (const ExtrusionPath& path : this->paths) {
Point p_tmp = point.projection_onto(path.polyline);
double dist = (p_tmp - point).cast<double>().norm();
if (dist < min) {
p = p_tmp;
min = dist;
path_idx = &path - &this->paths.front();
}
if (prefer_non_overhang && !is_bridge(path.role()) && dist < min_non_overhang) {
p_non_overhang = p_tmp;
min_non_overhang = dist;
path_idx_non_overhang = &path - &this->paths.front();
}
ClosestPathPoint out{0, 0};
double min2 = std::numeric_limits<double>::max();
ClosestPathPoint best_non_overhang{0, 0};
double min2_non_overhang = std::numeric_limits<double>::max();
for (const ExtrusionPath &path : this->paths) {
std::pair<int, Point> foot_pt_ = foot_pt(path.polyline.points, point);
double d2 = (foot_pt_.second - point).cast<double>().squaredNorm();
if (d2 < min2) {
out.foot_pt = foot_pt_.second;
out.path_idx = &path - &this->paths.front();
out.segment_idx = foot_pt_.first;
min2 = d2;
}
if (prefer_non_overhang && min_non_overhang != std::numeric_limits<double>::max()) {
// Only apply the non-overhang point if there is one.
path_idx = path_idx_non_overhang;
p = p_non_overhang;
if (prefer_non_overhang && !is_bridge(path.role()) && d2 < min2_non_overhang) {
best_non_overhang.foot_pt = foot_pt_.second;
best_non_overhang.path_idx = &path - &this->paths.front();
best_non_overhang.segment_idx = foot_pt_.first;
min2_non_overhang = d2;
}
}
return std::make_pair(path_idx, p);
if (prefer_non_overhang && min2_non_overhang != std::numeric_limits<double>::max())
// Only apply the non-overhang point if there is one.
out = best_non_overhang;
return out;
}
// Splitting an extrusion loop, possibly made of multiple segments, some of the segments may be bridging.
void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang, const double scaled_epsilon)
{
if (this->paths.empty())
return;
auto [path_idx, p] = get_closest_path_and_point(point, prefer_non_overhang);
auto [path_idx, segment_idx, p] = get_closest_path_and_point(point, prefer_non_overhang);
// Snap p to start or end of segment_idx if closer than scaled_epsilon.
{
const Point *p1 = this->paths[path_idx].polyline.points.data() + segment_idx;
const Point *p2 = p1;
++p2;
double d2_1 = (point - *p1).cast<double>().squaredNorm();
double d2_2 = (point - *p2).cast<double>().squaredNorm();
const double thr2 = scaled_epsilon * scaled_epsilon;
if (d2_1 < d2_2) {
if (d2_1 < thr2) p = *p1;
} else {
if (d2_2 < thr2) p = *p2;
}
}
// now split path_idx in two parts
const ExtrusionPath &path = this->paths[path_idx];

View file

@ -314,9 +314,15 @@ public:
const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); }
Polygon polygon() const;
double length() const override;
bool split_at_vertex(const Point &point);
void split_at(const Point &point, bool prefer_non_overhang);
std::pair<size_t, Point> get_closest_path_and_point(const Point& point, bool prefer_non_overhang) const;
bool split_at_vertex(const Point &point, const double scaled_epsilon = scaled<double>(0.001));
void split_at(const Point &point, bool prefer_non_overhang, const double scaled_epsilon = scaled<double>(0.001));
struct ClosestPathPoint
{
size_t path_idx;
size_t segment_idx;
Point foot_pt;
};
ClosestPathPoint get_closest_path_and_point(const Point &point, bool prefer_non_overhang) const;
void clip_end(double distance, ExtrusionPaths* paths) const;
// Test, whether the point is extruded by a bridging flow.
// This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.

View file

@ -31,9 +31,6 @@
const double STEP_TRANS_CHORD_ERROR = 0.005;
const double STEP_TRANS_ANGLE_RES = 1;
const int LOAD_STEP_STAGE_READ_FILE = 0;
const int LOAD_STEP_STAGE_GET_SOLID = 1;
const int LOAD_STEP_STAGE_GET_MESH = 2;
namespace Slic3r {
@ -213,11 +210,11 @@ static void getNamedSolids(const TopLoc_Location& location, const std::string& p
}
}
bool load_step(const char *path, Model *model, ImportStepProgressFn proFn, StepIsUtf8Fn isUtf8Fn)
bool load_step(const char *path, Model *model, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn)
{
bool cb_cancel = false;
if (proFn) {
proFn(LOAD_STEP_STAGE_READ_FILE, 0, 1, cb_cancel);
if (stepFn) {
stepFn(LOAD_STEP_STAGE_READ_FILE, 0, 1, cb_cancel);
if (cb_cancel)
return false;
}
@ -245,9 +242,13 @@ bool load_step(const char *path, Model *model, ImportStepProgressFn proFn, StepI
unsigned int id{1};
Standard_Integer topShapeLength = topLevelShapes.Length() + 1;
auto stage_unit2 = topShapeLength / LOAD_STEP_STAGE_UNIT_NUM + 1;
for (Standard_Integer iLabel = 1; iLabel < topShapeLength; ++iLabel) {
if (proFn) {
proFn(LOAD_STEP_STAGE_GET_SOLID, iLabel, topShapeLength, cb_cancel);
if (stepFn) {
if ((iLabel % stage_unit2) == 0) {
stepFn(LOAD_STEP_STAGE_GET_SOLID, iLabel, topShapeLength, cb_cancel);
}
if (cb_cancel) {
shapeTool.reset(nullptr);
application->Close(document);
@ -257,14 +258,94 @@ bool load_step(const char *path, Model *model, ImportStepProgressFn proFn, StepI
getNamedSolids(TopLoc_Location{}, "", id, shapeTool, topLevelShapes.Value(iLabel), namedSolids);
}
ModelObject* new_object = model->add_object();
const char *last_slash = strrchr(path, DIR_SEPARATOR);
std::vector<stl_file> stl;
stl.resize(namedSolids.size());
tbb::parallel_for(tbb::blocked_range<size_t>(0, namedSolids.size()), [&](const tbb::blocked_range<size_t> &range) {
for (size_t i = range.begin(); i < range.end(); i++) {
BRepMesh_IncrementalMesh mesh(namedSolids[i].solid, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true);
// BBS: calculate total number of the nodes and triangles
int aNbNodes = 0;
int aNbTriangles = 0;
for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(anExpSF.Current()), aLoc);
if (!aTriangulation.IsNull()) {
aNbNodes += aTriangulation->NbNodes();
aNbTriangles += aTriangulation->NbTriangles();
}
}
if (aNbTriangles == 0)
// BBS: No triangulation on the shape.
continue;
stl[i].stats.type = inmemory;
stl[i].stats.number_of_facets = (uint32_t) aNbTriangles;
stl[i].stats.original_num_facets = stl[i].stats.number_of_facets;
stl_allocate(&stl[i]);
std::vector<Vec3f> points;
points.reserve(aNbNodes);
// BBS: count faces missing triangulation
Standard_Integer aNbFacesNoTri = 0;
// BBS: fill temporary triangulation
Standard_Integer aNodeOffset = 0;
Standard_Integer aTriangleOffet = 0;
for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
const TopoDS_Shape &aFace = anExpSF.Current();
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(aFace), aLoc);
if (aTriangulation.IsNull()) {
++aNbFacesNoTri;
continue;
}
// BBS: copy nodes
gp_Trsf aTrsf = aLoc.Transformation();
for (Standard_Integer aNodeIter = 1; aNodeIter <= aTriangulation->NbNodes(); ++aNodeIter) {
gp_Pnt aPnt = aTriangulation->Node(aNodeIter);
aPnt.Transform(aTrsf);
points.emplace_back(std::move(Vec3f(aPnt.X(), aPnt.Y(), aPnt.Z())));
}
// BBS: copy triangles
const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation();
Standard_Integer anId[3];
for (Standard_Integer aTriIter = 1; aTriIter <= aTriangulation->NbTriangles(); ++aTriIter) {
Poly_Triangle aTri = aTriangulation->Triangle(aTriIter);
aTri.Get(anId[0], anId[1], anId[2]);
if (anOrientation == TopAbs_REVERSED)
std::swap(anId[1], anId[2]);
// BBS: save triangles facets
stl_facet facet;
facet.vertex[0] = points[anId[0] + aNodeOffset - 1].cast<float>();
facet.vertex[1] = points[anId[1] + aNodeOffset - 1].cast<float>();
facet.vertex[2] = points[anId[2] + aNodeOffset - 1].cast<float>();
facet.extra[0] = 0;
facet.extra[1] = 0;
stl_normal normal;
stl_calculate_normal(normal, &facet);
stl_normalize_vector(normal);
facet.normal = normal;
stl[i].facet_start[aTriangleOffet + aTriIter - 1] = facet;
}
aNodeOffset += aTriangulation->NbNodes();
aTriangleOffet += aTriangulation->NbTriangles();
}
}
});
ModelObject *new_object = model->add_object();
const char * last_slash = strrchr(path, DIR_SEPARATOR);
new_object->name.assign((last_slash == nullptr) ? path : last_slash + 1);
new_object->input_file = path;
for (size_t i = 0; i < namedSolids.size(); ++i) {
if (proFn) {
proFn(LOAD_STEP_STAGE_GET_MESH, i, namedSolids.size(), cb_cancel);
auto stage_unit3 = stl.size() / LOAD_STEP_STAGE_UNIT_NUM + 1;
for (size_t i = 0; i < stl.size(); i++) {
if (stepFn) {
if ((i % stage_unit3) == 0) {
stepFn(LOAD_STEP_STAGE_GET_MESH, i, stl.size(), cb_cancel);
}
if (cb_cancel) {
model->delete_object(new_object);
shapeTool.reset(nullptr);
@ -273,94 +354,13 @@ bool load_step(const char *path, Model *model, ImportStepProgressFn proFn, StepI
}
}
BRepMesh_IncrementalMesh mesh(namedSolids[i].solid, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true);
//BBS: calculate total number of the nodes and triangles
int aNbNodes = 0;
int aNbTriangles = 0;
for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(anExpSF.Current()), aLoc);
if (!aTriangulation.IsNull()) {
aNbNodes += aTriangulation->NbNodes();
aNbTriangles += aTriangulation->NbTriangles();
}
}
if (aNbTriangles == 0) {
//BBS: No triangulation on the shape.
continue;
}
stl_file stl;
stl.stats.type = inmemory;
stl.stats.number_of_facets = (uint32_t)aNbTriangles;
stl.stats.original_num_facets = stl.stats.number_of_facets;
stl_allocate(&stl);
std::vector<Vec3f> points;
points.reserve(aNbNodes);
//BBS: count faces missing triangulation
Standard_Integer aNbFacesNoTri = 0;
//BBS: fill temporary triangulation
Standard_Integer aNodeOffset = 0;
Standard_Integer aTriangleOffet = 0;
for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
const TopoDS_Shape& aFace = anExpSF.Current();
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(aFace), aLoc);
if (aTriangulation.IsNull()) {
++aNbFacesNoTri;
continue;
}
//BBS: copy nodes
gp_Trsf aTrsf = aLoc.Transformation();
for (Standard_Integer aNodeIter = 1; aNodeIter <= aTriangulation->NbNodes(); ++aNodeIter) {
gp_Pnt aPnt = aTriangulation->Node(aNodeIter);
aPnt.Transform(aTrsf);
points.emplace_back(std::move(Vec3f(aPnt.X(), aPnt.Y(), aPnt.Z())));
}
//BBS: copy triangles
const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation();
for (Standard_Integer aTriIter = 1; aTriIter <= aTriangulation->NbTriangles(); ++aTriIter) {
Poly_Triangle aTri = aTriangulation->Triangle(aTriIter);
Standard_Integer anId[3];
aTri.Get(anId[0], anId[1], anId[2]);
if (anOrientation == TopAbs_REVERSED) {
//BBS: swap 1, 2.
Standard_Integer aTmpIdx = anId[1];
anId[1] = anId[2];
anId[2] = aTmpIdx;
}
//BBS: Update nodes according to the offset.
anId[0] += aNodeOffset;
anId[1] += aNodeOffset;
anId[2] += aNodeOffset;
//BBS: save triangles facets
stl_facet facet;
facet.vertex[0] = points[anId[0] - 1].cast<float>();
facet.vertex[1] = points[anId[1] - 1].cast<float>();
facet.vertex[2] = points[anId[2] - 1].cast<float>();
facet.extra[0] = 0;
facet.extra[1] = 0;
stl_normal normal;
stl_calculate_normal(normal, &facet);
stl_normalize_vector(normal);
facet.normal = normal;
stl.facet_start[aTriangleOffet + aTriIter - 1] = facet;
}
aNodeOffset += aTriangulation->NbNodes();
aTriangleOffet += aTriangulation->NbTriangles();
}
TriangleMesh triangle_mesh;
triangle_mesh.from_stl(stl);
ModelVolume* new_volume = new_object->add_volume(std::move(triangle_mesh));
new_volume->name = namedSolids[i].name;
triangle_mesh.from_stl(stl[i]);
ModelVolume *new_volume = new_object->add_volume(std::move(triangle_mesh));
new_volume->name = namedSolids[i].name;
new_volume->source.input_file = path;
new_volume->source.object_idx = (int)model->objects.size() - 1;
new_volume->source.volume_idx = (int)new_object->volumes.size() - 1;
new_volume->source.object_idx = (int) model->objects.size() - 1;
new_volume->source.volume_idx = (int) new_object->volumes.size() - 1;
}
shapeTool.reset(nullptr);

View file

@ -6,6 +6,13 @@ namespace Slic3r {
class TriangleMesh;
class ModelObject;
// load step stage
const int LOAD_STEP_STAGE_READ_FILE = 0;
const int LOAD_STEP_STAGE_GET_SOLID = 1;
const int LOAD_STEP_STAGE_GET_MESH = 2;
const int LOAD_STEP_STAGE_NUM = 3;
const int LOAD_STEP_STAGE_UNIT_NUM = 5;
typedef std::function<void(int load_stage, int current, int total, bool& cancel)> ImportStepProgressFn;
typedef std::function<void(bool isUtf8)> StepIsUtf8Fn;

View file

@ -14,10 +14,10 @@
namespace Slic3r {
bool load_stl(const char *path, Model *model, const char *object_name_in)
bool load_stl(const char *path, Model *model, const char *object_name_in, ImportstlProgressFn stlFn)
{
TriangleMesh mesh;
if (! mesh.ReadSTLFile(path)) {
if (! mesh.ReadSTLFile(path, true, stlFn)) {
// die "Failed to open $file\n" if !-e $path;
return false;
}

View file

@ -1,13 +1,16 @@
#ifndef slic3r_Format_STL_hpp_
#define slic3r_Format_STL_hpp_
#include <admesh/stl.h>
namespace Slic3r {
class Model;
class TriangleMesh;
class ModelObject;
// Load an STL file into a provided model.
extern bool load_stl(const char *path, Model *model, const char *object_name = nullptr);
extern bool load_stl(const char *path, Model *model, const char *object_name = nullptr, ImportstlProgressFn stlFn = nullptr);
extern bool store_stl(const char *path, TriangleMesh *mesh, bool binary);
extern bool store_stl(const char *path, ModelObject *model_object, bool binary);

View file

@ -258,6 +258,8 @@ static constexpr const char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
static constexpr const char* SOURCE_IN_INCHES = "source_in_inches";
static constexpr const char* SOURCE_IN_METERS = "source_in_meters";
static constexpr const char* MESH_SHARED_KEY = "mesh_shared";
static constexpr const char* MESH_STAT_EDGES_FIXED = "edges_fixed";
static constexpr const char* MESH_STAT_DEGENERATED_FACETS = "degenerate_facets";
static constexpr const char* MESH_STAT_FACETS_REMOVED = "facets_removed";
@ -702,6 +704,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
std::string m_thumbnail_path;
std::vector<std::string> m_sub_model_paths;
std::map<int, ModelVolume*> m_shared_meshes;
//BBS: plater related structures
bool m_is_bbl_3mf { false };
bool m_parsing_slice_info { false };
@ -847,8 +851,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
bool _handle_start_relationship(const char** attributes, unsigned int num_attributes);
void _generate_current_object_list(std::vector<Id> &sub_objects, Id object_id, IdToCurrentObjectMap current_objects);
bool _generate_volumes_new(ModelObject& object, const std::vector<Id> &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions);
void _generate_current_object_list(std::vector<Component> &sub_objects, Id object_id, IdToCurrentObjectMap current_objects);
bool _generate_volumes_new(ModelObject& object, const std::vector<Component> &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions);
bool _generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions);
// callbacks to parse the .model file
@ -1281,7 +1285,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
add_error("3rd 3mf, can not find object, id " + std::to_string(object.first.second));
return false;
}
std::vector<Id> object_id_list;
std::vector<Component> object_id_list;
_generate_current_object_list(object_id_list, object.first, m_current_objects);
ObjectMetadata::VolumeMetadataList volumes;
@ -1289,7 +1293,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
for (int k = 0; k < object_id_list.size(); k++)
{
Id object_id = object_id_list[k];
Id object_id = object_id_list[k].object_id;
volumes.emplace_back(object_id.second);
}
@ -1344,7 +1348,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
model_object->sla_drain_holes = std::move(obj_drain_holes->second);
}*/
std::vector<Id> object_id_list;
std::vector<Component> object_id_list;
_generate_current_object_list(object_id_list, object.first, m_current_objects);
ObjectMetadata::VolumeMetadataList volumes;
@ -1375,7 +1379,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
//volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() - 1);
for (int k = 0; k < object_id_list.size(); k++)
{
Id object_id = object_id_list[k];
Id object_id = object_id_list[k].object_id;
volumes.emplace_back(object_id.second);
}
@ -2757,31 +2761,31 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
check_painting_version(m_mm_painting_version, MM_PAINTING_VERSION,
_(L("The selected 3MF contains multi-material painted object using a newer version of BambuStudio and is not compatible.")));*/
} else if (m_curr_metadata_name == BBL_MODEL_ID_TAG) {
m_model_id = m_curr_characters;
m_model_id = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_MODEL_NAME_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found model name = " << m_curr_characters;
model_info.model_name = m_curr_characters;
model_info.model_name = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_DESIGNER_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found designer = " << m_curr_characters;
m_designer = m_curr_characters;
m_designer = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_DESIGNER_USER_ID_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found designer_user_id = " << m_curr_characters;
m_designer_user_id = m_curr_characters;
m_designer_user_id = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_DESIGNER_COVER_FILE_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found designer_cover = " << m_curr_characters;
model_info.cover_file = m_curr_characters;
model_info.cover_file = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_DESCRIPTION_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found description = " << m_curr_characters;
model_info.description = m_curr_characters;
model_info.description = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_LICENSE_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found license = " << m_curr_characters;
model_info.license = m_curr_characters;
model_info.license = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_COPYRIGHT_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found copyright = " << m_curr_characters;
model_info.copyright = m_curr_characters;
model_info.copyright = xml_unescape(m_curr_characters);
} else if (m_curr_metadata_name == BBL_REGION_TAG) {
BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found region = " << m_curr_characters;
m_contry_code = m_curr_characters;
m_contry_code = xml_unescape(m_curr_characters);
}
return true;
@ -3279,34 +3283,34 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return true;
}
void _BBS_3MF_Importer::_generate_current_object_list(std::vector<Id> &sub_objects, Id object_id, IdToCurrentObjectMap current_objects)
void _BBS_3MF_Importer::_generate_current_object_list(std::vector<Component> &sub_objects, Id object_id, IdToCurrentObjectMap current_objects)
{
std::list<Id> id_list;
id_list.push_back(object_id);
std::list<Component> id_list;
id_list.push_back({ object_id, Transform3d::Identity() });
while (!id_list.empty())
{
Id current_id = id_list.front();
Component current_id = id_list.front();
id_list.pop_front();
IdToCurrentObjectMap::iterator current_object = current_objects.find(current_id);
IdToCurrentObjectMap::iterator current_object = current_objects.find(current_id.object_id);
if (current_object != current_objects.end()) {
//found one
if (!current_object->second.components.empty()) {
for (const Component& comp: current_object->second.components)
{
id_list.push_back(comp.object_id);
id_list.push_back(comp);
}
}
else if (!(current_object->second.geometry.empty())) {
//CurrentObject* ptr = &(current_objects[current_id]);
//CurrentObject* ptr2 = &(current_object->second);
sub_objects.push_back(current_object->first);
sub_objects.push_back({ current_object->first, current_id.transform });
}
}
}
}
bool _BBS_3MF_Importer::_generate_volumes_new(ModelObject& object, const std::vector<Id> &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions)
bool _BBS_3MF_Importer::_generate_volumes_new(ModelObject& object, const std::vector<Component> &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions)
{
if (!object.volumes.empty()) {
add_error("object already built with parts");
@ -3319,7 +3323,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
for (unsigned int index = 0; index < sub_objects.size(); index++)
{
//find the volume metadata firstly
Id object_id = sub_objects[index];
Component sub_comp = sub_objects[index];
Id object_id = sub_comp.object_id;
IdToCurrentObjectMap::iterator current_object = m_current_objects.find(object_id);
if (current_object == m_current_objects.end()) {
add_error("sub_objects can not be found, id=" + std::to_string(object_id.second));
@ -3338,70 +3343,114 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
Transform3d volume_matrix_to_object = Transform3d::Identity();
bool has_transform = false;
int shared_mesh_id = -1;
if (volume_data)
{
int found_count = 0;
// extract the volume transformation from the volume's metadata, if present
for (const Metadata& metadata : volume_data->metadata) {
if (metadata.key == MATRIX_KEY) {
volume_matrix_to_object = Slic3r::Geometry::transform3d_from_string(metadata.value);
has_transform = ! volume_matrix_to_object.isApprox(Transform3d::Identity(), 1e-10);
break;
found_count++;
}
else if (metadata.key == MESH_SHARED_KEY){
//add the shared mesh logic
shared_mesh_id = ::atoi(metadata.value.c_str());
found_count++;
}
if (found_count >= 2)
break;
}
}
else {
//create a volume_data
volume_data = &default_volume_data;
}
// splits volume out of imported geometry
indexed_triangle_set its;
its.indices.assign(sub_object->geometry.triangles.begin(), sub_object->geometry.triangles.end());
const size_t triangles_count = its.indices.size();
ModelVolume* volume = nullptr;
ModelVolume *shared_volume = nullptr;
if (shared_mesh_id != -1) {
std::map<int, ModelVolume*>::iterator iter = m_shared_meshes.find(shared_mesh_id);
if (iter != m_shared_meshes.end()) {
shared_volume = iter->second;
}
}
const size_t triangles_count = sub_object->geometry.triangles.size();
if (triangles_count == 0) {
add_error("found no trianges in the object " + std::to_string(sub_object->id));
return false;
}
for (const Vec3i& face : its.indices) {
for (const int tri_id : face) {
if (tri_id < 0 || tri_id >= int(sub_object->geometry.vertices.size())) {
add_error("invalid vertex id in object " + std::to_string(sub_object->id));
return false;
if (!shared_volume){
// splits volume out of imported geometry
indexed_triangle_set its;
its.indices.assign(sub_object->geometry.triangles.begin(), sub_object->geometry.triangles.end());
//const size_t triangles_count = its.indices.size();
//if (triangles_count == 0) {
// add_error("found no trianges in the object " + std::to_string(sub_object->id));
// return false;
//}
for (const Vec3i& face : its.indices) {
for (const int tri_id : face) {
if (tri_id < 0 || tri_id >= int(sub_object->geometry.vertices.size())) {
add_error("invalid vertex id in object " + std::to_string(sub_object->id));
return false;
}
}
}
}
its.vertices.assign(sub_object->geometry.vertices.begin(), sub_object->geometry.vertices.end());
its.vertices.assign(sub_object->geometry.vertices.begin(), sub_object->geometry.vertices.end());
// BBS
for (const std::string prop_str : sub_object->geometry.face_properties) {
FaceProperty face_prop;
face_prop.from_string(prop_str);
its.properties.push_back(face_prop);
}
TriangleMesh triangle_mesh(std::move(its), volume_data->mesh_stats);
if (m_version == 0) {
// if the 3mf was not produced by BambuStudio and there is only one instance,
// bake the transformation into the geometry to allow the reload from disk command
// to work properly
if (object.instances.size() == 1) {
triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false);
object.instances.front()->set_transformation(Slic3r::Geometry::Transformation());
//FIXME do the mesh fixing?
// BBS
for (const std::string prop_str : sub_object->geometry.face_properties) {
FaceProperty face_prop;
face_prop.from_string(prop_str);
its.properties.push_back(face_prop);
}
}
if (triangle_mesh.volume() < 0)
triangle_mesh.flip_triangles();
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
TriangleMesh triangle_mesh(std::move(its), volume_data->mesh_stats);
if (m_version == 0) {
// if the 3mf was not produced by BambuStudio and there is only one instance,
// bake the transformation into the geometry to allow the reload from disk command
// to work properly
if (object.instances.size() == 1) {
triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false);
object.instances.front()->set_transformation(Slic3r::Geometry::Transformation());
//FIXME do the mesh fixing?
}
}
if (triangle_mesh.volume() < 0)
triangle_mesh.flip_triangles();
volume = object.add_volume(std::move(triangle_mesh));
m_shared_meshes[sub_object->id] = volume;
}
else {
//create volume to use shared mesh
volume = object.add_volume_with_shared_mesh(*shared_volume);
}
// stores the volume matrix taken from the metadata, if present
if (has_transform)
volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
volume->calculate_convex_hull();
//set transform from 3mf
Slic3r::Geometry::Transformation comp_transformatino(sub_comp.transform);
volume->set_transformation(volume->get_transformation() * comp_transformatino);
if (shared_volume) {
const TriangleMesh& trangle_mesh = volume->mesh();
Vec3d shift = trangle_mesh.get_init_shift();
if (!shift.isApprox(Vec3d::Zero()))
volume->translate(shift);
}
// recreate custom supports, seam and mmu segmentation from previously loaded attribute
if (m_load_config) {
{
volume->supported_facets.reserve(triangles_count);
volume->seam_facets.reserve(triangles_count);
volume->mmu_segmentation_facets.reserve(triangles_count);
@ -3448,7 +3497,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
volume->source.is_converted_from_inches = metadata.value == "1";
else if (metadata.key == SOURCE_IN_METERS)
volume->source.is_converted_from_meters = metadata.value == "1";
else if (metadata.key == MATRIX_KEY)
else if ((metadata.key == MATRIX_KEY) || (metadata.key == MESH_SHARED_KEY))
continue;
else
volume->config.set_deserialize(metadata.key, metadata.value, config_substitutions);
@ -3814,18 +3863,38 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
auto filename = boost::format("3D/Objects/%s_%d.model") % object.name % obj_id;
std::string filepath = temp_path + "/" + filename.str();
if (!open_zip_writer(&archive, filepath)) {
std::string filepath_tmp = filepath + ".tmp";
boost::system::error_code ec;
boost::filesystem::remove(filepath_tmp, ec);
if (!open_zip_writer(&archive, filepath_tmp)) {
add_error("Unable to open the file");
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to open the file\n");
return false;
}
struct close_lock
{
mz_zip_archive & archive;
std::string const * filename;
void close() {
close_zip_writer(&archive);
filename = nullptr;
}
~close_lock() {
if (filename) {
close_zip_writer(&archive);
boost::filesystem::remove(*filename);
}
}
} lock{archive, &filepath_tmp};
IdToObjectDataMap objects_data;
objects_data.insert({obj_id, {&object, obj_id}});
_add_model_file_to_archive(filename.str(), archive, model, objects_data);
mz_zip_writer_finalize_archive(&archive);
close_zip_writer(&archive);
lock.close();
boost::filesystem::rename(filepath_tmp, filepath, ec);
return true;
}
@ -4363,6 +4432,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return false;
}
{
std::stringstream stream;
reset_stream(stream);
@ -4413,27 +4483,29 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
model_id = project->project_model_id;
region_code = project->project_country_code;
}
if (!sub_model) {
stream << " <" << METADATA_TAG << " name=\"" << BBL_MODEL_NAME_TAG << "\">" << xml_escape(name) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESIGNER_TAG << "\">" << xml_escape(user_name) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESIGNER_USER_ID_TAG << "\">" << user_id << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESIGNER_COVER_FILE_TAG << "\">" << xml_escape(design_cover) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESCRIPTION_TAG << "\">" << xml_escape(description) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_COPYRIGHT_TAG << "\">" << xml_escape(copyright) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_LICENSE_TAG << "\">" << xml_escape(license) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_MODEL_NAME_TAG << "\">" << xml_escape(name) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESIGNER_TAG << "\">" << xml_escape(user_name) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESIGNER_USER_ID_TAG << "\">" << user_id << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESIGNER_COVER_FILE_TAG << "\">" << xml_escape(design_cover) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_DESCRIPTION_TAG << "\">" << xml_escape(description) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_COPYRIGHT_TAG << "\">" << xml_escape(copyright) << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_LICENSE_TAG << "\">" << xml_escape(license) << "</" << METADATA_TAG << ">\n";
/* save model info */
if (!model_id.empty()) {
stream << " <" << METADATA_TAG << " name=\"" << BBL_MODEL_ID_TAG << "\">" << model_id << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_REGION_TAG << "\">" << region_code << "</" << METADATA_TAG << ">\n";
}
/* save model info */
if (!model_id.empty()) {
stream << " <" << METADATA_TAG << " name=\"" << BBL_MODEL_ID_TAG << "\">" << model_id << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"" << BBL_REGION_TAG << "\">" << region_code << "</" << METADATA_TAG << ">\n";
std::string date = Slic3r::Utils::utc_timestamp(Slic3r::Utils::get_current_time_utc());
// keep only the date part of the string
date = date.substr(0, 10);
stream << " <" << METADATA_TAG << " name=\"CreationDate\">" << date << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"ModificationDate\">" << date << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"Application\">" << SLIC3R_APP_KEY << "-" << SLIC3R_VERSION << "</" << METADATA_TAG << ">\n";
}
std::string date = Slic3r::Utils::utc_timestamp(Slic3r::Utils::get_current_time_utc());
// keep only the date part of the string
date = date.substr(0, 10);
stream << " <" << METADATA_TAG << " name=\"CreationDate\">" << date << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"ModificationDate\">" << date << "</" << METADATA_TAG << ">\n";
stream << " <" << METADATA_TAG << " name=\"Application\">" << SLIC3R_APP_KEY << "-" << SLIC3R_VERSION << "</" << METADATA_TAG << ">\n";
stream << " <" << RESOURCES_TAG << ">\n";
std::string buf = stream.str();
if (! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) {
@ -4645,12 +4717,37 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
if (m_from_backup_save) {
for (unsigned int index = 1; index <= object.volumes.size(); index ++) {
unsigned int ref_id = object_id | (index << 16);
stream << " <" << COMPONENT_TAG << " objectid=\"" << ref_id << "\"/>\n";
stream << " <" << COMPONENT_TAG << " objectid=\"" << ref_id; // << "\"/>\n";
//add the transform of the volume
ModelVolume* volume = object.volumes[index - 1];
const Transform3d& transf = volume->get_matrix();
stream << "\" " << TRANSFORM_ATTR << "=\"";
for (unsigned c = 0; c < 4; ++c) {
for (unsigned r = 0; r < 3; ++r) {
stream << transf(r, c);
if (r != 2 || c != 3)
stream << " ";
}
}
stream << "\"/>\n";
}
}
else {
for (unsigned int index = object_id; index < volume_start_id; index ++)
stream << " <" << COMPONENT_TAG << " objectid=\"" << index << "\"/>\n";
for (unsigned int index = object_id; index < volume_start_id; index ++) {
stream << " <" << COMPONENT_TAG << " objectid=\"" << index; // << "\"/>\n";
//add the transform of the volume
ModelVolume* volume = object.volumes[index - object_id];
const Transform3d& transf = volume->get_matrix();
stream << "\" " << TRANSFORM_ATTR << "=\"";
for (unsigned c = 0; c < 4; ++c) {
for (unsigned r = 0; r < 3; ++r) {
stream << transf(r, c);
if (r != 2 || c != 3)
stream << " ";
}
}
stream << "\"/>\n";
}
}
}
else {
@ -4800,7 +4897,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
const Transform3d& matrix = volume->get_matrix();
for (size_t i = 0; i < its.vertices.size(); ++i) {
Vec3f v = (matrix * its.vertices[i].cast<double>()).cast<float>();
//don't save the volume's matrix into vertex data
//add the shared mesh logic
//Vec3f v = (matrix * its.vertices[i].cast<double>()).cast<float>();
Vec3f v = its.vertices[i];
char* ptr = buf;
boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << VERTEX_TAG << " x=\"");
ptr = format_coordinate(v.x(), ptr);
@ -5202,6 +5302,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
bool _BBS_3MF_Exporter::_add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const IdToObjectDataMap &objects_data, int export_plate_idx, bool save_gcode)
{
std::stringstream stream;
std::map<const TriangleMesh*, int> shared_meshes;
// Store mesh transformation in full precision, as the volumes are stored transformed and they need to be transformed back
// when loaded as accurately as possible.
stream << std::setprecision(std::numeric_limits<double>::max_digits10);
@ -5294,6 +5395,17 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
stream << " <" << METADATA_TAG << " "<< KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n";
}
//add the shared mesh logic
const TriangleMesh* current_mesh = volume->mesh_ptr();
std::map<const TriangleMesh*,int>::iterator mesh_iter;
mesh_iter = shared_meshes.find(current_mesh);
if (mesh_iter != shared_meshes.end()) {
stream << " <" << METADATA_TAG << " "<< KEY_ATTR << "=\"" << MESH_SHARED_KEY << "\" " << VALUE_ATTR << "=\"" << mesh_iter->second << "\"/>\n";
}
else {
shared_meshes[current_mesh] = it->second;
}
// stores mesh's statistics
const RepairedMeshErrors& stats = volume->mesh().stats().repaired_errors;
stream << " <" << MESH_STAT_TAG << " ";

View file

@ -166,6 +166,7 @@ const int IMPORT_STAGE_CHECK_MODE_GCODE = 9;
const int UPDATE_GCODE_RESULT = 10;
const int IMPORT_LOAD_CONFIG = 11;
const int IMPORT_LOAD_MODEL_OBJECTS = 12;
const int IMPORT_STAGE_MAX = 13;
//BBS export 3mf progress
typedef std::function<void(int export_stage, int current, int total, bool& cancel)> Export3mfProgressFn;

View file

@ -554,6 +554,9 @@ bool GCode::gcode_label_objects = false;
{
std::string gcode;
assert(m_layer_idx >= 0);
if (m_layer_idx >= (int) m_tool_changes.size()) return gcode;
// Calculate where the wipe tower layer will be printed. -1 means that print z will not change,
// resulting in a wipe tower with sparse layers.
double wipe_tower_z = -1;
@ -571,16 +574,12 @@ bool GCode::gcode_label_objects = false;
m_is_first_print = false;
}
assert(m_layer_idx >= 0);
if (gcodegen.writer().need_toolchange(extruder_id) || finish_layer) {
if (m_layer_idx < (int)m_tool_changes.size()) {
if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size()))
throw Slic3r::RuntimeError("Wipe tower generation failed, possibly due to empty first layer.");
if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size())) throw Slic3r::RuntimeError("Wipe tower generation failed, possibly due to empty first layer.");
if (!ignore_sparse) {
gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z);
m_last_wipe_tower_print_z = wipe_tower_z;
}
if (!ignore_sparse) {
gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z);
m_last_wipe_tower_print_z = wipe_tower_z;
}
}
@ -667,6 +666,7 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
--idx_tree_support_layer;
}
layer_to_print.original_object = &object;
layers_to_print.push_back(layer_to_print);
bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
@ -1261,6 +1261,7 @@ enum BambuBedType {
bbtCoolPlate = 1,
bbtEngineeringPlate = 2,
bbtHighTemperaturePlate = 3,
bbtTexturedPEIPlate = 4,
};
static BambuBedType to_bambu_bed_type(BedType type)
@ -1272,6 +1273,8 @@ static BambuBedType to_bambu_bed_type(BedType type)
bambu_bed_type = bbtEngineeringPlate;
else if (type == btPEI)
bambu_bed_type = bbtHighTemperaturePlate;
else if (type == btPTE)
bambu_bed_type = bbtTexturedPEIPlate;
return bambu_bed_type;
}
@ -1570,7 +1573,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
print.throw_if_canceled();
// Collect custom seam data from all objects.
m_seam_placer.init(print);
std::function<void(void)> throw_if_canceled_func = [&print]() { print.throw_if_canceled(); };
m_seam_placer.init(print, throw_if_canceled_func);
// BBS: priming logic is removed, always set first extruer here.
//if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming))
@ -2153,7 +2157,9 @@ std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
// Sequential print, single object is being printed.
for (ObjectByExtruder &object_by_extruder : objects_by_extruder) {
const size_t layer_id = &object_by_extruder - objects_by_extruder.data();
const PrintObject *print_object = layers[layer_id].object();
//BBS:add the support of shared print object
const PrintObject *print_object = layers[layer_id].original_object;
//const PrintObject *print_object = layers[layer_id].object();
if (print_object)
out.emplace_back(object_by_extruder, layer_id, *print_object, single_object_instance_idx);
}
@ -2163,7 +2169,9 @@ std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
sorted.reserve(objects_by_extruder.size());
for (ObjectByExtruder &object_by_extruder : objects_by_extruder) {
const size_t layer_id = &object_by_extruder - objects_by_extruder.data();
const PrintObject *print_object = layers[layer_id].object();
//BBS:add the support of shared print object
const PrintObject *print_object = layers[layer_id].original_object;
//const PrintObject *print_object = layers[layer_id].object();
if (print_object)
sorted.emplace_back(print_object, &object_by_extruder);
}
@ -2173,6 +2181,10 @@ std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
out.reserve(sorted.size());
for (const PrintInstance *instance : *ordering) {
const PrintObject &print_object = *instance->print_object;
//BBS:add the support of shared print object
//const PrintObject* print_obj_ptr = &print_object;
//if (print_object.get_shared_object())
// print_obj_ptr = print_object.get_shared_object();
std::pair<const PrintObject*, ObjectByExtruder*> key(&print_object, nullptr);
auto it = std::lower_bound(sorted.begin(), sorted.end(), key);
if (it != sorted.end() && it->first == &print_object)
@ -2795,7 +2807,6 @@ GCode::LayerResult GCode::process_layer(
m_wipe_tower->set_is_first_print(true);
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size());
for (unsigned int extruder_id : layer_tools.extruders)
{
gcode += (layer_tools.has_wipe_tower && m_wipe_tower) ?
@ -2910,12 +2921,7 @@ GCode::LayerResult GCode::process_layer(
m_layer = layers[instance_to_print.layer_id].tree_support_layer;
}
m_object_layer_over_raft = false;
// BBS. Keep paths order
#if 0
gcode += this->extrude_support(
// support_extrusion_role is erSupportMaterial, erSupportTransition, erSupportMaterialInterface or erMixed for all extrusion paths.
instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, instance_to_print.object_by_extruder.support_extrusion_role));
#else
//BBS: print supports' brims first
if (this->m_objSupportsWithBrim.find(instance_to_print.print_object.id()) != this->m_objSupportsWithBrim.end() && !print_wipe_extrusions) {
this->set_origin(0., 0.);
@ -2945,12 +2951,10 @@ GCode::LayerResult GCode::process_layer(
ExtrusionRole support_extrusion_role = instance_to_print.object_by_extruder.support_extrusion_role;
bool is_overridden = support_extrusion_role == erSupportMaterialInterface ? support_intf_overridden : support_overridden;
if (is_overridden == (print_wipe_extrusions != 0))
support_eec.entities = filter_by_extrusion_role(instance_to_print.object_by_extruder.support->entities, instance_to_print.object_by_extruder.support_extrusion_role);
gcode += this->extrude_support(
// support_extrusion_role is erSupportMaterial, erSupportTransition, erSupportMaterialInterface or erMixed for all extrusion paths.
instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, support_extrusion_role));
for (auto& ptr : support_eec.entities)
ptr = ptr->clone();
gcode += this->extrude_support(support_eec);
#endif
m_layer = layer_to_print.layer();
m_object_layer_over_raft = object_layer_over_raft;
}
@ -2984,9 +2988,9 @@ GCode::LayerResult GCode::process_layer(
//This behaviour is same with cura
if (is_infill_first && !first_layer) {
gcode += this->extrude_infill(print, by_region_specific, false);
gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]);
gcode += this->extrude_perimeters(print, by_region_specific);
} else {
gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[instance_to_print.layer_id]);
gcode += this->extrude_perimeters(print, by_region_specific);
gcode += this->extrude_infill(print,by_region_specific, false);
}
// ironing
@ -3164,14 +3168,11 @@ static std::unique_ptr<EdgeGrid::Grid> calculate_layer_edge_grid(const Layer& la
}
std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid)
std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed)
{
// get a copy; don't modify the orientation of the original loop object otherwise
// next copies (if any) would not detect the correct orientation
if (m_layer->lower_layer && lower_layer_edge_grid != nullptr && ! *lower_layer_edge_grid)
*lower_layer_edge_grid = calculate_layer_edge_grid(*m_layer->lower_layer);
//BBS: extrude contour of wall ccw, hole of wall cw, except spiral mode
bool was_clockwise = loop.is_clockwise();
if (m_config.spiral_mode || !is_perimeter(loop.role()))
@ -3181,17 +3182,13 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// find the point of the loop that is closest to the current extruder position
// or randomize if requested
Point last_pos = this->last_pos();
if (m_config.spiral_mode) {
if (!m_config.spiral_mode && description == "perimeter") {
assert(m_layer != nullptr);
bool is_outer_wall_first = m_config.wall_infill_order == WallInfillOrder::OuterInnerInfill
|| m_config.wall_infill_order == WallInfillOrder::InfillOuterInner;
m_seam_placer.place_seam(m_layer, loop, is_outer_wall_first, this->last_pos());
} else
loop.split_at(last_pos, false);
}
else {
//BBS
bool is_outer_wall_first =
m_config.wall_infill_order == WallInfillOrder::OuterInnerInfill ||
m_config.wall_infill_order == WallInfillOrder::InfillOuterInner;
m_seam_placer.place_seam(loop, this->last_pos(), is_outer_wall_first,
EXTRUDER_CONFIG(nozzle_diameter), lower_layer_edge_grid ? lower_layer_edge_grid->get() : nullptr);
}
// clip the path to avoid the extruder to get exactly on the first point of the loop;
// if polyline was shorter than the clipping distance we'd get a null polyline, so
@ -3301,14 +3298,14 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, std::string
return gcode;
}
std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string description, double speed, std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid)
std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string description, double speed)
{
if (const ExtrusionPath* path = dynamic_cast<const ExtrusionPath*>(&entity))
return this->extrude_path(*path, description, speed);
else if (const ExtrusionMultiPath* multipath = dynamic_cast<const ExtrusionMultiPath*>(&entity))
return this->extrude_multi_path(*multipath, description, speed);
else if (const ExtrusionLoop* loop = dynamic_cast<const ExtrusionLoop*>(&entity))
return this->extrude_loop(*loop, description, speed, lower_layer_edge_grid);
return this->extrude_loop(*loop, description, speed);
else
throw Slic3r::InvalidArgument("Invalid argument supplied to extrude()");
return "";
@ -3330,24 +3327,15 @@ std::string GCode::extrude_path(ExtrusionPath path, std::string description, dou
}
// Extrude perimeters: Decide where to put seams (hide or align seams).
std::string GCode::extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid)
std::string GCode::extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region)
{
std::string gcode;
for (const ObjectByExtruder::Island::Region &region : by_region)
if (! region.perimeters.empty()) {
m_config.apply(print.get_print_region(&region - &by_region.front()).config());
// plan_perimeters tries to place seams, it needs to have the lower_layer_edge_grid calculated already.
if (m_layer->lower_layer && ! lower_layer_edge_grid)
lower_layer_edge_grid = calculate_layer_edge_grid(*m_layer->lower_layer);
m_seam_placer.plan_perimeters(std::vector<const ExtrusionEntity*>(region.perimeters.begin(), region.perimeters.end()),
*m_layer, m_config.seam_position, this->last_pos(), EXTRUDER_CONFIG(nozzle_diameter),
(m_layer == NULL ? nullptr : m_layer->object()),
(lower_layer_edge_grid ? lower_layer_edge_grid.get() : nullptr));
for (const ExtrusionEntity* ee : region.perimeters)
gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid);
gcode += this->extrude_entity(*ee, "perimeter", -1.);
}
return gcode;
}
@ -3817,6 +3805,11 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
return false;
}
//BBS: force to retract when leave from external perimeter for a long travel
//Better way is judging whether the travel move direction is same with last extrusion move.
if (is_perimeter(m_last_processor_extrusion_role) && m_last_processor_extrusion_role != erPerimeter)
return true;
if (role == erSupportMaterial || role == erSupportTransition) {
const SupportLayer* support_layer = dynamic_cast<const SupportLayer*>(m_layer);
//FIXME support_layer->support_islands.contains should use some search structure!

View file

@ -41,11 +41,11 @@ class OozePrevention {
public:
bool enable;
Points standby_points;
OozePrevention() : enable(false) {}
std::string pre_toolchange(GCode &gcodegen);
std::string post_toolchange(GCode &gcodegen);
private:
int _get_temp(GCode &gcodegen);
};
@ -54,7 +54,7 @@ class Wipe {
public:
bool enable;
Polyline path;
Wipe() : enable(false) {}
bool has_path() const { return !this->path.points.empty(); }
void reset_path() { this->path = Polyline(); }
@ -136,15 +136,15 @@ public:
};
class GCode {
public:
public:
GCode() :
m_origin(Vec2d::Zero()),
m_enable_loop_clipping(true),
m_enable_cooling_markers(false),
m_enable_loop_clipping(true),
m_enable_cooling_markers(false),
m_enable_extrusion_role_markers(false),
m_last_processor_extrusion_role(erNone),
m_layer_count(0),
m_layer_index(-1),
m_layer_index(-1),
m_layer(nullptr),
m_object_layer_over_raft(false),
//m_volumetric_speed(0),
@ -201,10 +201,11 @@ public:
// public, so that it could be accessed by free helper functions from GCode.cpp
struct LayerToPrint
{
LayerToPrint() : object_layer(nullptr), support_layer(nullptr), tree_support_layer(nullptr) {}
LayerToPrint() : object_layer(nullptr), support_layer(nullptr), tree_support_layer(nullptr), original_object(nullptr) {}
const Layer* object_layer;
const SupportLayer* support_layer;
const TreeSupportLayer* tree_support_layer;
const PrintObject* original_object; //BBS: used for shared object logic
const Layer* layer() const
{
if (object_layer != nullptr)
@ -218,7 +219,11 @@ public:
return nullptr;
}
const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; }
const PrintObject* object() const
{
return (this->layer() != nullptr) ? this->layer()->object() : nullptr;
}
coordf_t print_z() const
{
coordf_t sum_z = 0.;
@ -249,7 +254,7 @@ private:
bool is_open() const { return f; }
bool is_error() const;
void flush();
void close();
@ -257,12 +262,12 @@ private:
void write(const std::string& what) { this->write(what.c_str()); }
void write(const char* what);
// Write a string into a file.
// Write a string into a file.
// Add a newline, if the string does not end with a newline already.
// Used to export a custom G-code section processed by the PlaceholderParser.
void writeln(const std::string& what);
// Formats and write into a file the given data.
// Formats and write into a file the given data.
void write_format(const char* format, ...);
private:
@ -325,8 +330,8 @@ private:
std::string preamble();
// BBS
std::string change_layer(coordf_t print_z, bool lazy_raise = false);
std::string extrude_entity(const ExtrusionEntity &entity, std::string description = "", double speed = -1., std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid = nullptr);
std::string extrude_loop(ExtrusionLoop loop, std::string description, double speed = -1., std::unique_ptr<EdgeGrid::Grid> *lower_layer_edge_grid = nullptr);
std::string extrude_entity(const ExtrusionEntity &entity, std::string description = "", double speed = -1.);
std::string extrude_loop(ExtrusionLoop loop, std::string description, double speed = -1.);
std::string extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.);
std::string extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.);
@ -375,7 +380,7 @@ private:
InstanceToPrint(ObjectByExtruder &object_by_extruder, size_t layer_id, const PrintObject &print_object, size_t instance_id) :
object_by_extruder(object_by_extruder), layer_id(layer_id), print_object(print_object), instance_id(instance_id) {}
// Repository
// Repository
ObjectByExtruder &object_by_extruder;
// Index into std::vector<LayerToPrint>, which contains Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances.
const size_t layer_id;
@ -393,7 +398,7 @@ private:
// For sequential print, the instance of the object to be printing has to be defined.
const size_t single_object_instance_idx);
std::string extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid);
std::string extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region);
std::string extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, bool ironing);
std::string extrude_support(const ExtrusionEntityCollection &support_fills);
@ -500,14 +505,14 @@ private:
bool object_layer_over_raft() const { return m_object_layer_over_raft; }
friend ObjectByExtruder& object_by_extruder(
std::map<unsigned int, std::vector<ObjectByExtruder>> &by_extruder,
unsigned int extruder_id,
size_t object_idx,
std::map<unsigned int, std::vector<ObjectByExtruder>> &by_extruder,
unsigned int extruder_id,
size_t object_idx,
size_t num_objects);
friend std::vector<ObjectByExtruder::Island>& object_islands_by_extruder(
std::map<unsigned int, std::vector<ObjectByExtruder>> &by_extruder,
unsigned int extruder_id,
size_t object_idx,
std::map<unsigned int, std::vector<ObjectByExtruder>> &by_extruder,
unsigned int extruder_id,
size_t object_idx,
size_t num_objects,
size_t num_islands);

File diff suppressed because it is too large Load diff

View file

@ -3,12 +3,16 @@
#include <optional>
#include <vector>
#include <memory>
#include <atomic>
#include "libslic3r/libslic3r.h"
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/Polygon.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/AABBTreeIndirect.hpp"
#include "libslic3r/KDTreeIndirect.hpp"
namespace Slic3r {
@ -16,119 +20,147 @@ class PrintObject;
class ExtrusionLoop;
class Print;
class Layer;
namespace EdgeGrid { class Grid; }
class SeamHistory {
public:
SeamHistory() { clear(); }
std::optional<Point> get_last_seam(const PrintObject* po, size_t layer_id, const BoundingBox& island_bb);
void add_seam(const PrintObject* po, const Point& pos, const BoundingBox& island_bb);
void clear();
private:
struct SeamPoint {
Point m_pos;
BoundingBox m_island_bb;
};
std::map<const PrintObject*, std::vector<SeamPoint>> m_data_last_layer;
std::map<const PrintObject*, std::vector<SeamPoint>> m_data_this_layer;
size_t m_layer_id;
};
class SeamPlacer {
public:
void init(const Print& print);
// When perimeters are printed, first call this function with the respective
// external perimeter. SeamPlacer will find a location for its seam and remember it.
// Subsequent calls to get_seam will return this position.
void plan_perimeters(const std::vector<const ExtrusionEntity*> perimeters,
const Layer& layer, SeamPosition seam_position,
Point last_pos, coordf_t nozzle_dmr, const PrintObject* po,
const EdgeGrid::Grid* lower_layer_edge_grid);
void place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter,
const EdgeGrid::Grid* lower_layer_edge_grid);
using TreeType = AABBTreeIndirect::Tree<2, coord_t>;
using AlignedBoxType = Eigen::AlignedBox<TreeType::CoordType, TreeType::NumDimensions>;
private:
// When given an external perimeter (!), returns the seam.
Point calculate_seam(const Layer& layer, const SeamPosition seam_position,
const ExtrusionLoop& loop, coordf_t nozzle_dmr, const PrintObject* po,
const EdgeGrid::Grid* lower_layer_edge_grid, Point last_pos);
struct CustomTrianglesPerLayer {
Polygons polys;
TreeType tree;
};
// Just a cache to save some lookups.
const Layer* m_last_layer_po = nullptr;
coordf_t m_last_print_z = -1.;
const PrintObject* m_last_po = nullptr;
struct SeamPoint {
Point pt;
bool precalculated = false;
bool external = false;
const Layer* layer = nullptr;
SeamPosition seam_position;
const PrintObject* po = nullptr;
};
std::vector<SeamPoint> m_plan;
size_t m_plan_idx;
std::vector<std::vector<CustomTrianglesPerLayer>> m_enforcers;
std::vector<std::vector<CustomTrianglesPerLayer>> m_blockers;
std::vector<const PrintObject*> m_po_list;
//std::map<const PrintObject*, Point> m_last_seam_position;
SeamHistory m_seam_history;
// Get indices of points inside enforcers and blockers.
void get_enforcers_and_blockers(size_t layer_id,
const Polygon& polygon,
size_t po_id,
std::vector<size_t>& enforcers_idxs,
std::vector<size_t>& blockers_idxs) const;
// Apply penalties to points inside enforcers/blockers.
void apply_custom_seam(const Polygon& polygon, size_t po_id,
std::vector<float>& penalties,
const std::vector<float>& lengths,
int layer_id, SeamPosition seam_position) const;
// Return random point of a polygon. The distribution will be uniform
// along the contour and account for enforcers and blockers.
Point get_random_seam(size_t layer_idx, const Polygon& polygon, size_t po_id,
bool* saw_custom = nullptr) const;
// Is there any enforcer/blocker on this layer?
bool is_custom_seam_on_layer(size_t layer_id, size_t po_idx) const {
return is_custom_enforcer_on_layer(layer_id, po_idx)
|| is_custom_blocker_on_layer(layer_id, po_idx);
}
bool is_custom_enforcer_on_layer(size_t layer_id, size_t po_idx) const {
return (! m_enforcers.at(po_idx).empty() && ! m_enforcers.at(po_idx)[layer_id].polys.empty());
}
bool is_custom_blocker_on_layer(size_t layer_id, size_t po_idx) const {
return (! m_blockers.at(po_idx).empty() && ! m_blockers.at(po_idx)[layer_id].polys.empty());
}
};
namespace EdgeGrid {
class Grid;
}
namespace SeamPlacerImpl {
// ************ FOR BACKPORT COMPATIBILITY ONLY ***************
// Angle from v1 to v2, returning double atan2(y, x) normalized to <-PI, PI>.
template<typename Derived, typename Derived2> inline double angle(const Eigen::MatrixBase<Derived> &v1, const Eigen::MatrixBase<Derived2> &v2)
{
static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "angle(): first parameter is not a 2D vector");
static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "angle(): second parameter is not a 2D vector");
auto v1d = v1.template cast<double>();
auto v2d = v2.template cast<double>();
return atan2(cross2(v1d, v2d), v1d.dot(v2d));
}
// ***************************
struct GlobalModelInfo;
struct SeamComparator;
enum class EnforcedBlockedSeamPoint {
Blocked = 0,
Neutral = 1,
Enforced = 2,
};
// struct representing single perimeter loop
struct Perimeter
{
size_t start_index{};
size_t end_index{}; // inclusive!
size_t seam_index{};
float flow_width{};
// During alignment, a final position may be stored here. In that case, finalized is set to true.
// Note that final seam position is not limited to points of the perimeter loop. In theory it can be any position
// Random position also uses this flexibility to set final seam point position
bool finalized = false;
Vec3f final_seam_position = Vec3f::Zero();
};
// Struct over which all processing of perimeters is done. For each perimeter point, its respective candidate is created,
// then all the needed attributes are computed and finally, for each perimeter one point is chosen as seam.
// This seam position can be then further aligned
struct SeamCandidate
{
SeamCandidate(const Vec3f &pos, Perimeter &perimeter, float local_ccw_angle, EnforcedBlockedSeamPoint type)
: position(pos), perimeter(perimeter), visibility(0.0f), overhang(0.0f), embedded_distance(0.0f), local_ccw_angle(local_ccw_angle), type(type), central_enforcer(false)
{}
const Vec3f position;
// pointer to Perimeter loop of this point. It is shared across all points of the loop
Perimeter &perimeter;
float visibility;
float overhang;
// distance inside the merged layer regions, for detecting perimeter points which are hidden indside the print (e.g. multimaterial join)
// Negative sign means inside the print, comes from EdgeGrid structure
float embedded_distance;
float local_ccw_angle;
EnforcedBlockedSeamPoint type;
bool central_enforcer; // marks this candidate as central point of enforced segment on the perimeter - important for alignment
};
struct SeamCandidateCoordinateFunctor
{
SeamCandidateCoordinateFunctor(const std::vector<SeamCandidate> &seam_candidates) : seam_candidates(seam_candidates) {}
const std::vector<SeamCandidate> &seam_candidates;
float operator()(size_t index, size_t dim) const { return seam_candidates[index].position[dim]; }
};
} // namespace SeamPlacerImpl
struct PrintObjectSeamData
{
using SeamCandidatesTree = KDTreeIndirect<3, float, SeamPlacerImpl::SeamCandidateCoordinateFunctor>;
struct LayerSeams
{
Slic3r::deque<SeamPlacerImpl::Perimeter> perimeters;
std::vector<SeamPlacerImpl::SeamCandidate> points;
std::unique_ptr<SeamCandidatesTree> points_tree;
};
// Map of PrintObjects (PO) -> vector of layers of PO -> vector of perimeter
std::vector<LayerSeams> layers;
// Map of PrintObjects (PO) -> vector of layers of PO -> unique_ptr to KD
// tree of all points of the given layer
void clear() { layers.clear(); }
};
class SeamPlacer
{
public:
// Number of samples generated on the mesh. There are sqr_rays_per_sample_point*sqr_rays_per_sample_point rays casted from each samples
static constexpr size_t raycasting_visibility_samples_count = 30000;
// square of number of rays per sample point
static constexpr size_t sqr_rays_per_sample_point = 5;
// snapping angle - angles larger than this value will be snapped to during seam painting
static constexpr float sharp_angle_snapping_threshold = 55.0f * float(PI) / 180.0f;
// overhang angle for seam placement that still yields good results, in degrees, measured from vertical direction
//BBS
static constexpr float overhang_angle_threshold = 45.0f * float(PI) / 180.0f;
// determines angle importance compared to visibility ( neutral value is 1.0f. )
static constexpr float angle_importance_aligned = 0.6f;
static constexpr float angle_importance_nearest = 1.0f; // use much higher angle importance for nearest mode, to combat the visibility info noise
// For long polygon sides, if they are close to the custom seam drawings, they are oversampled with this step size
static constexpr float enforcer_oversampling_distance = 0.2f;
// When searching for seam clusters for alignment:
// following value describes, how much worse score can point have and still be picked into seam cluster instead of original seam point on the same layer
static constexpr float seam_align_score_tolerance = 0.3f;
// seam_align_tolerable_dist_factor - how far to search for seam from current position, final dist is seam_align_tolerable_dist_factor * flow_width
static constexpr float seam_align_tolerable_dist_factor = 4.0f;
// minimum number of seams needed in cluster to make alignment happen
static constexpr size_t seam_align_minimum_string_seams = 6;
// millimeters covered by spline; determines number of splines for the given string
static constexpr size_t seam_align_mm_per_segment = 4.0f;
// The following data structures hold all perimeter points for all PrintObject.
std::unordered_map<const PrintObject *, PrintObjectSeamData> m_seam_per_object;
void init(const Print &print, std::function<void(void)> throw_if_canceled_func);
void place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first, const Point &last_pos) const;
private:
void gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info, const SeamPosition configured_seam_preference);
void calculate_candidates_visibility(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info);
void calculate_overhangs_and_layer_embedding(const PrintObject *po);
void align_seam_points(const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator);
std::vector<std::pair<size_t, size_t>> find_seam_string(const PrintObject *po, std::pair<size_t, size_t> start_seam, const SeamPlacerImpl::SeamComparator &comparator) const;
std::optional<std::pair<size_t, size_t>> find_next_seam_in_layer(const std::vector<PrintObjectSeamData::LayerSeams> &layers,
const Vec3f & projected_position,
const size_t layer_idx,
const float max_distance,
const SeamPlacerImpl::SeamComparator & comparator) const;
};
} // namespace Slic3r
#endif // libslic3r_SeamPlacer_hpp_

View file

@ -474,7 +474,8 @@ void ToolOrdering::reorder_extruders(std::vector<unsigned int> tool_order_layer0
if (m_layer_tools.empty())
return;
assert(!tool_order_layer0.empty());
if (tool_order_layer0.empty())
return;
// Reorder the extruders of first layer
{

View file

@ -0,0 +1,291 @@
#ifndef BICUBIC_HPP
#define BICUBIC_HPP
#include <algorithm>
#include <vector>
#include <cmath>
#include <Eigen/Dense>
namespace Slic3r {
namespace Geometry {
namespace BicubicInternal {
// Linear kernel, to be able to test cubic methods with hat kernels.
template<typename T>
struct LinearKernel
{
typedef T FloatType;
static T a00() {
return T(0.);
}
static T a01() {
return T(0.);
}
static T a02() {
return T(0.);
}
static T a03() {
return T(0.);
}
static T a10() {
return T(1.);
}
static T a11() {
return T(-1.);
}
static T a12() {
return T(0.);
}
static T a13() {
return T(0.);
}
static T a20() {
return T(0.);
}
static T a21() {
return T(1.);
}
static T a22() {
return T(0.);
}
static T a23() {
return T(0.);
}
static T a30() {
return T(0.);
}
static T a31() {
return T(0.);
}
static T a32() {
return T(0.);
}
static T a33() {
return T(0.);
}
};
// Interpolation kernel aka Catmul-Rom aka Keyes kernel.
template<typename T>
struct CubicCatmulRomKernel
{
typedef T FloatType;
static T a00() {
return 0;
}
static T a01() {
return T( -0.5);
}
static T a02() {
return T( 1.);
}
static T a03() {
return T( -0.5);
}
static T a10() {
return T( 1.);
}
static T a11() {
return 0;
}
static T a12() {
return T( -5. / 2.);
}
static T a13() {
return T( 3. / 2.);
}
static T a20() {
return 0;
}
static T a21() {
return T( 0.5);
}
static T a22() {
return T( 2.);
}
static T a23() {
return T( -3. / 2.);
}
static T a30() {
return 0;
}
static T a31() {
return 0;
}
static T a32() {
return T( -0.5);
}
static T a33() {
return T( 0.5);
}
};
// B-spline kernel
template<typename T>
struct CubicBSplineKernel
{
typedef T FloatType;
static T a00() {
return T( 1. / 6.);
}
static T a01() {
return T( -3. / 6.);
}
static T a02() {
return T( 3. / 6.);
}
static T a03() {
return T( -1. / 6.);
}
static T a10() {
return T( 4. / 6.);
}
static T a11() {
return 0;
}
static T a12() {
return T( -6. / 6.);
}
static T a13() {
return T( 3. / 6.);
}
static T a20() {
return T( 1. / 6.);
}
static T a21() {
return T( 3. / 6.);
}
static T a22() {
return T( 3. / 6.);
}
static T a23() {
return T( -3. / 6.);
}
static T a30() {
return 0;
}
static T a31() {
return 0;
}
static T a32() {
return 0;
}
static T a33() {
return T( 1. / 6.);
}
};
template<class T>
inline T clamp(T a, T lower, T upper)
{
return (a < lower) ? lower :
(a > upper) ? upper : a;
}
}
template<typename Kernel>
struct CubicKernelWrapper
{
typedef typename Kernel::FloatType FloatType;
static constexpr size_t kernel_span = 4;
static FloatType kernel(FloatType x)
{
x = fabs(x);
if (x >= (FloatType) 2.)
return 0.0f;
if (x <= (FloatType) 1.) {
FloatType x2 = x * x;
FloatType x3 = x2 * x;
return Kernel::a10() + Kernel::a11() * x + Kernel::a12() * x2 + Kernel::a13() * x3;
}
assert(x > (FloatType )1. && x < (FloatType )2.);
x -= (FloatType) 1.;
FloatType x2 = x * x;
FloatType x3 = x2 * x;
return Kernel::a00() + Kernel::a01() * x + Kernel::a02() * x2 + Kernel::a03() * x3;
}
static FloatType interpolate(FloatType f0, FloatType f1, FloatType f2, FloatType f3, FloatType x)
{
const FloatType x2 = x * x;
const FloatType x3 = x * x * x;
return f0 * (Kernel::a00() + Kernel::a01() * x + Kernel::a02() * x2 + Kernel::a03() * x3) +
f1 * (Kernel::a10() + Kernel::a11() * x + Kernel::a12() * x2 + Kernel::a13() * x3) +
f2 * (Kernel::a20() + Kernel::a21() * x + Kernel::a22() * x2 + Kernel::a23() * x3) +
f3 * (Kernel::a30() + Kernel::a31() * x + Kernel::a32() * x2 + Kernel::a33() * x3);
}
};
// Linear splines
template<typename NumberType>
using LinearKernel = CubicKernelWrapper<BicubicInternal::LinearKernel<NumberType>>;
// Catmul-Rom splines
template<typename NumberType>
using CubicCatmulRomKernel = CubicKernelWrapper<BicubicInternal::CubicCatmulRomKernel<NumberType>>;
// Cubic B-splines
template<typename NumberType>
using CubicBSplineKernel = CubicKernelWrapper<BicubicInternal::CubicBSplineKernel<NumberType>>;
template<typename KernelWrapper>
static typename KernelWrapper::FloatType cubic_interpolate(const Eigen::ArrayBase<typename KernelWrapper::FloatType> &F,
const typename KernelWrapper::FloatType pt) {
typedef typename KernelWrapper::FloatType T;
const int w = int(F.size());
const int ix = (int) floor(pt);
const T s = pt - T( ix);
if (ix > 1 && ix + 2 < w) {
// Inside the fully interpolated region.
return KernelWrapper::interpolate(F[ix - 1], F[ix], F[ix + 1], F[ix + 2], s);
}
// Transition region. Extend with a constant function.
auto f = [&F, w](T x) {
return F[BicubicInternal::clamp(x, 0, w - 1)];
};
return KernelWrapper::interpolate(f(ix - 1), f(ix), f(ix + 1), f(ix + 2), s);
}
template<typename Kernel, typename Derived>
static float bicubic_interpolate(const Eigen::MatrixBase<Derived> &F,
const Eigen::Matrix<typename Kernel::FloatType, 2, 1, Eigen::DontAlign> &pt) {
typedef typename Kernel::FloatType T;
const int w = F.cols();
const int h = F.rows();
const int ix = (int) floor(pt[0]);
const int iy = (int) floor(pt[1]);
const T s = pt[0] - T( ix);
const T t = pt[1] - T( iy);
if (ix > 1 && ix + 2 < w && iy > 1 && iy + 2 < h) {
// Inside the fully interpolated region.
return Kernel::interpolate(
Kernel::interpolate(F(ix - 1, iy - 1), F(ix, iy - 1), F(ix + 1, iy - 1), F(ix + 2, iy - 1), s),
Kernel::interpolate(F(ix - 1, iy), F(ix, iy), F(ix + 1, iy), F(ix + 2, iy), s),
Kernel::interpolate(F(ix - 1, iy + 1), F(ix, iy + 1), F(ix + 1, iy + 1), F(ix + 2, iy + 1), s),
Kernel::interpolate(F(ix - 1, iy + 2), F(ix, iy + 2), F(ix + 1, iy + 2), F(ix + 2, iy + 2), s), t);
}
// Transition region. Extend with a constant function.
auto f = [&F, w, h](int x, int y) {
return F(BicubicInternal::clamp(x, 0, w - 1), BicubicInternal::clamp(y, 0, h - 1));
};
return Kernel::interpolate(
Kernel::interpolate(f(ix - 1, iy - 1), f(ix, iy - 1), f(ix + 1, iy - 1), f(ix + 2, iy - 1), s),
Kernel::interpolate(f(ix - 1, iy), f(ix, iy), f(ix + 1, iy), f(ix + 2, iy), s),
Kernel::interpolate(f(ix - 1, iy + 1), f(ix, iy + 1), f(ix + 1, iy + 1), f(ix + 2, iy + 1), s),
Kernel::interpolate(f(ix - 1, iy + 2), f(ix, iy + 2), f(ix + 1, iy + 2), f(ix + 2, iy + 2), s), t);
}
} //namespace Geometry
} // namespace Slic3r
#endif /* BICUBIC_HPP */

View file

@ -0,0 +1,218 @@
#ifndef SRC_LIBSLIC3R_GEOMETRY_CURVES_HPP_
#define SRC_LIBSLIC3R_GEOMETRY_CURVES_HPP_
#include "libslic3r/Point.hpp"
#include "Bicubic.hpp"
#include <iostream>
//#define LSQR_DEBUG
namespace Slic3r {
namespace Geometry {
template<int Dimension, typename NumberType>
struct PolynomialCurve {
Eigen::MatrixXf coefficients;
Vec<Dimension, NumberType> get_fitted_value(const NumberType& value) const {
Vec<Dimension, NumberType> result = Vec<Dimension, NumberType>::Zero();
size_t order = this->coefficients.rows() - 1;
auto x = NumberType(1.);
for (size_t index = 0; index < order + 1; ++index, x *= value)
result += x * this->coefficients.col(index);
return result;
}
};
//https://towardsdatascience.com/least-square-polynomial-CURVES-using-c-eigen-package-c0673728bd01
template<int Dimension, typename NumberType>
PolynomialCurve<Dimension, NumberType> fit_polynomial(const std::vector<Vec<Dimension, NumberType>> &observations,
const std::vector<NumberType> &observation_points,
const std::vector<NumberType> &weights, size_t order) {
// check to make sure inputs are correct
size_t cols = order + 1;
assert(observation_points.size() >= cols);
assert(observation_points.size() == weights.size());
assert(observations.size() == weights.size());
Eigen::MatrixXf data_points(Dimension, observations.size());
Eigen::MatrixXf T(observations.size(), cols);
for (size_t i = 0; i < weights.size(); ++i) {
auto squared_weight = sqrt(weights[i]);
data_points.col(i) = observations[i] * squared_weight;
// Populate the matrix
auto x = squared_weight;
auto c = observation_points[i];
for (size_t j = 0; j < cols; ++j, x *= c)
T(i, j) = x;
}
const auto QR = T.householderQr();
Eigen::MatrixXf coefficients(Dimension, cols);
// Solve for linear least square fit
for (size_t dim = 0; dim < Dimension; ++dim) {
coefficients.row(dim) = QR.solve(data_points.row(dim).transpose());
}
return {std::move(coefficients)};
}
template<size_t Dimension, typename NumberType, typename KernelType>
struct PiecewiseFittedCurve {
using Kernel = KernelType;
Eigen::MatrixXf coefficients;
NumberType start;
NumberType segment_size;
size_t endpoints_level_of_freedom;
Vec<Dimension, NumberType> get_fitted_value(const NumberType &observation_point) const {
Vec<Dimension, NumberType> result = Vec<Dimension, NumberType>::Zero();
//find corresponding segment index; expects kernels to be centered
int middle_right_segment_index = floor((observation_point - start) / segment_size);
//find index of first segment that is affected by the point i; this can be deduced from kernel_span
int start_segment_idx = middle_right_segment_index - Kernel::kernel_span / 2 + 1;
for (int segment_index = start_segment_idx; segment_index < int(start_segment_idx + Kernel::kernel_span);
segment_index++) {
NumberType segment_start = start + segment_index * segment_size;
NumberType normalized_segment_distance = (segment_start - observation_point) / segment_size;
int parameter_index = segment_index + endpoints_level_of_freedom;
parameter_index = std::clamp(parameter_index, 0, int(coefficients.cols()) - 1);
result += Kernel::kernel(normalized_segment_distance) * coefficients.col(parameter_index);
}
return result;
}
};
// observations: data to be fitted by the curve
// observation points: growing sequence of points where the observations were made.
// In other words, for function f(x) = y, observations are y0...yn, and observation points are x0...xn
// weights: how important the observation is
// segments_count: number of segments inside the valid length of the curve
// endpoints_level_of_freedom: number of additional parameters at each end; reasonable values depend on the kernel span
template<typename Kernel, int Dimension, typename NumberType>
PiecewiseFittedCurve<Dimension, NumberType, Kernel> fit_curve(
const std::vector<Vec<Dimension, NumberType>> &observations,
const std::vector<NumberType> &observation_points,
const std::vector<NumberType> &weights,
size_t segments_count,
size_t endpoints_level_of_freedom) {
// check to make sure inputs are correct
assert(segments_count > 0);
assert(observations.size() > 0);
assert(observation_points.size() == observations.size());
assert(observation_points.size() == weights.size());
assert(segments_count <= observations.size());
//prepare sqrt of weights, which will then be applied to both matrix T and observed data: https://en.wikipedia.org/wiki/Weighted_least_squares
std::vector<NumberType> sqrt_weights(weights.size());
for (size_t index = 0; index < weights.size(); ++index) {
assert(weights[index] > 0);
sqrt_weights[index] = sqrt(weights[index]);
}
// prepare result and compute metadata
PiecewiseFittedCurve<Dimension, NumberType, Kernel> result { };
NumberType valid_length = observation_points.back() - observation_points.front();
NumberType segment_size = valid_length / NumberType(segments_count);
result.start = observation_points.front();
result.segment_size = segment_size;
result.endpoints_level_of_freedom = endpoints_level_of_freedom;
// prepare observed data
// Eigen defaults to column major memory layout.
Eigen::MatrixXf data_points(Dimension, observations.size());
for (size_t index = 0; index < observations.size(); ++index) {
data_points.col(index) = observations[index] * sqrt_weights[index];
}
// parameters count is always increased by one to make the parametric space of the curve symmetric.
// without this fix, the end of the curve is less flexible than the beginning
size_t parameters_count = segments_count + 1 + 2 * endpoints_level_of_freedom;
//Create weight matrix T for each point and each segment;
Eigen::MatrixXf T(observation_points.size(), parameters_count);
T.setZero();
//Fill the weight matrix
for (size_t i = 0; i < observation_points.size(); ++i) {
NumberType observation_point = observation_points[i];
//find corresponding segment index; expects kernels to be centered
int middle_right_segment_index = floor((observation_point - result.start) / result.segment_size);
//find index of first segment that is affected by the point i; this can be deduced from kernel_span
int start_segment_idx = middle_right_segment_index - int(Kernel::kernel_span / 2) + 1;
for (int segment_index = start_segment_idx; segment_index < int(start_segment_idx + Kernel::kernel_span);
segment_index++) {
NumberType segment_start = result.start + segment_index * result.segment_size;
NumberType normalized_segment_distance = (segment_start - observation_point) / result.segment_size;
int parameter_index = segment_index + endpoints_level_of_freedom;
parameter_index = std::clamp(parameter_index, 0, int(parameters_count) - 1);
T(i, parameter_index) += Kernel::kernel(normalized_segment_distance) * sqrt_weights[i];
}
}
#ifdef LSQR_DEBUG
std::cout << "weight matrix: " << std::endl;
for (int obs = 0; obs < observation_points.size(); ++obs) {
std::cout << std::endl;
for (int segment = 0; segment < parameters_count; ++segment) {
std::cout << T(obs, segment) << " ";
}
}
std::cout << std::endl;
#endif
// Solve for linear least square fit
result.coefficients.resize(Dimension, parameters_count);
const auto QR = T.fullPivHouseholderQr();
for (size_t dim = 0; dim < Dimension; ++dim) {
result.coefficients.row(dim) = QR.solve(data_points.row(dim).transpose());
}
return result;
}
template<int Dimension, typename NumberType>
PiecewiseFittedCurve<Dimension, NumberType, LinearKernel<NumberType>>
fit_linear_spline(
const std::vector<Vec<Dimension, NumberType>> &observations,
std::vector<NumberType> observation_points,
std::vector<NumberType> weights,
size_t segments_count,
size_t endpoints_level_of_freedom = 0) {
return fit_curve<LinearKernel<NumberType>>(observations, observation_points, weights, segments_count,
endpoints_level_of_freedom);
}
template<int Dimension, typename NumberType>
PiecewiseFittedCurve<Dimension, NumberType, CubicBSplineKernel<NumberType>>
fit_cubic_bspline(
const std::vector<Vec<Dimension, NumberType>> &observations,
std::vector<NumberType> observation_points,
std::vector<NumberType> weights,
size_t segments_count,
size_t endpoints_level_of_freedom = 0) {
return fit_curve<CubicBSplineKernel<NumberType>>(observations, observation_points, weights, segments_count,
endpoints_level_of_freedom);
}
template<int Dimension, typename NumberType>
PiecewiseFittedCurve<Dimension, NumberType, CubicCatmulRomKernel<NumberType>>
fit_catmul_rom_spline(
const std::vector<Vec<Dimension, NumberType>> &observations,
std::vector<NumberType> observation_points,
std::vector<NumberType> weights,
size_t segments_count,
size_t endpoints_level_of_freedom = 0) {
return fit_curve<CubicCatmulRomKernel<NumberType>>(observations, observation_points, weights, segments_count,
endpoints_level_of_freedom);
}
}
}
#endif /* SRC_LIBSLIC3R_GEOMETRY_CURVES_HPP_ */

View file

@ -11,224 +11,362 @@
namespace Slic3r {
enum class VisitorReturnMask : unsigned int {
CONTINUE_LEFT = 1,
CONTINUE_RIGHT = 2,
STOP = 4,
};
// KD tree for N-dimensional closest point search.
template<size_t ANumDimensions, typename ACoordType, typename ACoordinateFn>
class KDTreeIndirect
{
public:
static constexpr size_t NumDimensions = ANumDimensions;
using CoordinateFn = ACoordinateFn;
using CoordType = ACoordType;
static constexpr size_t NumDimensions = ANumDimensions;
using CoordinateFn = ACoordinateFn;
using CoordType = ACoordType;
// Following could be static constexpr size_t, but that would not link in C++11
enum : size_t {
npos = size_t(-1)
};
KDTreeIndirect(CoordinateFn coordinate) : coordinate(coordinate) {}
KDTreeIndirect(CoordinateFn coordinate, std::vector<size_t> indices) : coordinate(coordinate) { this->build(std::move(indices)); }
KDTreeIndirect(CoordinateFn coordinate, std::vector<size_t> &&indices) : coordinate(coordinate) { this->build(std::move(indices)); }
KDTreeIndirect(CoordinateFn coordinate, size_t num_indices) : coordinate(coordinate) { this->build(num_indices); }
KDTreeIndirect(KDTreeIndirect &&rhs) : m_nodes(std::move(rhs.m_nodes)), coordinate(std::move(rhs.coordinate)) {}
KDTreeIndirect& operator=(KDTreeIndirect &&rhs) { m_nodes = std::move(rhs.m_nodes); coordinate = std::move(rhs.coordinate); return *this; }
void clear() { m_nodes.clear(); }
KDTreeIndirect(CoordinateFn coordinate) : coordinate(coordinate) {}
KDTreeIndirect(CoordinateFn coordinate, std::vector<size_t> indices) : coordinate(coordinate) { this->build(indices); }
KDTreeIndirect(CoordinateFn coordinate, size_t num_indices) : coordinate(coordinate) { this->build(num_indices); }
KDTreeIndirect(KDTreeIndirect &&rhs) : m_nodes(std::move(rhs.m_nodes)), coordinate(std::move(rhs.coordinate)) {}
KDTreeIndirect& operator=(KDTreeIndirect &&rhs) { m_nodes = std::move(rhs.m_nodes); coordinate = std::move(rhs.coordinate); return *this; }
void clear() { m_nodes.clear(); }
void build(size_t num_indices)
{
std::vector<size_t> indices;
indices.reserve(num_indices);
for (size_t i = 0; i < num_indices; ++ i)
indices.emplace_back(i);
this->build(std::move(indices));
}
void build(size_t num_indices)
{
std::vector<size_t> indices;
indices.reserve(num_indices);
for (size_t i = 0; i < num_indices; ++ i)
indices.emplace_back(i);
this->build(indices);
}
void build(std::vector<size_t> &&indices)
{
if (indices.empty())
clear();
else {
// Allocate enough memory for a full binary tree.
m_nodes.assign(next_highest_power_of_2(indices.size() + 1), npos);
build_recursive(indices, 0, 0, 0, indices.size() - 1);
}
indices.clear();
}
void build(std::vector<size_t> &indices)
{
if (indices.empty())
clear();
else {
// Allocate enough memory for a full binary tree.
m_nodes.assign(next_highest_power_of_2(indices.size() + 1), npos);
build_recursive(indices, 0, 0, 0, indices.size() - 1);
}
indices.clear();
}
enum class VisitorReturnMask : unsigned int
{
CONTINUE_LEFT = 1,
CONTINUE_RIGHT = 2,
STOP = 4,
};
template<typename CoordType>
unsigned int descent_mask(const CoordType &point_coord, const CoordType &search_radius, size_t idx, size_t dimension) const
{
CoordType dist = point_coord - this->coordinate(idx, dimension);
return (dist * dist < search_radius + CoordType(EPSILON)) ?
// The plane intersects a hypersphere centered at point_coord of search_radius.
((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) :
// The plane does not intersect the hypersphere.
(dist > CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT);
}
template<typename CoordType>
unsigned int descent_mask(const CoordType &point_coord, const CoordType &search_radius, size_t idx, size_t dimension) const
{
CoordType dist = point_coord - this->coordinate(idx, dimension);
return (dist * dist < search_radius + CoordType(EPSILON)) ?
// The plane intersects a hypersphere centered at point_coord of search_radius.
((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) :
// The plane does not intersect the hypersphere.
(dist > CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT);
}
// Visitor is supposed to return a bit mask of VisitorReturnMask.
template<typename Visitor>
void visit(Visitor &visitor) const
{
// Visitor is supposed to return a bit mask of VisitorReturnMask.
template<typename Visitor>
void visit(Visitor &visitor) const
{
visit_recursive(0, 0, visitor);
}
}
CoordinateFn coordinate;
CoordinateFn coordinate;
private:
// Build a balanced tree by splitting the input sequence by an axis aligned plane at a dimension.
void build_recursive(std::vector<size_t> &input, size_t node, const size_t dimension, const size_t left, const size_t right)
{
if (left > right)
return;
// Build a balanced tree by splitting the input sequence by an axis aligned plane at a dimension.
void build_recursive(std::vector<size_t> &input, size_t node, const size_t dimension, const size_t left, const size_t right)
{
if (left > right)
return;
assert(node < m_nodes.size());
assert(node < m_nodes.size());
if (left == right) {
// Insert a node into the balanced tree.
m_nodes[node] = input[left];
return;
}
if (left == right) {
// Insert a node into the balanced tree.
m_nodes[node] = input[left];
return;
}
// Partition the input to left / right pieces of the same length to produce a balanced tree.
size_t center = (left + right) / 2;
partition_input(input, dimension, left, right, center);
// Insert a node into the tree.
m_nodes[node] = input[center];
// Build up the left / right subtrees.
size_t next_dimension = dimension;
if (++ next_dimension == NumDimensions)
next_dimension = 0;
if (center > left)
build_recursive(input, node * 2 + 1, next_dimension, left, center - 1);
build_recursive(input, node * 2 + 2, next_dimension, center + 1, right);
}
// Partition the input to left / right pieces of the same length to produce a balanced tree.
size_t center = (left + right) / 2;
partition_input(input, dimension, left, right, center);
// Insert a node into the tree.
m_nodes[node] = input[center];
// Build up the left / right subtrees.
size_t next_dimension = dimension;
if (++ next_dimension == NumDimensions)
next_dimension = 0;
if (center > left)
build_recursive(input, node * 2 + 1, next_dimension, left, center - 1);
build_recursive(input, node * 2 + 2, next_dimension, center + 1, right);
}
// Partition the input m_nodes <left, right> at "k" and "dimension" using the QuickSelect method:
// https://en.wikipedia.org/wiki/Quickselect
// Items left of the k'th item are lower than the k'th item in the "dimension",
// items right of the k'th item are higher than the k'th item in the "dimension",
void partition_input(std::vector<size_t> &input, const size_t dimension, size_t left, size_t right, const size_t k) const
{
while (left < right) {
size_t center = (left + right) / 2;
CoordType pivot;
{
// Bubble sort the input[left], input[center], input[right], so that a median of the three values
// will end up in input[center].
CoordType left_value = this->coordinate(input[left], dimension);
CoordType center_value = this->coordinate(input[center], dimension);
CoordType right_value = this->coordinate(input[right], dimension);
if (left_value > center_value) {
std::swap(input[left], input[center]);
std::swap(left_value, center_value);
}
if (left_value > right_value) {
std::swap(input[left], input[right]);
right_value = left_value;
}
if (center_value > right_value) {
std::swap(input[center], input[right]);
center_value = right_value;
}
pivot = center_value;
}
if (right <= left + 2)
// The <left, right> interval is already sorted.
break;
size_t i = left;
size_t j = right - 1;
std::swap(input[center], input[j]);
// Partition the set based on the pivot.
for (;;) {
// Skip left points that are already at correct positions.
// Search will certainly stop at position (right - 1), which stores the pivot.
while (this->coordinate(input[++ i], dimension) < pivot) ;
// Skip right points that are already at correct positions.
while (this->coordinate(input[-- j], dimension) > pivot && i < j) ;
if (i >= j)
break;
std::swap(input[i], input[j]);
}
// Restore pivot to the center of the sequence.
std::swap(input[i], input[right - 1]);
// Which side the kth element is in?
if (k < i)
right = i - 1;
else if (k == i)
// Sequence is partitioned, kth element is at its place.
break;
else
left = i + 1;
}
}
// Partition the input m_nodes <left, right> at "k" and "dimension" using the QuickSelect method:
// https://en.wikipedia.org/wiki/Quickselect
// Items left of the k'th item are lower than the k'th item in the "dimension",
// items right of the k'th item are higher than the k'th item in the "dimension",
void partition_input(std::vector<size_t> &input, const size_t dimension, size_t left, size_t right, const size_t k) const
{
while (left < right) {
size_t center = (left + right) / 2;
CoordType pivot;
{
// Bubble sort the input[left], input[center], input[right], so that a median of the three values
// will end up in input[center].
CoordType left_value = this->coordinate(input[left], dimension);
CoordType center_value = this->coordinate(input[center], dimension);
CoordType right_value = this->coordinate(input[right], dimension);
if (left_value > center_value) {
std::swap(input[left], input[center]);
std::swap(left_value, center_value);
}
if (left_value > right_value) {
std::swap(input[left], input[right]);
right_value = left_value;
}
if (center_value > right_value) {
std::swap(input[center], input[right]);
center_value = right_value;
}
pivot = center_value;
}
if (right <= left + 2)
// The <left, right> interval is already sorted.
break;
size_t i = left;
size_t j = right - 1;
std::swap(input[center], input[j]);
// Partition the set based on the pivot.
for (;;) {
// Skip left points that are already at correct positions.
// Search will certainly stop at position (right - 1), which stores the pivot.
while (this->coordinate(input[++ i], dimension) < pivot) ;
// Skip right points that are already at correct positions.
while (this->coordinate(input[-- j], dimension) > pivot && i < j) ;
if (i >= j)
break;
std::swap(input[i], input[j]);
}
// Restore pivot to the center of the sequence.
std::swap(input[i], input[right - 1]);
// Which side the kth element is in?
if (k < i)
right = i - 1;
else if (k == i)
// Sequence is partitioned, kth element is at its place.
break;
else
left = i + 1;
}
}
template<typename Visitor>
void visit_recursive(size_t node, size_t dimension, Visitor &visitor) const
{
assert(! m_nodes.empty());
if (node >= m_nodes.size() || m_nodes[node] == npos)
return;
template<typename Visitor>
void visit_recursive(size_t node, size_t dimension, Visitor &visitor) const
{
assert(! m_nodes.empty());
if (node >= m_nodes.size() || m_nodes[node] == npos)
return;
// Left / right child node index.
size_t left = node * 2 + 1;
size_t right = left + 1;
unsigned int mask = visitor(m_nodes[node], dimension);
if ((mask & (unsigned int)VisitorReturnMask::STOP) == 0) {
size_t next_dimension = (++ dimension == NumDimensions) ? 0 : dimension;
if (mask & (unsigned int)VisitorReturnMask::CONTINUE_LEFT)
visit_recursive(left, next_dimension, visitor);
if (mask & (unsigned int)VisitorReturnMask::CONTINUE_RIGHT)
visit_recursive(right, next_dimension, visitor);
}
}
// Left / right child node index.
size_t left = node * 2 + 1;
size_t right = left + 1;
unsigned int mask = visitor(m_nodes[node], dimension);
if ((mask & (unsigned int)VisitorReturnMask::STOP) == 0) {
size_t next_dimension = (++ dimension == NumDimensions) ? 0 : dimension;
if (mask & (unsigned int)VisitorReturnMask::CONTINUE_LEFT)
visit_recursive(left, next_dimension, visitor);
if (mask & (unsigned int)VisitorReturnMask::CONTINUE_RIGHT)
visit_recursive(right, next_dimension, visitor);
}
}
std::vector<size_t> m_nodes;
std::vector<size_t> m_nodes;
};
// Find a closest point using Euclidian metrics.
// Returns npos if not found.
template<typename KDTreeIndirectType, typename PointType, typename FilterFn>
size_t find_closest_point(const KDTreeIndirectType &kdtree, const PointType &point, FilterFn filter)
template<size_t K,
typename PointType,
typename FilterFn,
size_t D,
typename CoordT,
typename CoordFn>
std::array<size_t, K> find_closest_points(
const KDTreeIndirect<D, CoordT, CoordFn> &kdtree,
const PointType &point,
FilterFn filter)
{
using CoordType = typename KDTreeIndirectType::CoordType;
using Tree = KDTreeIndirect<D, CoordT, CoordFn>;
struct Visitor {
const KDTreeIndirectType &kdtree;
const PointType &point;
const FilterFn filter;
size_t min_idx = KDTreeIndirectType::npos;
CoordType min_dist = std::numeric_limits<CoordType>::max();
struct Visitor
{
const Tree &kdtree;
const PointType &point;
const FilterFn filter;
Visitor(const KDTreeIndirectType &kdtree, const PointType &point, FilterFn filter) : kdtree(kdtree), point(point), filter(filter) {}
unsigned int operator()(size_t idx, size_t dimension) {
if (this->filter(idx)) {
auto dist = CoordType(0);
for (size_t i = 0; i < KDTreeIndirectType::NumDimensions; ++ i) {
CoordType d = point[i] - kdtree.coordinate(idx, i);
dist += d * d;
}
if (dist < min_dist) {
min_dist = dist;
min_idx = idx;
}
}
return kdtree.descent_mask(point[dimension], min_dist, idx, dimension);
}
} visitor(kdtree, point, filter);
std::array<std::pair<size_t, CoordT>, K> results;
kdtree.visit(visitor);
return visitor.min_idx;
Visitor(const Tree &kdtree, const PointType &point, FilterFn filter)
: kdtree(kdtree), point(point), filter(filter)
{
results.fill(std::make_pair(Tree::npos,
std::numeric_limits<CoordT>::max()));
}
unsigned int operator()(size_t idx, size_t dimension)
{
if (this->filter(idx)) {
auto dist = CoordT(0);
for (size_t i = 0; i < D; ++i) {
CoordT d = point[i] - kdtree.coordinate(idx, i);
dist += d * d;
}
auto res = std::make_pair(idx, dist);
auto it = std::lower_bound(results.begin(), results.end(),
res, [](auto &r1, auto &r2) {
return r1.second < r2.second;
});
if (it != results.end()) {
std::rotate(it, std::prev(results.end()), results.end());
*it = res;
}
}
return kdtree.descent_mask(point[dimension],
results.front().second, idx,
dimension);
}
} visitor(kdtree, point, filter);
kdtree.visit(visitor);
std::array<size_t, K> ret;
for (size_t i = 0; i < K; i++) ret[i] = visitor.results[i].first;
return ret;
}
template<size_t K, typename PointType, size_t D, typename CoordT, typename CoordFn>
std::array<size_t, K> find_closest_points(
const KDTreeIndirect<D, CoordT, CoordFn> &kdtree, const PointType &point)
{
return find_closest_points<K>(kdtree, point, [](size_t) { return true; });
}
template<typename PointType,
typename FilterFn,
size_t D,
typename CoordT,
typename CoordFn>
size_t find_closest_point(const KDTreeIndirect<D, CoordT, CoordFn> &kdtree,
const PointType &point,
FilterFn filter)
{
return find_closest_points<1>(kdtree, point, filter)[0];
}
template<typename KDTreeIndirectType, typename PointType>
size_t find_closest_point(const KDTreeIndirectType& kdtree, const PointType& point)
{
return find_closest_point(kdtree, point, [](size_t) { return true; });
return find_closest_point(kdtree, point, [](size_t) { return true; });
}
// Find nearby points (spherical neighbourhood) using Euclidian metrics.
template<typename KDTreeIndirectType, typename PointType, typename FilterFn>
std::vector<size_t> find_nearby_points(const KDTreeIndirectType &kdtree, const PointType &center,
const typename KDTreeIndirectType::CoordType& max_distance, FilterFn filter)
{
using CoordType = typename KDTreeIndirectType::CoordType;
struct Visitor {
const KDTreeIndirectType &kdtree;
const PointType center;
const CoordType max_distance_squared;
const FilterFn filter;
std::vector<size_t> result;
Visitor(const KDTreeIndirectType &kdtree, const PointType& center, const CoordType &max_distance,
FilterFn filter) :
kdtree(kdtree), center(center), max_distance_squared(max_distance*max_distance), filter(filter) {
}
unsigned int operator()(size_t idx, size_t dimension) {
if (this->filter(idx)) {
auto dist = CoordType(0);
for (size_t i = 0; i < KDTreeIndirectType::NumDimensions; ++i) {
CoordType d = center[i] - kdtree.coordinate(idx, i);
dist += d * d;
}
if (dist < max_distance_squared) {
result.push_back(idx);
}
}
return kdtree.descent_mask(center[dimension], max_distance_squared, idx, dimension);
}
} visitor(kdtree, center, max_distance, filter);
kdtree.visit(visitor);
return visitor.result;
}
template<typename KDTreeIndirectType, typename PointType>
std::vector<size_t> find_nearby_points(const KDTreeIndirectType &kdtree, const PointType &center,
const typename KDTreeIndirectType::CoordType& max_distance)
{
return find_nearby_points(kdtree, center, max_distance, [](size_t) {
return true;
});
}
// Find nearby points (spherical neighbourhood) using Euclidian metrics.
template<typename KDTreeIndirectType, typename PointType, typename FilterFn>
std::vector<size_t> find_nearby_points(const KDTreeIndirectType &kdtree,
const PointType &bb_min,
const PointType &bb_max,
FilterFn filter)
{
struct Visitor {
const KDTreeIndirectType &kdtree;
const PointType &bb_min, &bb_max;
const FilterFn filter;
std::vector<size_t> result;
Visitor(const KDTreeIndirectType &kdtree, const PointType& bbmin, const PointType& bbmax,
FilterFn filter) :
kdtree(kdtree), bb_min{bbmin}, bb_max{bbmax}, filter(filter) {
}
unsigned int operator()(size_t idx, size_t dimension) {
unsigned int ret =
static_cast<unsigned int>(VisitorReturnMask::CONTINUE_LEFT) |
static_cast<unsigned int>(VisitorReturnMask::CONTINUE_RIGHT);
if (this->filter(idx)) {
PointType p;
bool contains = true;
for (size_t i = 0; i < KDTreeIndirectType::NumDimensions; ++i) {
p(i) = kdtree.coordinate(idx, i);
contains = contains && bb_min(i) <= p(i) && p(i) <= bb_max(i);
}
if (p(dimension) < bb_min(dimension))
ret = static_cast<unsigned int>(VisitorReturnMask::CONTINUE_RIGHT);
if (p(dimension) > bb_max(dimension))
ret = static_cast<unsigned int>(VisitorReturnMask::CONTINUE_LEFT);
if (contains)
result.emplace_back(idx);
}
return ret;
}
} visitor(kdtree, bb_min, bb_max, filter);
kdtree.visit(visitor);
return visitor.result;
}
} // namespace Slic3r

View file

@ -137,7 +137,7 @@ Model::~Model()
// Loading model from a file, it may be a simple geometry file as STL or OBJ, however it may be a project file as well.
Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions,
LoadStrategy options, PlateDataPtrs* plate_data, std::vector<Preset*>* project_presets, bool *is_xxx, Semver* file_version, Import3mfProgressFn proFn,
ImportStepProgressFn stepFn, StepIsUtf8Fn stepIsUtf8Fn, BBLProject* project)
ImportstlProgressFn stlFn, ImportStepProgressFn stepFn, StepIsUtf8Fn stepIsUtf8Fn, BBLProject* project)
{
Model model;
@ -163,7 +163,7 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
boost::algorithm::iends_with(input_file, ".step"))
result = load_step(input_file.c_str(), &model, stepFn, stepIsUtf8Fn);
else if (boost::algorithm::iends_with(input_file, ".stl"))
result = load_stl(input_file.c_str(), &model);
result = load_stl(input_file.c_str(), &model, nullptr, stlFn);
else if (boost::algorithm::iends_with(input_file, ".obj"))
result = load_obj(input_file.c_str(), &model);
//BBS: remove the old .amf.xml files
@ -185,10 +185,10 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
if (model.objects.empty())
throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty");
for (ModelObject *o : model.objects)
o->input_file = input_file;
if (options & LoadStrategy::AddDefaultInstances)
model.add_default_instances();
@ -259,14 +259,14 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
//BBS
//CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_STAGE_UPDATE_GCODE\n");
if (proFn) {
proFn(IMPORT_STAGE_UPDATE_GCODE, 0, 1, cb_cancel);
if (cb_cancel)
throw Slic3r::RuntimeError("Canceled");
}
CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_STAGE_CHECK_MODE_GCODE\n");
@ -416,7 +416,7 @@ void Model::collect_reusable_objects(std::vector<ObjectBase*>& objects)
std::mem_fn(&ObjectBase::id));
model_object->volumes.clear();
}
// we never own these objects
// we never own these objects
this->objects.clear();
}
@ -591,7 +591,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
assert(this->objects.size() >= 2);
if (this->objects.size() < 2)
return;
ModelObject* object = new ModelObject(this);
object->input_file = this->objects.front()->input_file;
object->name = boost::filesystem::path(this->objects.front()->input_file).stem().string();
@ -601,7 +601,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
for (const ModelObject* o : this->objects)
for (const ModelVolume* v : o->volumes) {
// If there are more than one object, put all volumes together
// If there are more than one object, put all volumes together
// Each object may contain any number of volumes and instances
// The volumes transformations are relative to the object containing them...
Geometry::Transformation trafo_volume = v->get_transformation();
@ -620,7 +620,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
} else {
for (const ModelInstance* i : o->instances)
// ...so, transform everything to a common reference system (world)
copy_volume(object->add_volume(*v))->set_transformation(i->get_transformation() * trafo_volume);
copy_volume(object->add_volume(*v))->set_transformation(i->get_transformation() * trafo_volume);
}
}
@ -1010,6 +1010,20 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&me
return v;
}
ModelVolume* ModelObject::add_volume_with_shared_mesh(const ModelVolume &other, ModelVolumeType type /*= ModelVolumeType::INVALID*/)
{
ModelVolume* v = new ModelVolume(this, other.m_mesh);
if (type != ModelVolumeType::INVALID && v->type() != type)
v->set_type(type);
this->volumes.push_back(v);
// The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull.
// v->center_geometry_after_creation();
// this->invalidate_bounding_box();
// BBS: backup
Slic3r::save_object_mesh(*this);
return v;
}
void ModelObject::delete_volume(size_t idx)
{
ModelVolumePtrs::iterator i = this->volumes.begin() + idx;
@ -1324,7 +1338,7 @@ Polygon ModelObject::convex_hull_2d(const Transform3d& trafo_instance) const
void ModelObject::center_around_origin(bool include_modifiers)
{
// calculate the displacements needed to
// calculate the displacements needed to
// center this object around the origin
const BoundingBoxf3 bb = include_modifiers ? full_raw_mesh_bounding_box() : raw_mesh_bounding_box();
@ -1476,8 +1490,8 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
// Perform conversion only if the target "imperial" state is different from the current one.
// This check supports conversion of "mixed" set of volumes, each with different "imperial" state.
if (//vol->source.is_converted_from_inches != from_imperial &&
(volume_idxs.empty() ||
if (//vol->source.is_converted_from_inches != from_imperial &&
(volume_idxs.empty() ||
std::find(volume_idxs.begin(), volume_idxs.end(), vol_idx) != volume_idxs.end())) {
vol->scale_geometry_after_creation(koef);
vol->set_offset(Vec3d(koef, koef, koef).cwiseProduct(volume->get_offset()));
@ -1538,7 +1552,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, std::array<Vec3d, 4> plane_poi
bool keep_lower = attributes.has(ModelObjectCutAttribute::KeepLower);
bool cut_to_parts = attributes.has(ModelObjectCutAttribute::CutToParts);
ModelObject* upper = keep_upper ? ModelObject::new_clone(*this) : nullptr;
ModelObject* lower = cut_to_parts ? upper : (keep_lower ? ModelObject::new_clone(*this) : nullptr);
ModelObject* lower = (cut_to_parts&&upper!=nullptr) ? upper : (keep_lower ? ModelObject::new_clone(*this) : nullptr);
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
upper->set_model(nullptr);
@ -1598,7 +1612,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, std::array<Vec3d, 4> plane_poi
if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower->add_volume(*volume);
}
else if (! volume->mesh().empty()) {
else if (! volume->mesh().empty()) {
// Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed.
TriangleMesh mesh(volume->mesh());
@ -1744,7 +1758,7 @@ ModelObjectPtrs ModelObject::segment(size_t instance, unsigned int max_extruders
// Modifiers are not cut, but we still need to add the instance transformation
// to the modifier volume transformation to preserve their shape properly.
volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix));
upper->add_volume(*volume);
upper->add_volume(*volume);
}
else if (!volume->mesh().empty()) {
// Transform the mesh by the combined transformation matrix.
@ -2185,7 +2199,7 @@ void ModelObject::print_info() const
using namespace std;
cout << fixed;
boost::nowide::cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl;
TriangleMesh mesh = this->raw_mesh();
BoundingBoxf3 bb = mesh.bounding_box();
Vec3d size = bb.size();
@ -2203,7 +2217,7 @@ void ModelObject::print_info() const
cout << "manifold = " << (mesh.stats().manifold() ? "yes" : "no") << endl;
if (! mesh.stats().manifold())
cout << "open_edges = " << mesh.stats().open_edges << endl;
if (mesh.stats().repaired()) {
const RepairedMeshErrors& stats = mesh.stats().repaired_errors;
if (stats.degenerate_facets > 0)
@ -2286,7 +2300,7 @@ void ModelVolume::set_material_id(t_model_material_id material_id)
}
ModelMaterial* ModelVolume::material() const
{
{
return this->object->get_model()->get_material(m_material_id);
}
@ -2362,8 +2376,10 @@ void ModelVolume::center_geometry_after_creation(bool update_source_offset)
Vec3d shift = this->mesh().bounding_box().center();
if (!shift.isApprox(Vec3d::Zero()))
{
if (m_mesh)
const_cast<TriangleMesh*>(m_mesh.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
if (m_mesh) {
const_cast<TriangleMesh*>(m_mesh.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
const_cast<TriangleMesh*>(m_mesh.get())->set_init_shift(shift);
}
if (m_convex_hull)
const_cast<TriangleMesh*>(m_convex_hull.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
translate(shift);
@ -2803,7 +2819,7 @@ double Model::getThermalLength(const std::vector<ModelVolume*> modelVolumePtrs)
}
return thermalLength;
}
// max printing speed, difference in bed temperature and envirument temperature and bed adhension coefficients are considered
// max printing speed, difference in bed temperature and envirument temperature and bed adhension coefficients are considered
double ModelInstance::get_auto_brim_width(double deltaT, double adhension) const
{
BoundingBoxf3 raw_bbox = object->raw_mesh_bounding_box();
@ -2896,7 +2912,7 @@ double ModelInstance::get_auto_brim_width() const
void ModelInstance::get_arrange_polygon(void* ap) const
{
// static const double SIMPLIFY_TOLERANCE_MM = 0.1;
Vec3d rotation = get_rotation();
rotation.z() = 0.;
Transform3d trafo_instance =
@ -2909,7 +2925,7 @@ void ModelInstance::get_arrange_polygon(void* ap) const
// pp = p.simplify(scaled<double>(SIMPLIFY_TOLERANCE_MM));
// if (!pp.empty()) p = pp.front();
// }
arrangement::ArrangePolygon& ret = *(arrangement::ArrangePolygon*)ap;
ret.poly.contour = std::move(p);
ret.translation = Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))};
@ -3039,6 +3055,12 @@ void FacetsAnnotation::set_triangle_from_string(int triangle_id, const std::stri
}
}
bool FacetsAnnotation::equals(const FacetsAnnotation &other) const
{
const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>>& data = other.get_data();
return (m_data == data);
}
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing.
bool model_object_list_equal(const Model &model_old, const Model &model_new)
@ -3140,22 +3162,22 @@ bool model_property_changed(const ModelObject &model_object_old, const ModelObje
bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject& mo_new)
{
return model_property_changed(mo, mo_new,
[](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; },
return model_property_changed(mo, mo_new,
[](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; },
[](const ModelVolume &mv_old, const ModelVolume &mv_new){ return mv_old.supported_facets.timestamp_matches(mv_new.supported_facets); });
}
bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo_new)
{
return model_property_changed(mo, mo_new,
[](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; },
return model_property_changed(mo, mo_new,
[](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; },
[](const ModelVolume &mv_old, const ModelVolume &mv_new){ return mv_old.seam_facets.timestamp_matches(mv_new.seam_facets); });
}
bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObject& mo_new)
{
return model_property_changed(mo, mo_new,
[](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; },
return model_property_changed(mo, mo_new,
[](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; },
[](const ModelVolume &mv_old, const ModelVolume &mv_new){ return mv_old.mmu_segmentation_facets.timestamp_matches(mv_new.mmu_segmentation_facets); });
}
@ -3189,7 +3211,7 @@ bool model_has_advanced_features(const Model &model)
void check_model_ids_validity(const Model &model)
{
std::set<ObjectID> ids;
auto check = [&ids](ObjectID id) {
auto check = [&ids](ObjectID id) {
assert(id.valid());
assert(ids.find(id) == ids.end());
ids.insert(id);

View file

@ -18,6 +18,8 @@
#include "Format/bbs_3mf.hpp"
//BBS: add step
#include "Format/STEP.hpp"
//BBS: add stl
#include "Format/STL.hpp"
#include <map>
#include <memory>
@ -306,6 +308,7 @@ public:
ModelVolume* add_volume(TriangleMesh &&mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART);
ModelVolume* add_volume(const ModelVolume &volume, ModelVolumeType type = ModelVolumeType::INVALID);
ModelVolume* add_volume(const ModelVolume &volume, TriangleMesh &&mesh);
ModelVolume* add_volume_with_shared_mesh(const ModelVolume &other, ModelVolumeType type = ModelVolumeType::MODEL_PART);
void delete_volume(size_t idx);
void clear_volumes();
void sort_volumes(bool full_sort);
@ -615,6 +618,7 @@ public:
void set_triangle_from_string(int triangle_id, const std::string& str);
// After deserializing the last triangle, shrink data to fit.
void shrink_to_fit() { m_data.first.shrink_to_fit(); m_data.second.shrink_to_fit(); }
bool equals(const FacetsAnnotation &other) const;
private:
// Constructors to be only called by derived classes.
@ -674,6 +678,7 @@ public:
// The triangular model.
const TriangleMesh& mesh() const { return *m_mesh.get(); }
const TriangleMesh* mesh_ptr() const { return m_mesh.get(); }
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<const TriangleMesh>(std::move(mesh)); }
void set_mesh(const indexed_triangle_set &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
@ -857,6 +862,18 @@ private:
if (mesh.facets_count() > 1)
calculate_convex_hull();
}
ModelVolume(ModelObject *object, const std::shared_ptr<const TriangleMesh> &mesh, ModelVolumeType type = ModelVolumeType::MODEL_PART) : m_mesh(mesh), m_type(type), object(object)
{
assert(this->id().valid());
assert(this->config.id().valid());
assert(this->supported_facets.id().valid());
assert(this->seam_facets.id().valid());
assert(this->mmu_segmentation_facets.id().valid());
assert(this->id() != this->config.id());
assert(this->id() != this->supported_facets.id());
assert(this->id() != this->seam_facets.id());
assert(this->id() != this->mmu_segmentation_facets.id());
}
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull, ModelVolumeType type = ModelVolumeType::MODEL_PART) :
m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(type), object(object) {
assert(this->id().valid());
@ -1282,7 +1299,7 @@ public:
DynamicPrintConfig* config = nullptr, ConfigSubstitutionContext* config_substitutions = nullptr,
LoadStrategy options = LoadStrategy::AddDefaultInstances, PlateDataPtrs* plate_data = nullptr,
std::vector<Preset*>* project_presets = nullptr, bool* is_xxx = nullptr, Semver* file_version = nullptr, Import3mfProgressFn proFn = nullptr,
ImportStepProgressFn stepFn = nullptr, StepIsUtf8Fn stepIsUtf8Fn = nullptr, BBLProject* project = nullptr);
ImportstlProgressFn stlFn = nullptr, ImportStepProgressFn stepFn = nullptr, StepIsUtf8Fn stepIsUtf8Fn = nullptr, BBLProject* project = nullptr);
// BBS
static double findMaxSpeed(const ModelObject* object);
static double getThermalLength(const ModelVolume* modelVolumePtr);

View file

@ -63,6 +63,23 @@ int MultiPoint::find_point(const Point &point) const
return -1; // not found
}
int MultiPoint::find_point(const Point &point, double scaled_epsilon) const
{
if (scaled_epsilon == 0) return this->find_point(point);
auto dist2_min = std::numeric_limits<double>::max();
auto eps2 = scaled_epsilon * scaled_epsilon;
int idx_min = -1;
for (const Point &pt : this->points) {
double d2 = (pt - point).cast<double>().squaredNorm();
if (d2 < dist2_min) {
idx_min = int(&pt - &this->points.front());
dist2_min = d2;
}
}
return dist2_min < eps2 ? idx_min : -1;
}
bool MultiPoint::has_boundary_point(const Point &point) const
{
double dist = (point.projection_onto(*this) - point).cast<double>().norm();

View file

@ -43,7 +43,12 @@ public:
double length() const;
bool is_valid() const { return this->points.size() >= 2; }
// Return index of a polygon point exactly equal to point.
// Return -1 if no such point exists.
int find_point(const Point &point) const;
// Return index of the closest point to point closer than scaled_epsilon.
// Return -1 if no such point exists.
int find_point(const Point &point, const double scaled_epsilon) const;
bool has_boundary_point(const Point &point) const;
int closest_point_index(const Point &point) const {
int idx = -1;

View file

@ -0,0 +1,142 @@
#include "NormalUtils.hpp"
using namespace Slic3r;
Vec3f NormalUtils::create_triangle_normal(
const stl_triangle_vertex_indices &indices,
const std::vector<stl_vertex> & vertices)
{
const stl_vertex &v0 = vertices[indices[0]];
const stl_vertex &v1 = vertices[indices[1]];
const stl_vertex &v2 = vertices[indices[2]];
Vec3f direction = (v1 - v0).cross(v2 - v0);
direction.normalize();
return direction;
}
std::vector<Vec3f> NormalUtils::create_triangle_normals(
const indexed_triangle_set &its)
{
std::vector<Vec3f> normals;
normals.reserve(its.indices.size());
for (const Vec3crd &index : its.indices) {
normals.push_back(create_triangle_normal(index, its.vertices));
}
return normals;
}
NormalUtils::Normals NormalUtils::create_normals_average_neighbor(
const indexed_triangle_set &its)
{
size_t count_vertices = its.vertices.size();
std::vector<Vec3f> normals(count_vertices, Vec3f(.0f, .0f, .0f));
std::vector<unsigned int> count(count_vertices, 0);
for (const Vec3crd &indice : its.indices) {
Vec3f normal = create_triangle_normal(indice, its.vertices);
for (int i = 0; i < 3; ++i) {
normals[indice[i]] += normal;
++count[indice[i]];
}
}
// normalize to size 1
for (auto &normal : normals) {
size_t index = &normal - &normals.front();
normal /= static_cast<float>(count[index]);
}
return normals;
}
// calc triangle angle of vertex defined by index to triangle indices
float NormalUtils::indice_angle(int i,
const Vec3crd & indice,
const std::vector<stl_vertex> &vertices)
{
int i1 = (i == 0) ? 2 : (i - 1);
int i2 = (i == 2) ? 0 : (i + 1);
Vec3f v1 = vertices[i1] - vertices[i];
Vec3f v2 = vertices[i2] - vertices[i];
v1.normalize();
v2.normalize();
float w = v1.dot(v2);
if (w > 1.f)
w = 1.f;
else if (w < -1.f)
w = -1.f;
return acos(w);
}
NormalUtils::Normals NormalUtils::create_normals_angle_weighted(
const indexed_triangle_set &its)
{
size_t count_vertices = its.vertices.size();
std::vector<Vec3f> normals(count_vertices, Vec3f(.0f, .0f, .0f));
std::vector<float> count(count_vertices, 0.f);
for (const Vec3crd &indice : its.indices) {
Vec3f normal = create_triangle_normal(indice, its.vertices);
Vec3f angles(indice_angle(0, indice, its.vertices),
indice_angle(1, indice, its.vertices), 0.f);
angles[2] = (M_PI - angles[0] - angles[1]);
for (int i = 0; i < 3; ++i) {
const float &weight = angles[i];
normals[indice[i]] += normal * weight;
count[indice[i]] += weight;
}
}
// normalize to size 1
for (auto &normal : normals) {
size_t index = &normal - &normals.front();
normal /= count[index];
}
return normals;
}
NormalUtils::Normals NormalUtils::create_normals_nelson_weighted(
const indexed_triangle_set &its)
{
size_t count_vertices = its.vertices.size();
std::vector<Vec3f> normals(count_vertices, Vec3f(.0f, .0f, .0f));
std::vector<float> count(count_vertices, 0.f);
const std::vector<stl_vertex> &vertices = its.vertices;
for (const Vec3crd &indice : its.indices) {
Vec3f normal = create_triangle_normal(indice, vertices);
const stl_vertex &v0 = vertices[indice[0]];
const stl_vertex &v1 = vertices[indice[1]];
const stl_vertex &v2 = vertices[indice[2]];
float e0 = (v0 - v1).norm();
float e1 = (v1 - v2).norm();
float e2 = (v2 - v0).norm();
Vec3f coefs(e0 * e2, e0 * e1, e1 * e2);
for (int i = 0; i < 3; ++i) {
const float &weight = coefs[i];
normals[indice[i]] += normal * weight;
count[indice[i]] += weight;
}
}
// normalize to size 1
for (auto &normal : normals) {
size_t index = &normal - &normals.front();
normal /= count[index];
}
return normals;
}
// calculate normals by averaging normals of neghbor triangles
std::vector<Vec3f> NormalUtils::create_normals(
const indexed_triangle_set &its, VertexNormalType type)
{
switch (type) {
case VertexNormalType::AverageNeighbor:
return create_normals_average_neighbor(its);
case VertexNormalType::AngleWeighted:
return create_normals_angle_weighted(its);
case VertexNormalType::NelsonMaxWeighted:
default:
return create_normals_nelson_weighted(its);
}
}

View file

@ -0,0 +1,69 @@
#ifndef slic3r_NormalUtils_hpp_
#define slic3r_NormalUtils_hpp_
#include "Point.hpp"
#include "Model.hpp"
namespace Slic3r {
/// <summary>
/// Collection of static function
/// to create normals
/// </summary>
class NormalUtils
{
public:
using Normal = Vec3f;
using Normals = std::vector<Normal>;
NormalUtils() = delete; // only static functions
enum class VertexNormalType {
AverageNeighbor,
AngleWeighted,
NelsonMaxWeighted
};
/// <summary>
/// Create normal for triangle defined by indices from vertices
/// </summary>
/// <param name="indices">index into vertices</param>
/// <param name="vertices">vector of vertices</param>
/// <returns>normal to triangle(normalized to size 1)</returns>
static Normal create_triangle_normal(
const stl_triangle_vertex_indices &indices,
const std::vector<stl_vertex> & vertices);
/// <summary>
/// Create normals for each vertices
/// </summary>
/// <param name="its">indices and vertices</param>
/// <returns>Vector of normals</returns>
static Normals create_triangle_normals(const indexed_triangle_set &its);
/// <summary>
/// Create normals for each vertex by averaging neighbor triangles normal
/// </summary>
/// <param name="its">Triangle indices and vertices</param>
/// <param name="type">Type of calculation normals</param>
/// <returns>Normal for each vertex</returns>
static Normals create_normals(
const indexed_triangle_set &its,
VertexNormalType type = VertexNormalType::NelsonMaxWeighted);
static Normals create_normals_average_neighbor(const indexed_triangle_set &its);
static Normals create_normals_angle_weighted(const indexed_triangle_set &its);
static Normals create_normals_nelson_weighted(const indexed_triangle_set &its);
/// <summary>
/// Calculate angle of trinagle side.
/// </summary>
/// <param name="i">index to indices, define angle point</param>
/// <param name="indice">address to vertices</param>
/// <param name="vertices">vertices data</param>
/// <returns>Angle [in radian]</returns>
static float indice_angle(int i,
const Vec3crd & indice,
const std::vector<stl_vertex> &vertices);
};
} // namespace Slic3r
#endif // slic3r_NormalUtils_hpp_

View file

@ -517,6 +517,28 @@ bool remove_degenerate(Polylines &polylines)
return modified;
}
std::pair<int, Point> foot_pt(const Points &polyline, const Point &pt)
{
if (polyline.size() < 2) return std::make_pair(-1, Point(0, 0));
auto d2_min = std::numeric_limits<double>::max();
Point foot_pt_min;
Point prev = polyline.front();
auto it = polyline.begin();
auto it_proj = polyline.begin();
for (++it; it != polyline.end(); ++it) {
Point foot_pt = pt.projection_onto(Line(prev, *it));
double d2 = (foot_pt - pt).cast<double>().squaredNorm();
if (d2 < d2_min) {
d2_min = d2;
foot_pt_min = foot_pt;
it_proj = it;
}
prev = *it;
}
return std::make_pair(int(it_proj - polyline.begin()) - 1, foot_pt_min);
}
ThickLines ThickPolyline::thicklines() const
{
ThickLines lines;

View file

@ -222,6 +222,9 @@ const Point& leftmost_point(const Polylines &polylines);
bool remove_degenerate(Polylines &polylines);
// Returns index of a segment of a polyline and foot point of pt on polyline.
std::pair<int, Point> foot_pt(const Points &polyline, const Point &pt);
class ThickPolyline : public Polyline {
public:
ThickPolyline() : endpoints(std::make_pair(false, false)) {}

View file

@ -691,7 +691,7 @@ static std::vector<std::string> s_Preset_filament_options {
"filament_flow_ratio", "filament_density", "filament_cost", "filament_minimal_purge_on_wipe_tower",
"chamber_temperature", "nozzle_temperature", "nozzle_temperature_initial_layer",
// BBS
"cool_plate_temp", "eng_plate_temp", "hot_plate_temp", "cool_plate_temp_initial_layer", "eng_plate_temp_initial_layer", "hot_plate_temp_initial_layer",
"cool_plate_temp", "eng_plate_temp", "hot_plate_temp", "textured_plate_temp", "cool_plate_temp_initial_layer", "eng_plate_temp_initial_layer", "hot_plate_temp_initial_layer","textured_plate_temp_initial_layer",
// "bed_type",
//BBS:temperature_vitrification
"temperature_vitrification", "reduce_fan_stop_start_freq", "slow_down_for_layer_cooling", "fan_min_speed",
@ -1400,7 +1400,7 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
//filament_id
std::string cloud_filament_id;
if ((m_type == Preset::TYPE_FILAMENT) && preset_values.find(BBL_JSON_KEY_FILAMENT_ID) == preset_values.end()) {
if ((m_type == Preset::TYPE_FILAMENT) && preset_values.find(BBL_JSON_KEY_FILAMENT_ID) != preset_values.end()) {
cloud_filament_id = preset_values[BBL_JSON_KEY_FILAMENT_ID];
}
@ -1830,9 +1830,12 @@ std::pair<Preset*, bool> PresetCollection::load_external_preset(
Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select)
{
lock();
auto it = this->find_preset_internal(name);
if (it == m_presets.end() || it->name != name) {
// The preset was not found. Create a new preset.
if (m_presets.begin() + m_idx_selected >= it)
++m_idx_selected;
it = m_presets.emplace(it, Preset(m_type, name, false));
}
Preset &preset = *it;
@ -1842,6 +1845,7 @@ Preset& PresetCollection::load_preset(const std::string &path, const std::string
preset.is_dirty = false;
if (select)
this->select_preset_by_name(name, true);
unlock();
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", preset type %1%, name %2%, path %3%, is_system %4%, is_default %5% is_visible %6%")%Preset::get_type_string(m_type) %preset.name %preset.file %preset.is_system %preset.is_default %preset.is_visible;
return preset;

View file

@ -592,6 +592,102 @@ PresetsConfigSubstitutions PresetBundle::load_user_presets(AppConfig &config, st
return substitutions;
}
PresetsConfigSubstitutions PresetBundle::import_presets(std::vector<std::string> & files,
std::function<int(std::string const &)> override_confirm,
ForwardCompatibilitySubstitutionRule rule)
{
PresetsConfigSubstitutions substitutions;
int overwrite = 0;
std::vector<std::string> result;
for (auto &file : files) {
if (Slic3r::is_json_file(file)) {
try {
DynamicPrintConfig config;
// BBS: change to json format
// ConfigSubstitutions config_substitutions = config.load_from_ini(preset.file, substitution_rule);
std::map<std::string, std::string> key_values;
std::string reason;
ConfigSubstitutions config_substitutions = config.load_from_json(file, rule, key_values, reason);
std::string name = key_values[BBL_JSON_KEY_NAME];
std::string version_str = key_values[BBL_JSON_KEY_VERSION];
boost::optional<Semver> version = Semver::parse(version_str);
if (!version) continue;
Semver app_version = *(Semver::parse(SLIC3R_VERSION));
if (version->maj() != app_version.maj()) {
BOOST_LOG_TRIVIAL(warning) << "Preset incompatibla, not loading: " << name;
continue;
}
PresetCollection * collection = nullptr;
if (config.has("printer_settings_id"))
collection = &printers;
else if (config.has("print_settings_id"))
collection = &prints;
else if (config.has("filament_settings_id"))
collection = &filaments;
if (collection == nullptr) {
BOOST_LOG_TRIVIAL(warning) << "Preset type is unknown, not loading: " << name;
continue;
}
if (auto p = collection->find_preset(name, false)) {
if (p->is_default || p->is_system) {
BOOST_LOG_TRIVIAL(warning) << "Preset already present and is system preset, not loading: " << name;
continue;
}
overwrite = override_confirm(name);
if (overwrite == 0 || overwrite == 2) {
BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << name;
continue;
}
}
DynamicPrintConfig new_config;
Preset * inherit_preset = nullptr;
ConfigOption *inherits_config = config.option(BBL_JSON_KEY_INHERITS);
if (inherits_config) {
ConfigOptionString *option_str = dynamic_cast<ConfigOptionString *>(inherits_config);
std::string inherits_value = option_str->value;
inherit_preset = collection->find_preset(inherits_value, false, true);
}
if (inherit_preset) {
new_config = inherit_preset->config;
} else {
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
// new_config = default_preset.config;
// we should skip this preset here
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", can not find inherit preset for user preset %1%, just skip") % name;
continue;
}
new_config.apply(std::move(config));
Preset &preset = collection->load_preset(collection->path_from_name(name), name, std::move(new_config), false);
preset.is_external = true;
preset.version = *version;
if (inherit_preset)
preset.base_id = inherit_preset->setting_id;
Preset::normalize(preset.config);
// Report configuration fields, which are misplaced into a wrong group.
const Preset &default_preset = collection->default_preset_for(new_config);
std::string incorrect_keys = Preset::remove_invalid_keys(preset.config, default_preset.config);
if (!incorrect_keys.empty())
BOOST_LOG_TRIVIAL(error) << "Error in a preset file: The preset \"" << preset.file << "\" contains the following incorrect keys: " << incorrect_keys
<< ", which were removed";
if (!config_substitutions.empty())
substitutions.push_back({name, collection->type(), PresetConfigSubstitutions::Source::UserFile, file, std::move(config_substitutions)});
preset.save(inherit_preset ? &inherit_preset->config : nullptr);
result.push_back(file);
} catch (const std::ifstream::failure &err) {
BOOST_LOG_TRIVIAL(error) << boost::format("The config cannot be loaded: %1%. Reason: %2%") % file % err.what();
} catch (const std::runtime_error &err) {
BOOST_LOG_TRIVIAL(error) << boost::format("Failed importing config file: %1%. Reason: %2%") % file % err.what();
}
}
}
files = result;
return substitutions;
}
//BBS save user preset to user_id preset folder
void PresetBundle::save_user_presets(AppConfig& config, std::vector<std::string>& need_to_delete_list)
{
@ -626,6 +722,17 @@ void PresetBundle::update_user_presets_directory(const std::string preset_folder
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" finished");
}
void PresetBundle::remove_user_presets_directory(const std::string preset_folder)
{
const std::string dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/" + preset_folder;
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, delete directory : %1%") % dir_user_presets;
fs::path folder(dir_user_presets);
if (fs::exists(folder)) {
fs::remove_all(folder);
}
}
void PresetBundle::update_system_preset_setting_ids(std::map<std::string, std::map<std::string, std::string>>& system_presets)
{
for (auto iterator: system_presets)
@ -3215,6 +3322,36 @@ void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_pri
}
}*/
std::vector<std::string> PresetBundle::export_current_configs(const std::string & path,
std::function<int(std::string const &)> override_confirm,
bool include_modify,
bool export_system_settings)
{
const Preset &print_preset = include_modify ? prints.get_edited_preset() : prints.get_selected_preset();
const Preset &printer_preset = include_modify ? printers.get_edited_preset() : printers.get_selected_preset();
std::set<Preset const *> presets { &print_preset, &printer_preset };
for (auto &f : filament_presets) {
auto filament_preset = filaments.find_preset(f, include_modify);
if (filament_preset) presets.insert(filament_preset);
}
int overwrite = 0;
std::vector<std::string> result;
for (auto preset : presets) {
if ((preset->is_system && !export_system_settings) || preset->is_default)
continue;
std::string file = path + "/" + preset->name + ".json";
if (boost::filesystem::exists(file) && overwrite < 2) {
overwrite = override_confirm(preset->name);
if (overwrite == 0 || overwrite == 2)
continue;
}
preset->config.save_to_json(file, preset->name, "", preset->version.to_string());
result.push_back(file);
}
return result;
}
// Set the filament preset name. As the name could come from the UI selection box,
// an optional "(modified)" suffix will be removed from the filament name.
void PresetBundle::set_filament_preset(size_t idx, const std::string &name)

View file

@ -46,9 +46,11 @@ public:
// BBS Load user presets
PresetsConfigSubstitutions load_user_presets(AppConfig &config, std::map<std::string, std::map<std::string, std::string>>& my_presets, ForwardCompatibilitySubstitutionRule rule);
PresetsConfigSubstitutions import_presets(std::vector<std::string> &files, std::function<int(std::string const &)> override_confirm, ForwardCompatibilitySubstitutionRule rule);
void save_user_presets(AppConfig& config, std::vector<std::string>& need_to_delete_list);
void remove_users_preset(AppConfig &config);
void update_user_presets_directory(const std::string preset_folder);
void update_user_presets_directory(const std::string preset_folder);
void remove_user_presets_directory(const std::string preset_folder);
void update_system_preset_setting_ids(std::map<std::string, std::map<std::string, std::string>>& system_presets);
//BBS: add API to get previous machine
@ -164,6 +166,8 @@ public:
//void export_current_configbundle(const std::string &path);
//BBS: add a function to export system presets for cloud-slicer
//void export_system_configs(const std::string &path);
std::vector<std::string> export_current_configs(const std::string &path, std::function<int(std::string const &)> override_confirm,
bool include_modify, bool export_system_settings = false);
// Enable / disable the "- default -" preset.
void set_default_suppressed(bool default_suppressed);

View file

@ -96,6 +96,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"cool_plate_temp_initial_layer",
"eng_plate_temp_initial_layer",
"hot_plate_temp_initial_layer",
"textured_plate_temp_initial_layer",
"gcode_add_line_number",
"layer_change_gcode",
"fan_min_speed",
@ -175,6 +176,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "cool_plate_temp"
|| opt_key == "eng_plate_temp"
|| opt_key == "hot_plate_temp"
|| opt_key == "textured_plate_temp"
|| opt_key == "enable_prime_tower"
|| opt_key == "prime_tower_width"
|| opt_key == "prime_tower_brim_width"
@ -1132,6 +1134,46 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
}
}
void PrintObject::set_shared_object(PrintObject *object)
{
m_shared_object = object;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, found shared object from %2%")%this%m_shared_object;
}
void PrintObject::clear_shared_object()
{
if (m_shared_object) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, clear previous shared object data %2%")%this %m_shared_object;
m_layers.clear();
m_support_layers.clear();
m_tree_support_layers.clear();
m_shared_object = nullptr;
invalidate_all_steps_without_cancel();
}
}
void PrintObject::copy_layers_from_shared_object()
{
if (m_shared_object) {
m_layers.clear();
m_support_layers.clear();
m_tree_support_layers.clear();
firstLayerObjSliceByVolume.clear();
firstLayerObjSliceByGroups.clear();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, copied layers from object %2%")%this%m_shared_object;
m_layers = m_shared_object->layers();
m_support_layers = m_shared_object->support_layers();
m_tree_support_layers = m_shared_object->tree_support_layers();
firstLayerObjSliceByVolume = m_shared_object->firstLayerObjSlice();
firstLayerObjSliceByGroups = m_shared_object->firstLayerObjGroups();
}
}
// BBS
BoundingBox PrintObject::get_first_layer_bbox(float& a, float& layer_height, std::string& name)
{
@ -1181,15 +1223,115 @@ void Print::process()
{
name_tbb_thread_pool_threads_set_locale();
//compute the PrintObject with the same geometries
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, enter")%this;
for (PrintObject *obj : m_objects)
obj->clear_shared_object();
//add the print_object share check logic
auto is_print_object_the_same = [this](const PrintObject* object1, const PrintObject* object2) -> bool{
if (object1->trafo().matrix() != object2->trafo().matrix())
return false;
const ModelObject* model_obj1 = object1->model_object();
const ModelObject* model_obj2 = object2->model_object();
if (model_obj1->volumes.size() != model_obj2->volumes.size())
return false;
bool has_extruder1 = model_obj1->config.has("extruder");
bool has_extruder2 = model_obj2->config.has("extruder");
if ((has_extruder1 != has_extruder2)
|| (has_extruder1 && model_obj1->config.extruder() != model_obj2->config.extruder()))
return false;
for (int index = 0; index < model_obj1->volumes.size(); index++) {
const ModelVolume &model_volume1 = *model_obj1->volumes[index];
const ModelVolume &model_volume2 = *model_obj2->volumes[index];
if (model_volume1.type() != model_volume2.type())
return false;
if (model_volume1.mesh_ptr() != model_volume2.mesh_ptr())
return false;
has_extruder1 = model_volume1.config.has("extruder");
has_extruder2 = model_volume2.config.has("extruder");
if ((has_extruder1 != has_extruder2)
|| (has_extruder1 && model_volume1.config.extruder() != model_volume2.config.extruder()))
return false;
if (!model_volume1.supported_facets.equals(model_volume2.supported_facets))
return false;
if (!model_volume1.seam_facets.equals(model_volume2.seam_facets))
return false;
if (!model_volume1.mmu_segmentation_facets.equals(model_volume2.mmu_segmentation_facets))
return false;
if (model_volume1.config.get() != model_volume2.config.get())
return false;
}
//if (!object1->config().equals(object2->config()))
// return false;
if (model_obj1->config.get() != model_obj2->config.get())
return false;
return true;
};
int object_count = m_objects.size();
std::set<PrintObject*> need_slicing_objects;
for (int index = 0; index < object_count; index++)
{
PrintObject *obj = m_objects[index];
for (PrintObject *slicing_obj : need_slicing_objects)
{
if (is_print_object_the_same(obj, slicing_obj)) {
obj->set_shared_object(slicing_obj);
break;
}
}
if (!obj->get_shared_object())
need_slicing_objects.insert(obj);
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": total object counts %1% in current print, need to slice %2%")%m_objects.size()%need_slicing_objects.size();
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
for (PrintObject *obj : m_objects) {
if (need_slicing_objects.count(obj) != 0) {
obj->make_perimeters();
}
else {
if (obj->set_started(posSlice))
obj->set_done(posSlice);
if (obj->set_started(posPerimeters))
obj->set_done(posPerimeters);
}
}
for (PrintObject *obj : m_objects) {
if (need_slicing_objects.count(obj) != 0) {
obj->infill();
}
else {
if (obj->set_started(posPrepareInfill))
obj->set_done(posPrepareInfill);
if (obj->set_started(posInfill))
obj->set_done(posInfill);
}
}
for (PrintObject *obj : m_objects) {
if (need_slicing_objects.count(obj) != 0) {
obj->ironing();
}
else {
if (obj->set_started(posIroning))
obj->set_done(posIroning);
}
}
for (PrintObject *obj : m_objects) {
if (need_slicing_objects.count(obj) != 0) {
obj->generate_support_material();
}
else {
if (obj->set_started(posSupportMaterial))
obj->set_done(posSupportMaterial);
}
}
for (PrintObject *obj : m_objects)
obj->make_perimeters();
for (PrintObject *obj : m_objects)
obj->infill();
for (PrintObject *obj : m_objects)
obj->ironing();
for (PrintObject *obj : m_objects)
obj->generate_support_material();
{
if (need_slicing_objects.count(obj) == 0)
obj->copy_layers_from_shared_object();
}
if (this->set_started(psWipeTower)) {
m_wipe_tower_data.clear();
m_tool_ordering.clear();
@ -1292,8 +1434,17 @@ void Print::process()
this->set_done(psSkirtBrim);
}
//BBS
for (PrintObject *obj : m_objects)
obj->simplify_extrusion_path();
for (PrintObject *obj : m_objects) {
if (need_slicing_objects.count(obj) != 0) {
obj->simplify_extrusion_path();
}
else {
if (obj->set_started(posSimplifyPath))
obj->set_done(posSimplifyPath);
if (obj->set_started(posSimplifySupportPath))
obj->set_done(posSimplifySupportPath);
}
}
BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info();
}
@ -1634,7 +1785,7 @@ void Print::_make_wipe_tower()
for (LayerTools& layer_tools : layer_tools_array) {
layer_tools.has_wipe_tower = true;
if (layer_tools.wipe_tower_partitions == 0) {
layer_tools.wipe_tower_partitions = 1;
layer_tools.wipe_tower_partitions = 1;
}
}
}

View file

@ -49,6 +49,12 @@ struct groupedVolumeSlices
ExPolygons slices;
};
enum SupportNecessaryType {
NoNeedSupp=0,
SharpTail,
LargeOverhang,
};
namespace FillAdaptive {
struct Octree;
struct OctreeDeleter;
@ -409,6 +415,11 @@ public:
//BBS
BoundingBox get_first_layer_bbox(float& area, float& layer_height, std::string& name);
PrintObject* get_shared_object() const { return m_shared_object; }
void set_shared_object(PrintObject *object);
void clear_shared_object();
void copy_layers_from_shared_object();
// BBS: Boundingbox of the first layer
BoundingBox firstLayerObjectBrimBoundingBox;
private:
@ -457,7 +468,7 @@ private:
std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> prepare_adaptive_infill_data();
// BBS
bool is_support_necessary();
SupportNecessaryType is_support_necessary();
// XYZ in scaled coordinates
Vec3crd m_size;
@ -489,6 +500,8 @@ private:
// BBS: per object skirt
ExtrusionEntityCollection m_skirt;
PrintObject* m_shared_object{ nullptr };
public:
//BBS: When printing multi-material objects, this settings will make slicer to clip the overlapping object parts one by the other.
//(2nd part will be clipped by the 1st, 3rd part will be clipped by the 1st and 2nd etc).

View file

@ -629,6 +629,8 @@ protected:
{ return m_state.invalidate_multiple(il.begin(), il.end(), PrintObjectBase::cancel_callback(m_print)); }
bool invalidate_all_steps()
{ return m_state.invalidate_all(PrintObjectBase::cancel_callback(m_print)); }
bool invalidate_all_steps_without_cancel()
{ return m_state.invalidate_all([](){}); }
bool is_step_started_unguarded(PrintObjectStepEnum step) const { return m_state.is_started_unguarded(step); }
bool is_step_done_unguarded(PrintObjectStepEnum step) const { return m_state.is_done_unguarded(step); }

View file

@ -230,7 +230,8 @@ static const t_config_enum_values s_keys_map_OverhangFanThreshold = {
static const t_config_enum_values s_keys_map_BedType = {
{ "Cool Plate", btPC },
{ "Engineering Plate", btEP },
{ "High Temp Plate", btPEI }
{ "High Temp Plate", btPEI },
{ "Textured PEI Plate", btPTE }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BedType)
@ -462,6 +463,16 @@ void PrintConfigDef::init_fff_params()
def->max = 120;
def->set_default_value(new ConfigOptionInts{ 45 });
def = this->add("textured_plate_temp", coInts);
def->label = L("Other layers");
def->tooltip = L("Bed temperature for layers except the initial one. "
"Value 0 means the filament does not support to print on the Textured PEI Plate");
def->sidetext = L("°C");
def->full_label = L("Bed temperature");
def->min = 0;
def->max = 120;
def->set_default_value(new ConfigOptionInts{45});
def = this->add("cool_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
@ -492,6 +503,15 @@ void PrintConfigDef::init_fff_params()
def->max = 120;
def->set_default_value(new ConfigOptionInts{ 45 });
def = this->add("textured_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
def->tooltip = L("Bed temperature of the initial layer. "
"Value 0 means the filament does not support to print on the Textured PEI Plate");
def->sidetext = L("°C");
def->max = 0;
def->max = 120;
def->set_default_value(new ConfigOptionInts{45});
def = this->add("curr_bed_type", coEnums);
def->label = L("Bed type");
@ -501,9 +521,11 @@ void PrintConfigDef::init_fff_params()
def->enum_values.emplace_back("Cool Plate");
def->enum_values.emplace_back("Engineering Plate");
def->enum_values.emplace_back("High Temp Plate");
def->enum_values.emplace_back("Textured PEI Plate");
def->enum_labels.emplace_back(L("Cool Plate"));
def->enum_labels.emplace_back(L("Engineering Plate"));
def->enum_labels.emplace_back(L("High Temp Plate"));
def->enum_labels.emplace_back(L("Textured PEI Plate"));
def->set_default_value(new ConfigOptionEnum<BedType>(btPC));
def = this->add("before_layer_change_gcode", coString);
@ -1091,7 +1113,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Filament price. For statistics only");
def->sidetext = L("money/kg");
def->min = 0;
def->mode = comDevelop;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 0. });
def = this->add("filament_settings_id", coStrings);
@ -2000,9 +2022,11 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("nearest");
def->enum_values.push_back("aligned");
def->enum_values.push_back("back");
def->enum_values.push_back("random");
def->enum_labels.push_back(L("Nearest"));
def->enum_labels.push_back(L("Aligned"));
def->enum_labels.push_back(L("Back"));
def->enum_labels.push_back(L("Random"));
def->mode = comSimple;
def->set_default_value(new ConfigOptionEnum<SeamPosition>(spAligned));
@ -2108,10 +2132,12 @@ void PrintConfigDef::init_fff_params()
def = this->add("timelapse_no_toolhead", coBool);
def->label = L("Timelapse");
def->tooltip = L("Record timelapse video of printing without showing toolhead. In this mode "
"the toolhead docks near the excess chute at each layer change, and then "
"a snapshot is taken with the chamber camera. When printing finishes a timelapse "
"video is composed of all the snapshots.");
def->tooltip = L("If enabled, a timelapse video will be generated for each print. "
"After each layer is printed, the toolhead will move to the excess chute, "
"and then a snapshot is taken with the chamber camera. "
"All of these snapshots are composed into a timelapse video when printing completes. "
"Since the melt filament may leak from the nozzle during the process of taking a snapshot, "
"prime tower is required for nozzle priming.");
def->mode = comSimple;
def->set_default_value(new ConfigOptionBool(false));
@ -2406,8 +2432,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("independent_support_layer_height", coBool);
def->label = L("Independent support layer height");
def->category = L("Support");
def->tooltip = L("Support layer uses layer height independent with object layer. This is to support custom support gap,"
"but may cause extra filament switches if support is specified as different extruder with object");
def->tooltip = L("Support layer uses layer height independent with object layer. This is to support customizing z-gap and save print time.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));

View file

@ -160,6 +160,7 @@ enum BedType {
btPC = 0,
btEP,
btPEI,
btPTE,
btCount
};
@ -185,6 +186,8 @@ static std::string bed_type_to_gcode_string(const BedType type)
case btPEI:
type_str = "high_temp_plate";
break;
case btPTE:
type_str = "frosted_plate";
default:
type_str = "unknown";
break;
@ -204,6 +207,9 @@ static std::string get_bed_temp_key(const BedType type)
if (type == btPEI)
return "hot_plate_temp";
if (type == btPTE)
return "textured_plate_temp";
return "";
}
@ -218,6 +224,9 @@ static std::string get_bed_temp_1st_layer_key(const BedType type)
if (type == btPEI)
return "hot_plate_temp_initial_layer";
if (type == btPTE)
return "textured_plate_temp_initial_layer";
return "";
}
@ -786,9 +795,11 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionInts, cool_plate_temp))
((ConfigOptionInts, eng_plate_temp))
((ConfigOptionInts, hot_plate_temp)) // hot is short for high temperature
((ConfigOptionInts, textured_plate_temp))
((ConfigOptionInts, cool_plate_temp_initial_layer))
((ConfigOptionInts, eng_plate_temp_initial_layer))
((ConfigOptionInts, hot_plate_temp_initial_layer)) // hot is short for high temperature
((ConfigOptionInts, textured_plate_temp_initial_layer))
((ConfigOptionBools, enable_overhang_bridge_fan))
((ConfigOptionInts, overhang_fan_speed))
((ConfigOptionEnumsGeneric, overhang_fan_threshold))

View file

@ -86,6 +86,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Transfor
PrintObject::~PrintObject()
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, m_shared_object %2%")%this%m_shared_object;
if (m_shared_regions && -- m_shared_regions->m_ref_cnt == 0) delete m_shared_regions;
clear_layers();
clear_support_layers();
@ -419,11 +420,17 @@ void PrintObject::generate_support_material()
m_print->throw_if_canceled();
} else {
// BBS: pop a warning if objects have significant amount of overhangs but support material is not enabled
if (this->is_support_necessary()) {
SupportNecessaryType sntype = this->is_support_necessary();
if (sntype != NoNeedSupp) {
m_print->set_status(50, L("Checking support necessity"));
std::string warning_message = format(L("It seems object %s needs support to print. Please enable support generation."), this->model_object()->name);
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning_message, PrintStateBase::SlicingNeedSupportOn);
if (sntype == SharpTail) {
std::string warning_message = format(L("It seems object %s has completely floating regions. Please re-orient the object or enable support generation."),
this->model_object()->name);
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning_message, PrintStateBase::SlicingNeedSupportOn);
} else {
std::string warning_message = format(L("It seems object %s has large overhangs. Please enable support generation."), this->model_object()->name);
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning_message, PrintStateBase::SlicingNeedSupportOn);
}
}
#if 0
@ -529,9 +536,11 @@ std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> PrintObject::prepare
void PrintObject::clear_layers()
{
for (Layer *l : m_layers)
delete l;
m_layers.clear();
if (!m_shared_object) {
for (Layer *l : m_layers)
delete l;
m_layers.clear();
}
}
Layer* PrintObject::add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z)
@ -567,9 +576,11 @@ SupportLayer* PrintObject::get_support_layer_at_printz(coordf_t print_z, coordf_
void PrintObject::clear_tree_support_layers()
{
for (TreeSupportLayer* l : m_tree_support_layers)
delete l;
m_tree_support_layers.clear();
if (!m_shared_object) {
for (TreeSupportLayer* l : m_tree_support_layers)
delete l;
m_tree_support_layers.clear();
}
}
std::shared_ptr<TreeSupportData> PrintObject::alloc_tree_support_preview_cache()
@ -596,9 +607,11 @@ TreeSupportLayer* PrintObject::add_tree_support_layer(int id, coordf_t height, c
void PrintObject::clear_support_layers()
{
for (Layer *l : m_support_layers)
delete l;
m_support_layers.clear();
if (!m_shared_object) {
for (Layer *l : m_support_layers)
delete l;
m_support_layers.clear();
}
}
SupportLayer* PrintObject::add_support_layer(int id, int interface_id, coordf_t height, coordf_t print_z)
@ -2448,7 +2461,7 @@ template void PrintObject::remove_bridges_from_contacts<Polygons>(
float max_bridge_length, bool break_bridge);
bool PrintObject::is_support_necessary()
SupportNecessaryType PrintObject::is_support_necessary()
{
static const double super_overhang_area_threshold = SQ(scale_(5.0));
@ -2471,7 +2484,7 @@ bool PrintObject::is_support_necessary()
for (const ExPolygon& expoly : layerm->raw_slices) {
// detect sharp tail
if (intersection_ex({ expoly }, lower_layer_offseted).empty())
return true;
return SharpTail;
}
}
@ -2503,18 +2516,19 @@ bool PrintObject::is_support_necessary()
double super_overhang_area = 0.0;
for (Polygon& poly : super_overhang_polys) {
bool is_ccw = poly.is_counter_clockwise();
double area_ = poly.area();
if (is_ccw) {
if (super_overhang_area > super_overhang_area_threshold)
return true;
super_overhang_area = poly.area();
if (area_ > super_overhang_area_threshold)
return LargeOverhang;
super_overhang_area += area_;
}
else {
super_overhang_area -= poly.area();
super_overhang_area -= area_;
}
}
if (super_overhang_area > super_overhang_area_threshold)
return true;
//if (super_overhang_area > super_overhang_area_threshold)
// return LargeOverhang;
// 3. check overhang distance
const double distance_threshold_scaled = extrusion_width_scaled * 2;
@ -2529,10 +2543,10 @@ bool PrintObject::is_support_necessary()
}),
exceed_overhang.end());
if (!exceed_overhang.empty())
return true;
return LargeOverhang;
}
return false;
return NoNeedSupp;
}
static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const indexed_triangle_set &custom_facets, const Transform3f &tr, bool seam, std::vector<Polygons> &out)

View file

@ -474,9 +474,7 @@ std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std
if (layers[idx_layer]->slicing_errors) {
buggy_layers.push_back(idx_layer);
//BBS
error_msg = L("Empty layers around bottom are replaced by nearest normal layers.");
}
}
else
break; // only detect empty layers near bed
}
@ -484,14 +482,15 @@ std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - begin";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, buggy_layers.size()),
[&layers, &throw_if_canceled, &buggy_layers](const tbb::blocked_range<size_t>& range) {
[&layers, &throw_if_canceled, &buggy_layers, &error_msg](const tbb::blocked_range<size_t>& range) {
for (size_t buggy_layer_idx = range.begin(); buggy_layer_idx < range.end(); ++ buggy_layer_idx) {
throw_if_canceled();
size_t idx_layer = buggy_layers[buggy_layer_idx];
// BBS: only replace empty first layer
if (idx_layer > 0)
// BBS: only replace empty layers lower than 1mm
const coordf_t thresh_empty_layer_height = 1;
Layer* layer = layers[idx_layer];
if (layer->print_z>= thresh_empty_layer_height)
continue;
Layer *layer = layers[idx_layer];
assert(layer->slicing_errors);
// Try to repair the layer surfaces by merging all contours and all holes from neighbor layers.
// BOOST_LOG_TRIVIAL(trace) << "Attempting to repair layer" << idx_layer;
@ -500,42 +499,39 @@ std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std
// Find the first valid layer below / above the current layer.
const Surfaces *upper_surfaces = nullptr;
const Surfaces *lower_surfaces = nullptr;
//BBS: only repair first layer if the 2nd layer is Good
for (size_t j = idx_layer + 1; j < /*layers.size()*/2; ++ j)
if (! layers[j]->slicing_errors) {
//BBS: only repair empty layers lowers than 1mm
for (size_t j = idx_layer + 1; j < layers.size(); ++j) {
if (!layers[j]->slicing_errors) {
upper_surfaces = &layers[j]->regions()[region_id]->slices.surfaces;
break;
}
for (int j = /*int(idx_layer) -*/ 1; j >= 0; -- j)
if (! layers[j]->slicing_errors) {
if (layers[j]->print_z >= thresh_empty_layer_height) break;
}
for (int j = int(idx_layer) - 1; j >= 0; --j) {
if (layers[j]->print_z >= thresh_empty_layer_height) continue;
if (!layers[j]->slicing_errors) {
lower_surfaces = &layers[j]->regions()[region_id]->slices.surfaces;
break;
}
}
// Collect outer contours and holes from the valid layers above & below.
Polygons outer;
outer.reserve(
ExPolygons expolys;
expolys.reserve(
((upper_surfaces == nullptr) ? 0 : upper_surfaces->size()) +
((lower_surfaces == nullptr) ? 0 : lower_surfaces->size()));
size_t num_holes = 0;
if (upper_surfaces)
for (const auto &surface : *upper_surfaces) {
outer.push_back(surface.expolygon.contour);
num_holes += surface.expolygon.holes.size();
expolys.emplace_back(surface.expolygon);
}
if (lower_surfaces)
for (const auto &surface : *lower_surfaces) {
outer.push_back(surface.expolygon.contour);
num_holes += surface.expolygon.holes.size();
expolys.emplace_back(surface.expolygon);
}
Polygons holes;
holes.reserve(num_holes);
if (upper_surfaces)
for (const auto &surface : *upper_surfaces)
polygons_append(holes, surface.expolygon.holes);
if (lower_surfaces)
for (const auto &surface : *lower_surfaces)
polygons_append(holes, surface.expolygon.holes);
layerm->slices.set(diff_ex(union_(outer), holes), stInternal);
if (!expolys.empty()) {
//BBS
error_msg = L("Empty layers around bottom are replaced by nearest normal layers.");
layerm->slices.set(union_ex(expolys), stInternal);
}
}
// Update layer slices after repairing the single regions.
layer->make_slices();
@ -555,8 +551,7 @@ std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std
//BBS
if(error_msg.empty() && !buggy_layers.empty())
error_msg = L("The model has overlapping or self-intersecting facets. I tried to repair it, "
"however you might want to check the results or repair the input file and retry.");
error_msg = L("The model has too many empty layers.");
return error_msg;
}

View file

@ -51,6 +51,7 @@ public:
{
profile_ = profile;
prediction = 0;
weight = 0.0f;
}
BBLSliceInfo(const BBLSliceInfo& obj) {

View file

@ -0,0 +1,203 @@
#include "../libslic3r.h"
#include "../Model.hpp"
#include "../TriangleMesh.hpp"
#include "TextShape.hpp"
#include <string>
#include <vector>
#include "Standard_TypeDef.hxx"
#include "STEPCAFControl_Reader.hxx"
#include "BRepMesh_IncrementalMesh.hxx"
#include "Interface_Static.hxx"
#include "XCAFDoc_DocumentTool.hxx"
#include "XCAFDoc_ShapeTool.hxx"
#include "XCAFApp_Application.hxx"
#include "TopoDS_Solid.hxx"
#include "TopoDS_Compound.hxx"
#include "TopoDS_Builder.hxx"
#include "TopoDS.hxx"
#include "TDataStd_Name.hxx"
#include "BRepBuilderAPI_Transform.hxx"
#include "TopExp_Explorer.hxx"
#include "TopExp_Explorer.hxx"
#include "BRep_Tool.hxx"
#include "Font_BRepFont.hxx"
#include "Font_BRepTextBuilder.hxx"
#include "BRepPrimAPI_MakePrism.hxx"
#include "Font_FontMgr.hxx"
namespace Slic3r {
std::vector<std::string> init_occt_fonts()
{
std::vector<std::string> stdFontNames;
Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
aFontMgr->InitFontDataBase();
TColStd_SequenceOfHAsciiString availFontNames;
aFontMgr->GetAvailableFontsNames(availFontNames);
stdFontNames.reserve(availFontNames.Size());
for (auto afn : availFontNames)
stdFontNames.push_back(afn->ToCString());
return stdFontNames;
}
static bool TextToBRep(const char* text, const char* font, const float theTextHeight, Font_FontAspect& theFontAspect, TopoDS_Shape& theShape)
{
Standard_Integer anArgIt = 1;
Standard_CString aName = "text_shape";
Standard_CString aText = text;
Font_BRepFont aFont;
//TCollection_AsciiString aFontName("Courier");
TCollection_AsciiString aFontName(font);
Standard_Real aTextHeight = theTextHeight;
Font_FontAspect aFontAspect = theFontAspect;
Standard_Boolean anIsCompositeCurve = Standard_False;
gp_Ax3 aPenAx3(gp::XOY());
gp_Dir aNormal(0.0, 0.0, 1.0);
gp_Dir aDirection(1.0, 0.0, 0.0);
gp_Pnt aPenLoc;
Graphic3d_HorizontalTextAlignment aHJustification = Graphic3d_HTA_LEFT;
Graphic3d_VerticalTextAlignment aVJustification = Graphic3d_VTA_BOTTOM;
Font_StrictLevel aStrictLevel = Font_StrictLevel_Any;
aFont.SetCompositeCurveMode(anIsCompositeCurve);
if (!aFont.FindAndInit(aFontName.ToCString(), aFontAspect, aTextHeight, aStrictLevel))
return false;
aPenAx3 = gp_Ax3(aPenLoc, aNormal, aDirection);
Font_BRepTextBuilder aBuilder;
theShape = aBuilder.Perform(aFont, aText, aPenAx3, aHJustification, aVJustification);
return true;
}
static bool Prism(const TopoDS_Shape& theBase, const float thickness, TopoDS_Shape& theSolid)
{
if (theBase.IsNull()) return false;
gp_Vec V(0.f, 0.f, thickness);
BRepPrimAPI_MakePrism* Prism = new BRepPrimAPI_MakePrism(theBase, V, Standard_False);
theSolid = Prism->Shape();
return true;
}
static void MakeMesh(TopoDS_Shape& theSolid, TriangleMesh& theMesh)
{
const double STEP_TRANS_CHORD_ERROR = 0.005;
const double STEP_TRANS_ANGLE_RES = 1;
BRepMesh_IncrementalMesh mesh(theSolid, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true);
int aNbNodes = 0;
int aNbTriangles = 0;
for (TopExp_Explorer anExpSF(theSolid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(anExpSF.Current()), aLoc);
if (!aTriangulation.IsNull()) {
aNbNodes += aTriangulation->NbNodes();
aNbTriangles += aTriangulation->NbTriangles();
}
}
stl_file stl;
stl.stats.type = inmemory;
stl.stats.number_of_facets = (uint32_t)aNbTriangles;
stl.stats.original_num_facets = stl.stats.number_of_facets;
stl_allocate(&stl);
std::vector<Vec3f> points;
points.reserve(aNbNodes);
//BBS: count faces missing triangulation
Standard_Integer aNbFacesNoTri = 0;
//BBS: fill temporary triangulation
Standard_Integer aNodeOffset = 0;
Standard_Integer aTriangleOffet = 0;
for (TopExp_Explorer anExpSF(theSolid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
const TopoDS_Shape& aFace = anExpSF.Current();
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(aFace), aLoc);
if (aTriangulation.IsNull()) {
++aNbFacesNoTri;
continue;
}
//BBS: copy nodes
gp_Trsf aTrsf = aLoc.Transformation();
for (Standard_Integer aNodeIter = 1; aNodeIter <= aTriangulation->NbNodes(); ++aNodeIter) {
gp_Pnt aPnt = aTriangulation->Node(aNodeIter);
aPnt.Transform(aTrsf);
points.emplace_back(std::move(Vec3f(aPnt.X(), aPnt.Y(), aPnt.Z())));
}
//BBS: copy triangles
const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation();
for (Standard_Integer aTriIter = 1; aTriIter <= aTriangulation->NbTriangles(); ++aTriIter) {
Poly_Triangle aTri = aTriangulation->Triangle(aTriIter);
Standard_Integer anId[3];
aTri.Get(anId[0], anId[1], anId[2]);
if (anOrientation == TopAbs_REVERSED) {
//BBS: swap 1, 2.
Standard_Integer aTmpIdx = anId[1];
anId[1] = anId[2];
anId[2] = aTmpIdx;
}
//BBS: Update nodes according to the offset.
anId[0] += aNodeOffset;
anId[1] += aNodeOffset;
anId[2] += aNodeOffset;
//BBS: save triangles facets
stl_facet facet;
facet.vertex[0] = points[anId[0] - 1].cast<float>();
facet.vertex[1] = points[anId[1] - 1].cast<float>();
facet.vertex[2] = points[anId[2] - 1].cast<float>();
facet.extra[0] = 0;
facet.extra[1] = 0;
stl_normal normal;
stl_calculate_normal(normal, &facet);
stl_normalize_vector(normal);
facet.normal = normal;
stl.facet_start[aTriangleOffet + aTriIter - 1] = facet;
}
aNodeOffset += aTriangulation->NbNodes();
aTriangleOffet += aTriangulation->NbTriangles();
}
theMesh.from_stl(stl);
}
void load_text_shape(const char*text, const char* font, const float text_height, const float thickness, bool is_bold, bool is_italic, TriangleMesh& text_mesh)
{
Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
if (aFontMgr->GetAvailableFonts().IsEmpty())
aFontMgr->InitFontDataBase();
TopoDS_Shape aTextBase;
Font_FontAspect aFontAspect = Font_FontAspect_UNDEFINED;
if (is_bold && is_italic)
aFontAspect = Font_FontAspect_BoldItalic;
else if (is_bold)
aFontAspect = Font_FontAspect_Bold;
else if (is_italic)
aFontAspect = Font_FontAspect_Italic;
else
aFontAspect = Font_FontAspect_Regular;
if (!TextToBRep(text, font, text_height, aFontAspect, aTextBase))
return;
TopoDS_Shape aTextShape;
if (!Prism(aTextBase, thickness, aTextShape))
return;
MakeMesh(aTextShape, text_mesh);
}
}; // namespace Slic3r

View file

@ -0,0 +1,12 @@
#ifndef slic3r_Text_Shape_hpp_
#define slic3r_Text_Shape_hpp_
namespace Slic3r {
class TriangleMesh;
extern std::vector<std::string> init_occt_fonts();
extern void load_text_shape(const char* text, const char* font, const float text_height, const float thickness, bool is_bold, bool is_italic, TriangleMesh& text_mesh);
}; // namespace Slic3r
#endif // slic3r_Text_Shape_hpp_

View file

@ -0,0 +1,183 @@
#include "ShortEdgeCollapse.hpp"
#include "libslic3r/NormalUtils.hpp"
#include <unordered_map>
#include <unordered_set>
#include <random>
#include <algorithm>
namespace Slic3r {
void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_count) {
// whenever vertex is removed, its mapping is update to the index of vertex with wich it merged
std::vector<size_t> vertices_index_mapping(mesh.vertices.size());
for (size_t idx = 0; idx < vertices_index_mapping.size(); ++idx) {
vertices_index_mapping[idx] = idx;
}
// Algorithm uses get_final_index query to get the actual vertex index. The query also updates all mappings on the way, essentially flattening the mapping
std::vector<size_t> flatten_queue;
auto get_final_index = [&vertices_index_mapping, &flatten_queue](const size_t &orig_index) {
flatten_queue.clear();
size_t idx = orig_index;
while (vertices_index_mapping[idx] != idx) {
flatten_queue.push_back(idx);
idx = vertices_index_mapping[idx];
}
for (size_t i : flatten_queue) {
vertices_index_mapping[i] = idx;
}
return idx;
};
// if face is removed, mark it here
std::vector<bool> face_removal_flags(mesh.indices.size(), false);
std::vector<Vec3i> triangles_neighbors = its_face_neighbors_par(mesh);
// now compute vertices dot product - this is used during edge collapse,
// to determine which vertex to remove and which to keep; We try to keep the one with larger angle, because it defines the shape "more".
// The min vertex dot product is lowest dot product of its normal with the normals of faces around it.
// the lower the dot product, the more we want to keep the vertex
// NOTE: This score is not updated, even though the decimation does change the mesh. It saves computation time, and there are no strong reasons to update.
std::vector<float> min_vertex_dot_product(mesh.vertices.size(), 1);
{
std::vector<Vec3f> face_normals = its_face_normals(mesh);
std::vector<Vec3f> vertex_normals = NormalUtils::create_normals(mesh);
for (size_t face_idx = 0; face_idx < mesh.indices.size(); ++face_idx) {
Vec3i t = mesh.indices[face_idx];
Vec3f n = face_normals[face_idx];
min_vertex_dot_product[t[0]] = std::min(min_vertex_dot_product[t[0]], n.dot(vertex_normals[t[0]]));
min_vertex_dot_product[t[1]] = std::min(min_vertex_dot_product[t[1]], n.dot(vertex_normals[t[1]]));
min_vertex_dot_product[t[2]] = std::min(min_vertex_dot_product[t[2]], n.dot(vertex_normals[t[2]]));
}
}
// lambda to remove face. It flags the face as removed, and updates neighbourhood info
auto remove_face = [&triangles_neighbors, &face_removal_flags](int face_idx, int other_face_idx) {
if (face_idx < 0) {
return;
}
face_removal_flags[face_idx] = true;
Vec3i neighbors = triangles_neighbors[face_idx];
int n_a = neighbors[0] != other_face_idx ? neighbors[0] : neighbors[1];
int n_b = neighbors[2] != other_face_idx ? neighbors[2] : neighbors[1];
if (n_a > 0)
for (int &n : triangles_neighbors[n_a]) {
if (n == face_idx) {
n = n_b;
break;
}
}
if (n_b > 0)
for (int &n : triangles_neighbors[n_b]) {
if (n == face_idx) {
n = n_a;
break;
}
}
};
std::mt19937_64 generator { 27644437 };// default constant seed! so that results are deterministic
std::vector<size_t> face_indices(mesh.indices.size());
for (size_t idx = 0; idx < face_indices.size(); ++idx) {
face_indices[idx] = idx;
}
//tmp face indices used only for swapping
std::vector<size_t> tmp_face_indices(mesh.indices.size());
float decimation_ratio = 1.0f; // decimation ratio updated in each iteration. it is number of removed triangles / number of all
float edge_len = 0.2f; // Allowed collapsible edge size. Starts low, but is gradually increased
while (face_indices.size() > target_triangle_count) {
// simpple func to increase the edge len - if decimation ratio is low, it increases the len up to twice, if decimation ratio is high, increments are low
edge_len = edge_len * (1.0f + 1.0 - decimation_ratio);
float max_edge_len_squared = edge_len * edge_len;
//shuffle the faces and traverse in random order, this MASSIVELY improves the quality of the result
std::shuffle(face_indices.begin(), face_indices.end(), generator);
for (const size_t &face_idx : face_indices) {
if (face_removal_flags[face_idx]) {
// if face already removed from previous collapses, skip (each collapse removes two triangles [at least] )
continue;
}
// look at each edge if it is good candidate for collapse
for (size_t edge_idx = 0; edge_idx < 3; ++edge_idx) {
size_t vertex_index_keep = get_final_index(mesh.indices[face_idx][edge_idx]);
size_t vertex_index_remove = get_final_index(mesh.indices[face_idx][(edge_idx + 1) % 3]);
//check distance, skip long edges
if ((mesh.vertices[vertex_index_keep] - mesh.vertices[vertex_index_remove]).squaredNorm()
> max_edge_len_squared) {
continue;
}
// swap indexes if vertex_index_keep has higher dot product (we want to keep low dot product vertices)
if (min_vertex_dot_product[vertex_index_remove] < min_vertex_dot_product[vertex_index_keep]) {
size_t tmp = vertex_index_keep;
vertex_index_keep = vertex_index_remove;
vertex_index_remove = tmp;
}
//remove vertex
{
// map its index to the index of the kept vertex
vertices_index_mapping[vertex_index_remove] = vertices_index_mapping[vertex_index_keep];
}
int neighbor_to_remove_face_idx = triangles_neighbors[face_idx][edge_idx];
// remove faces
remove_face(face_idx, neighbor_to_remove_face_idx);
remove_face(neighbor_to_remove_face_idx, face_idx);
// break. this triangle is done
break;
}
}
// filter face_indices, remove those that have been collapsed
size_t prev_size = face_indices.size();
tmp_face_indices.clear();
for (size_t face_idx : face_indices) {
if (!face_removal_flags[face_idx]){
tmp_face_indices.push_back(face_idx);
}
}
face_indices.swap(tmp_face_indices);
decimation_ratio = float(prev_size - face_indices.size()) / float(prev_size);
//std::cout << " DECIMATION RATIO: " << decimation_ratio << std::endl;
}
//Extract the result mesh
std::unordered_map<size_t, size_t> final_vertices_mapping;
std::vector<Vec3f> final_vertices;
std::vector<Vec3i> final_indices;
final_indices.reserve(face_indices.size());
for (size_t idx : face_indices) {
Vec3i final_face;
for (size_t i = 0; i < 3; ++i) {
final_face[i] = get_final_index(mesh.indices[idx][i]);
}
if (final_face[0] == final_face[1] || final_face[1] == final_face[2] || final_face[2] == final_face[0]) {
continue; // discard degenerate triangles
}
for (size_t i = 0; i < 3; ++i) {
if (final_vertices_mapping.find(final_face[i]) == final_vertices_mapping.end()) {
final_vertices_mapping[final_face[i]] = final_vertices.size();
final_vertices.push_back(mesh.vertices[final_face[i]]);
}
final_face[i] = final_vertices_mapping[final_face[i]];
}
final_indices.push_back(final_face);
}
mesh.vertices = final_vertices;
mesh.indices = final_indices;
}
} //namespace Slic3r

View file

@ -0,0 +1,16 @@
#ifndef SRC_LIBSLIC3R_SHORTEDGECOLLAPSE_HPP_
#define SRC_LIBSLIC3R_SHORTEDGECOLLAPSE_HPP_
#include "libslic3r/TriangleMesh.hpp"
namespace Slic3r{
// Decimates the model by collapsing short edges. It starts with very small edges and gradually increases the collapsible length,
// until the target triangle count is reached (the algorithm will certainly undershoot the target count, result will have less triangles than target count)
// The algorithm does not check for triangle flipping, disconnections, self intersections or any other degeneration that can appear during mesh processing.
void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_count);
}
#endif /* SRC_LIBSLIC3R_SHORTEDGECOLLAPSE_HPP_ */

View file

@ -1421,6 +1421,15 @@ void TreeSupport::generate_toolpaths()
bool obj_is_vertical = obj_size.x() < obj_size.y();
int num_layers_to_change_infill_direction = int(HEIGHT_TO_SWITCH_INFILL_DIRECTION / object_config.layer_height.value); // change direction every 30mm
std::shared_ptr<Fill> filler_interface = std::shared_ptr<Fill>(Fill::new_from_type(m_support_params.contact_fill_pattern));
std::shared_ptr<Fill> filler_Roof1stLayer = std::shared_ptr<Fill>(Fill::new_from_type(ipRectilinear));
std::shared_ptr<Fill> filler_support = std::shared_ptr<Fill>(Fill::new_from_type(m_support_params.base_fill_pattern));
filler_interface->set_bounding_box(bbox_object);
filler_Roof1stLayer->set_bounding_box(bbox_object);
filler_support->set_bounding_box(bbox_object);
filler_interface->angle = Geometry::deg2rad(object_config.support_angle.value + 90.);//(1 - obj_is_vertical) * M_PI_2;//((1-obj_is_vertical) + int(layer_id / num_layers_to_change_infill_direction)) * M_PI_2;;//layer_id % 2 ? 0 : M_PI_2;
filler_Roof1stLayer->angle = Geometry::deg2rad(object_config.support_angle.value + 90.);
// generate tree support tool paths
tbb::parallel_for(
tbb::blocked_range<size_t>(m_raft_layers, m_object->tree_support_layer_count()),
@ -1434,12 +1443,7 @@ void TreeSupport::generate_toolpaths()
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_id);
Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
std::unique_ptr<Fill> filler_interface = std::unique_ptr<Fill>(Fill::new_from_type(m_support_params.contact_fill_pattern));
std::unique_ptr<Fill> filler_support = std::unique_ptr<Fill>(Fill::new_from_type(m_support_params.base_fill_pattern));
filler_interface->set_bounding_box(bbox_object);
filler_support->set_bounding_box(bbox_object);
filler_interface->angle = Geometry::deg2rad(object_config.support_angle.value + 90.);//(1 - obj_is_vertical) * M_PI_2;//((1-obj_is_vertical) + int(layer_id / num_layers_to_change_infill_direction)) * M_PI_2;;//layer_id % 2 ? 0 : M_PI_2;
for (auto& area_group : ts_layer->area_groups) {
ExPolygon& poly = *area_group.first;
@ -1465,9 +1469,9 @@ void TreeSupport::generate_toolpaths()
// roof_1st_layer
fill_params.density = interface_density;
// Note: spacing means the separation between two lines as if they are tightly extruded
filler_interface->spacing = m_support_material_interface_flow.spacing();
fill_expolygons_generate_paths(ts_layer->support_fills.entities, std::move(polys), filler_interface.get(), fill_params, erSupportMaterial,
m_support_material_interface_flow);
filler_Roof1stLayer->spacing = m_support_material_interface_flow.spacing();
fill_expolygons_generate_paths(ts_layer->support_fills.entities, std::move(polys), filler_Roof1stLayer.get(), fill_params, erSupportMaterial,
m_support_material_interface_flow);
} else if (area_group.second == TreeSupportLayer::FloorType) {
// floor_areas
fill_params.density = bottom_interface_density;
@ -2670,7 +2674,7 @@ void TreeSupport::adjust_layer_heights(std::vector<std::vector<Node*>>& contact_
for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) {
std::vector<Node*>& curr_layer_nodes = contact_nodes[layer_nr];
for (Node* node : curr_layer_nodes) {
if (node->support_roof_layers_below == top_intf_layers || node->support_floor_layers_above == bot_intf_layers) {
if (node->support_roof_layers_below >0 || node->support_floor_layers_above == bot_intf_layers) {
extremes.push_back(layer_nr);
break;
}

View file

@ -9,7 +9,7 @@
#include "Execution/ExecutionTBB.hpp"
#include "Execution/ExecutionSeq.hpp"
#include "Utils.hpp"
#include "Format/STL.hpp"
#include <libqhullcpp/Qhull.h>
#include <libqhullcpp/QhullFacetList.h>
#include <libqhullcpp/QhullVertexSet.h>
@ -208,10 +208,10 @@ bool TriangleMesh::from_stl(stl_file& stl, bool repair)
return true;
}
bool TriangleMesh::ReadSTLFile(const char* input_file, bool repair)
bool TriangleMesh::ReadSTLFile(const char* input_file, bool repair, ImportstlProgressFn stlFn)
{
stl_file stl;
if (! stl_open(&stl, input_file))
if (! stl_open(&stl, input_file, stlFn))
return false;
return from_stl(stl, repair);
}

View file

@ -10,7 +10,7 @@
#include "Point.hpp"
#include "Polygon.hpp"
#include "ExPolygon.hpp"
#include "Format/STL.hpp"
namespace Slic3r {
class TriangleMesh;
@ -94,7 +94,7 @@ public:
explicit TriangleMesh(indexed_triangle_set &&M, const RepairedMeshErrors& repaired_errors = RepairedMeshErrors());
void clear() { this->its.clear(); this->m_stats.clear(); }
bool from_stl(stl_file& stl, bool repair = true);
bool ReadSTLFile(const char* input_file, bool repair = true);
bool ReadSTLFile(const char* input_file, bool repair = true, ImportstlProgressFn stlFn = nullptr);
bool write_ascii(const char* output_file);
bool write_binary(const char* output_file);
float volume();
@ -152,10 +152,14 @@ public:
const TriangleMeshStats& stats() const { return m_stats; }
void set_init_shift(const Vec3d &offset) { m_init_shift = offset; }
Vec3d get_init_shift() const { return m_init_shift; }
indexed_triangle_set its;
private:
TriangleMeshStats m_stats;
Vec3d m_init_shift {0.0, 0.0, 0.0};
};
// Index of face indices incident with a vertex index.

View file

@ -1427,7 +1427,7 @@ void TriangleSelector::get_facets(std::vector<indexed_triangle_set>& facets_per_
{
facets_per_type.clear();
for (int type = (int)EnforcerBlockerType::NONE; type < (int)EnforcerBlockerType::Extruder15; type++) {
for (int type = (int)EnforcerBlockerType::NONE; type <= (int)EnforcerBlockerType::ExtruderMax; type++) {
facets_per_type.emplace_back();
indexed_triangle_set& its = facets_per_type.back();
std::vector<int> vertex_map(m_vertices.size(), -1);

View file

@ -7,12 +7,10 @@
#include <cfloat>
#include "Point.hpp"
#include "TriangleMesh.hpp"
#include "libslic3r/Model.hpp"
namespace Slic3r {
enum class EnforcerBlockerType : int8_t;
// Following class holds information about selected triangles. It also has power
// to recursively subdivide the triangles and make the selection finer.
class TriangleSelector
@ -275,7 +273,7 @@ public:
std::pair<std::vector<std::pair<int, int>>, std::vector<bool>> serialize() const;
// Load serialized data. Assumes that correct mesh is loaded.
void deserialize(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>>& data, bool needs_reset = true, EnforcerBlockerType max_ebt = (EnforcerBlockerType)std::numeric_limits<int8_t>::max());
void deserialize(const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>>& data, bool needs_reset = true, EnforcerBlockerType max_ebt = EnforcerBlockerType::ExtruderMax);
// For all triangles, remove the flag indicating that the triangle was selected by seed fill.
void seed_fill_unselect_all_triangles();
@ -312,7 +310,7 @@ protected:
void set_division(int sides_to_split, int special_side_idx);
// Get/set current state.
void set_state(EnforcerBlockerType type) { assert(! is_split()); state = type; }
void set_state(EnforcerBlockerType type) { assert(!is_split()); state = type; }
EnforcerBlockerType get_state() const { assert(! is_split()); return state; }
// Set if the triangle has been selected or unselected by seed fill.

View file

@ -0,0 +1,71 @@
#include "TriangleSetSampling.hpp"
#include <map>
#include <random>
#include <tbb/parallel_for.h>
#include <tbb/blocked_range.h>
namespace Slic3r {
TriangleSetSamples sample_its_uniform_parallel(size_t samples_count, const indexed_triangle_set &triangle_set) {
std::vector<double> triangles_area(triangle_set.indices.size());
tbb::parallel_for(tbb::blocked_range<size_t>(0, triangle_set.indices.size()),
[&triangle_set, &triangles_area](
tbb::blocked_range<size_t> r) {
for (size_t t_idx = r.begin(); t_idx < r.end(); ++t_idx) {
const Vec3f &a = triangle_set.vertices[triangle_set.indices[t_idx].x()];
const Vec3f &b = triangle_set.vertices[triangle_set.indices[t_idx].y()];
const Vec3f &c = triangle_set.vertices[triangle_set.indices[t_idx].z()];
double area = double(0.5 * (b - a).cross(c - a).norm());
triangles_area[t_idx] = area;
}
});
std::map<double, size_t> area_sum_to_triangle_idx;
float area_sum = 0;
for (size_t t_idx = 0; t_idx < triangles_area.size(); ++t_idx) {
area_sum += triangles_area[t_idx];
area_sum_to_triangle_idx[area_sum] = t_idx;
}
std::mt19937_64 mersenne_engine { 27644437 };
// random numbers on interval [0, 1)
std::uniform_real_distribution<double> fdistribution;
auto get_random = [&fdistribution, &mersenne_engine]() {
return Vec3d { fdistribution(mersenne_engine), fdistribution(mersenne_engine), fdistribution(mersenne_engine) };
};
std::vector<Vec3d> random_samples(samples_count);
std::generate(random_samples.begin(), random_samples.end(), get_random);
TriangleSetSamples result;
result.total_area = area_sum;
result.positions.resize(samples_count);
result.normals.resize(samples_count);
result.triangle_indices.resize(samples_count);
tbb::parallel_for(tbb::blocked_range<size_t>(0, samples_count),
[&triangle_set, &area_sum_to_triangle_idx, &area_sum, &random_samples, &result](
tbb::blocked_range<size_t> r) {
for (size_t s_idx = r.begin(); s_idx < r.end(); ++s_idx) {
double t_sample = random_samples[s_idx].x() * area_sum;
size_t t_idx = area_sum_to_triangle_idx.upper_bound(t_sample)->second;
double sq_u = std::sqrt(random_samples[s_idx].y());
double v = random_samples[s_idx].z();
Vec3f A = triangle_set.vertices[triangle_set.indices[t_idx].x()];
Vec3f B = triangle_set.vertices[triangle_set.indices[t_idx].y()];
Vec3f C = triangle_set.vertices[triangle_set.indices[t_idx].z()];
result.positions[s_idx] = A * (1 - sq_u) + B * (sq_u * (1 - v)) + C * (v * sq_u);
result.normals[s_idx] = ((B - A).cross(C - B)).normalized();
result.triangle_indices[s_idx] = t_idx;
}
});
return result;
}
}

View file

@ -0,0 +1,20 @@
#ifndef SRC_LIBSLIC3R_TRIANGLESETSAMPLING_HPP_
#define SRC_LIBSLIC3R_TRIANGLESETSAMPLING_HPP_
#include <admesh/stl.h>
#include "libslic3r/Point.hpp"
namespace Slic3r {
struct TriangleSetSamples {
float total_area;
std::vector<Vec3f> positions;
std::vector<Vec3f> normals;
std::vector<size_t> triangle_indices;
};
TriangleSetSamples sample_its_uniform_parallel(size_t samples_count, const indexed_triangle_set &triangle_set);
}
#endif /* SRC_LIBSLIC3R_TRIANGLESETSAMPLING_HPP_ */

View file

@ -283,6 +283,7 @@ inline typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER
}
extern std::string xml_escape(std::string text, bool is_marked = false);
extern std::string xml_unescape(std::string text);
#if defined __GNUC__ && __GNUC__ < 5 && !defined __clang__

View file

@ -24,6 +24,13 @@
#include <cmath>
#include <type_traits>
#ifdef _WIN32
// On MSVC, std::deque degenerates to a list of pointers, which defeats its purpose of reducing allocator load and memory fragmentation.
// https://github.com/microsoft/STL/issues/147#issuecomment-1090148740
// Thus it is recommended to use boost::container::deque instead.
#include <boost/container/deque.hpp>
#endif // _WIN32
#include "Technologies.hpp"
#include "Semver.hpp"
@ -96,6 +103,16 @@ namespace Slic3r {
extern Semver SEMVER;
// On MSVC, std::deque degenerates to a list of pointers, which defeats its purpose of reducing allocator load and memory fragmentation.
template<class T, class Allocator = std::allocator<T>>
using deque =
#ifdef _WIN32
// Use boost implementation, which allocates blocks of 512 bytes instead of blocks of 8 bytes.
boost::container::deque<T, Allocator>;
#else // _WIN32
std::deque<T, Allocator>;
#endif // _WIN32
template<typename T, typename Q>
inline T unscale(Q v) { return T(v) * T(SCALING_FACTOR); }
@ -341,6 +358,12 @@ public:
inline bool empty() const { return size() == 0; }
};
template<class T, class = FloatingOnly<T>>
constexpr T NaN = std::numeric_limits<T>::quiet_NaN();
constexpr float NaNf = NaN<float>;
constexpr double NaNd = NaN<double>;
} // namespace Slic3r
#endif

View file

@ -1182,6 +1182,42 @@ std::string xml_escape(std::string text, bool is_marked/* = false*/)
return text;
}
std::string xml_unescape(std::string s)
{
std::string ret;
std::string::size_type i = 0;
std::string::size_type pos = 0;
while (i < s.size()) {
std::string rep;
if (s[i] == '&') {
if (s.substr(i, 4) == "&lt;") {
ret += s.substr(pos, i - pos) + "<";
i += 4;
pos = i;
}
else if (s.substr(i, 4) == "&gt;") {
ret += s.substr(pos, i - pos) + ">";
i += 4;
pos = i;
}
else if (s.substr(i, 5) == "&amp;") {
ret += s.substr(pos, i - pos) + "&";
i += 5;
pos = i;
}
else {
++i;
}
}
else {
++i;
}
}
ret += s.substr(pos);
return ret;
}
std::string format_memsize_MB(size_t n)
{
std::string out;

View file

@ -68,8 +68,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Widgets/SideTools.hpp
GUI/Widgets/WebView.cpp
GUI/Widgets/WebView.hpp
GUI/Widgets/wxStaticText2.cpp
GUI/Widgets/wxStaticText2.hpp
GUI/Widgets/ErrorMsgStaticText.cpp
GUI/Widgets/ErrorMsgStaticText.hpp
GUI/AboutDialog.cpp
GUI/AboutDialog.hpp
GUI/NetworkTestDialog.cpp
@ -129,6 +129,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Gizmos/GLGizmoFaceDetector.hpp
GUI/Gizmos/GLGizmoSeam.cpp
GUI/Gizmos/GLGizmoSeam.hpp
GUI/Gizmos/GLGizmoText.cpp
GUI/Gizmos/GLGizmoText.hpp
GUI/GLSelectionRectangle.cpp
GUI/GLSelectionRectangle.hpp
GUI/Gizmos/GizmoObjectManipulation.cpp

View file

@ -434,6 +434,7 @@ GLVolume::GLVolume(float r, float g, float b, float a)
, zoom_to_volumes(true)
, shader_outside_printer_detection_enabled(false)
, is_outside(false)
, partly_inside(false)
, hover(HS_None)
, is_modifier(false)
, is_wipe_tower(false)
@ -679,7 +680,7 @@ void GLVolume::render(bool with_outline) const
bool color_volume = false;
ModelObjectPtrs& model_objects = GUI::wxGetApp().model().objects;
do {
if (object_idx() >= model_objects.size())
if ((!printable) || object_idx() >= model_objects.size())
break;
ModelObject* mo = model_objects[object_idx()];
@ -907,7 +908,7 @@ void GLVolume::simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_obj
ModelObject* model_object = nullptr;
ModelVolume* model_volume = nullptr;
do {
if (object_idx() >= model_objects.size())
if ((!printable) || object_idx() >= model_objects.size())
break;
model_object = model_objects[object_idx()];
@ -1290,9 +1291,16 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
//shader->set_uniform("print_volume.xy_data", m_render_volume.data);
//shader->set_uniform("print_volume.z_data", m_render_volume.zs);
/*shader->set_uniform("print_volume.type", static_cast<int>(m_print_volume.type));
shader->set_uniform("print_volume.xy_data", m_print_volume.data);
shader->set_uniform("print_volume.z_data", m_print_volume.zs);*/
if (volume.first->partly_inside) {
//only partly inside volume need to be painted with boundary check
shader->set_uniform("print_volume.type", static_cast<int>(m_print_volume.type));
shader->set_uniform("print_volume.xy_data", m_print_volume.data);
shader->set_uniform("print_volume.z_data", m_print_volume.zs);
}
else {
//use -1 ad a invalid type
shader->set_uniform("print_volume.type", -1);
}
shader->set_uniform("volume_world_matrix", volume.first->world_matrix());
shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower);
shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
@ -1378,6 +1386,7 @@ bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, Mo
const std::vector<BoundingBoxf3>& exclude_areas = curr_plate->get_exclude_areas();
for (GLVolume* volume : this->volumes)
{
if (! volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (! volume->is_wipe_tower && volume->composite_id.volume_id >= 0))) {
BuildVolume::ObjectState state;
const BoundingBoxf3& bb = volume_bbox(*volume);
@ -1405,6 +1414,7 @@ bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, Mo
int64_t comp_id = ((int64_t)volume->composite_id.object_id << 32) | ((int64_t)volume->composite_id.instance_id);
volume->is_outside = state != BuildVolume::ObjectState::Inside;
//volume->partly_inside = (state == BuildVolume::ObjectState::Colliding);
if (volume->printable) {
if (overall_state == ModelInstancePVS_Inside && volume->is_outside) {
overall_state = ModelInstancePVS_Fully_Outside;
@ -1448,6 +1458,23 @@ bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, Mo
BOOST_LOG_TRIVIAL(debug) << "instance includes " << volume->name << " is partially outside of bed";
}
}
}
for (GLVolume* volume : this->volumes)
{
if (! volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (! volume->is_wipe_tower && volume->composite_id.volume_id >= 0)))
{
int64_t comp_id = ((int64_t)volume->composite_id.object_id << 32) | ((int64_t)volume->composite_id.instance_id);
if (model_state.find(comp_id) != model_state.end())
{
if (model_state[comp_id] == ModelInstancePVS_Partly_Outside) {
volume->partly_inside = true;
}
else
volume->partly_inside = false;
}
}
}
if (out_state != nullptr)
*out_state = overall_state;
@ -1459,8 +1486,10 @@ void GLVolumeCollection::reset_outside_state()
{
for (GLVolume* volume : this->volumes)
{
if (volume != nullptr)
if (volume != nullptr) {
volume->is_outside = false;
volume->partly_inside = false;
}
}
}

View file

@ -371,6 +371,7 @@ public:
bool shader_outside_printer_detection_enabled : 1;
// Wheter or not this volume is outside print volume.
bool is_outside : 1;
bool partly_inside : 1;
// Wheter or not this volume has been generated from a modifier
bool is_modifier : 1;
// Wheter or not this volume has been generated from the wipe tower

View file

@ -188,10 +188,6 @@ void AMSSetting::create()
m_sizer_main->Fit(this);
this->Centre(wxBOTH);
// set mode
update_insert_material_read_mode(true);
update_starting_read_mode(false);
}
void AMSSetting::update_insert_material_read_mode(bool selected)

View file

@ -393,7 +393,7 @@ void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data)
sizer_mapping_item->Add(number, 0, wxALIGN_CENTER_HORIZONTAL, 0);
sizer_mapping_item->Add(m_filament_name, 0, wxALIGN_CENTER_HORIZONTAL, 0);
sizer_mapping_list->Add(sizer_mapping_item, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, FromDIP(5));
sizer_mapping_list->Add(sizer_mapping_item, 0, wxALL, FromDIP(5));
m_amsmapping_sizer_list.push_back(sizer_mapping_list);
}
m_sizer_list->Add(sizer_mapping_list, 0, wxALIGN_CENTER_HORIZONTAL, 0);
@ -514,4 +514,101 @@ void MappingItem::doRender(wxDC &dc)
}
}
AmsMapingTipPopup::AmsMapingTipPopup(wxWindow *parent)
:wxPopupTransientWindow(parent, wxBORDER_NONE)
{
SetBackgroundColour(*wxWHITE);
wxBoxSizer *m_sizer_main = new wxBoxSizer(wxVERTICAL);
m_sizer_main->Add(0, 0, 1, wxTOP, FromDIP(28));
wxBoxSizer *m_sizer_body = new wxBoxSizer(wxHORIZONTAL);
m_sizer_body->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(20));
m_panel_enable_ams = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(200, -1), wxTAB_TRAVERSAL);
m_panel_enable_ams->SetBackgroundColour(*wxWHITE);
wxBoxSizer *sizer_enable_ams = new wxBoxSizer(wxVERTICAL);
m_title_enable_ams = new wxStaticText(m_panel_enable_ams, wxID_ANY, _L("Enable AMS"), wxDefaultPosition, wxDefaultSize, 0);
m_title_enable_ams->SetBackgroundColour(*wxWHITE);
m_title_enable_ams->Wrap(-1);
sizer_enable_ams->Add(m_title_enable_ams, 0, 0, 0);
m_tip_enable_ams = new wxStaticText(m_panel_enable_ams, wxID_ANY, _L("Print with filaments in the AMS"), wxDefaultPosition, wxDefaultSize, 0);
m_tip_enable_ams->SetBackgroundColour(*wxWHITE);
m_tip_enable_ams->Wrap(-1);
sizer_enable_ams->Add(m_tip_enable_ams, 0, wxTOP, 8);
wxBoxSizer *sizer_enable_ams_img;
sizer_enable_ams_img = new wxBoxSizer(wxVERTICAL);
auto img_enable_ams = new wxStaticBitmap(m_panel_enable_ams, wxID_ANY, create_scaled_bitmap("monitor_upgrade_ams", this, 108), wxDefaultPosition,
wxSize(FromDIP(118), FromDIP(108)), 0);
sizer_enable_ams_img->Add(img_enable_ams, 0, wxALIGN_CENTER_HORIZONTAL, 0);
sizer_enable_ams->Add(sizer_enable_ams_img, 1, wxEXPAND | wxTOP, FromDIP(20));
m_panel_enable_ams->SetSizer(sizer_enable_ams);
m_panel_enable_ams->Layout();
m_sizer_body->Add(m_panel_enable_ams, 0, 0, 0);
m_split_lines = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(1, FromDIP(150)), wxTAB_TRAVERSAL);
m_split_lines->SetBackgroundColour(wxColour(238, 238, 238));
m_sizer_body->Add(m_split_lines, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, FromDIP(10));
m_panel_disable_ams = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(200, -1), wxTAB_TRAVERSAL);
m_panel_disable_ams->SetBackgroundColour(*wxWHITE);
wxBoxSizer *sizer_disable_ams;
sizer_disable_ams = new wxBoxSizer(wxVERTICAL);
m_title_disable_ams = new wxStaticText(m_panel_disable_ams, wxID_ANY, _L("Disable AMS"), wxDefaultPosition, wxDefaultSize, 0);
m_title_disable_ams->SetBackgroundColour(*wxWHITE);
m_title_disable_ams->Wrap(-1);
sizer_disable_ams->Add(m_title_disable_ams, 0, 0, 0);
m_tip_disable_ams = new wxStaticText(m_panel_disable_ams, wxID_ANY, _L("Print with the filament mounted on the back of chassis"), wxDefaultPosition, wxDefaultSize, 0);
m_tip_disable_ams->SetBackgroundColour(*wxWHITE);
m_tip_disable_ams->Wrap(-1);
sizer_disable_ams->Add(m_tip_disable_ams, 0, wxTOP, FromDIP(8));
wxBoxSizer *sizer_disable_ams_img;
sizer_disable_ams_img = new wxBoxSizer(wxVERTICAL);
auto img_disable_ams = new wxStaticBitmap(m_panel_disable_ams, wxID_ANY, create_scaled_bitmap("disable_ams_demo_icon", this, 95), wxDefaultPosition,
wxSize(FromDIP(95), FromDIP(109)), 0);
sizer_disable_ams_img->Add(img_disable_ams, 0, wxALIGN_CENTER_HORIZONTAL, 0);
sizer_disable_ams->Add(sizer_disable_ams_img, 1, wxEXPAND | wxTOP, FromDIP(20));
m_panel_disable_ams->SetSizer(sizer_disable_ams);
m_panel_disable_ams->Layout();
m_sizer_body->Add(m_panel_disable_ams, 0, 0, 0);
m_sizer_body->Add(0, 0, 0, wxEXPAND | wxRIGHT, FromDIP(20));
m_sizer_main->Add(m_sizer_body, 0, wxEXPAND, 0);
m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(28));
this->SetSizer(m_sizer_main);
this->Layout();
this->Fit();
Bind(wxEVT_PAINT, &AmsMapingTipPopup::paintEvent, this);
}
void AmsMapingTipPopup::paintEvent(wxPaintEvent &evt)
{
wxPaintDC dc(this);
dc.SetPen(wxColour(0xAC, 0xAC, 0xAC));
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRoundedRectangle(0, 0, GetSize().x, GetSize().y, 0);
}
void AmsMapingTipPopup::OnDismiss() {}
bool AmsMapingTipPopup::ProcessLeftDown(wxMouseEvent &event) {
return wxPopupTransientWindow::ProcessLeftDown(event); }
}} // namespace Slic3r::GUI

View file

@ -137,6 +137,27 @@ public:
void paintEvent(wxPaintEvent &evt);
};
class AmsMapingTipPopup : public wxPopupTransientWindow
{
public:
AmsMapingTipPopup(wxWindow *parent);
~AmsMapingTipPopup(){};
void paintEvent(wxPaintEvent &evt);
virtual void OnDismiss() wxOVERRIDE;
virtual bool ProcessLeftDown(wxMouseEvent &event) wxOVERRIDE;
public:
wxPanel * m_panel_enable_ams;
wxStaticText * m_title_enable_ams;
wxStaticText * m_tip_enable_ams;
wxPanel * m_split_lines;
wxPanel * m_panel_disable_ams;
wxStaticText * m_title_disable_ams;
wxStaticText * m_tip_disable_ams;
};
wxDECLARE_EVENT(EVT_SET_FINISH_MAPPING, wxCommandEvent);
}} // namespace Slic3r::GUI

View file

@ -17,6 +17,8 @@
#include <wx/filedlg.h>
#include <wx/wupdlock.h>
#include <wx/dataview.h>
#include <wx/tokenzr.h>
#include <wx/arrstr.h>
#include <wx/tglbtn.h>
#include "wxExtensions.hpp"
@ -44,18 +46,17 @@ const std::vector<std::string> license_list = {
AuFile::AuFile(wxWindow *parent, fs::path file_path, wxString file_name, AuxiliaryFolderType type, wxWindowID id, const wxPoint &pos, const wxSize &size, long style)
{
wxPanel::Create(parent, id, pos, wxSize(FromDIP(300), FromDIP(340)), style);
SetBackgroundColour(AUFILE_GREY300);
wxBoxSizer *sizer_body = new wxBoxSizer(wxVERTICAL);
SetSize(wxSize(FromDIP(300), FromDIP(340)));
SetMinSize(wxSize(FromDIP(300), FromDIP(340)));
SetMaxSize(wxSize(FromDIP(300), FromDIP(340)));
m_type = type;
m_file_path = file_path;
m_file_name = file_name;
wxSize panel_size = m_type == MODEL_PICTURE ? AUFILE_PICTURES_PANEL_SIZE : AUFILE_PANEL_SIZE;
wxPanel::Create(parent, id, pos, panel_size, style);
SetBackgroundColour(AUFILE_GREY300);
wxBoxSizer *sizer_body = new wxBoxSizer(wxVERTICAL);
SetSize(panel_size);
if (m_type == MODEL_PICTURE) {
if (m_file_path.empty()) { return; }
auto image = new wxImage(encode_path(m_file_path.string().c_str()));
@ -64,19 +65,19 @@ AuFile::AuFile(wxWindow *parent, fs::path file_path, wxString file_name, Auxilia
auto size = wxSize(0, 0);
float proportion = float(image->GetSize().x) / float(image->GetSize().y);
if (proportion >= 1) {
size.x = FromDIP(300);
size.y = FromDIP(300) / proportion;
size.x = AUFILE_PICTURES_SIZE.x;
size.y = AUFILE_PICTURES_SIZE.x / proportion;
} else {
size.y = FromDIP(300);
size.x = FromDIP(300) * proportion;
size.y = AUFILE_PICTURES_SIZE.y;
size.x = AUFILE_PICTURES_SIZE.y * proportion;
}
image->Rescale(size.x, size.y);
m_file_bitmap = wxBitmap(*image);
m_file_bitmap.bmp() = wxBitmap(*image);
} else {
m_bitmap_excel = create_scaled_bitmap("placeholder_excel", nullptr, 300);
m_bitmap_pdf = create_scaled_bitmap("placeholder_pdf", nullptr, 300);
m_bitmap_txt = create_scaled_bitmap("placeholder_txt", nullptr, 300);
m_bitmap_excel = ScalableBitmap(this, "placeholder_excel", 168);
m_bitmap_pdf = ScalableBitmap(this, "placeholder_pdf", 168);
m_bitmap_txt = ScalableBitmap(this, "placeholder_txt", 168);
if (m_type == OTHERS) {m_file_bitmap = m_bitmap_txt;}
if (m_type == BILL_OF_MATERIALS) {
@ -90,6 +91,7 @@ AuFile::AuFile(wxWindow *parent, fs::path file_path, wxString file_name, Auxilia
if (m_type == ASSEMBLY_GUIDE) {m_file_bitmap = m_bitmap_pdf;}
}
m_add_file = _L("Add File");
cover_text_left = _L("Set as cover");
cover_text_right = _L("Rename");
cover_text_cover = _L("Cover");
@ -98,15 +100,15 @@ AuFile::AuFile(wxWindow *parent, fs::path file_path, wxString file_name, Auxilia
m_file_edit_mask = ScalableBitmap(this, "auxiliary_edit_mask", 43);
m_file_delete = ScalableBitmap(this, "auxiliary_delete", 28);
auto m_text_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(300), FromDIP(40)), wxTAB_TRAVERSAL);
auto m_text_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(panel_size.x, AUFILE_TEXT_HEIGHT), wxTAB_TRAVERSAL);
m_text_panel->SetBackgroundColour(AUFILE_GREY300);
wxBoxSizer *m_text_sizer = new wxBoxSizer(wxVERTICAL);
m_text_name = new wxStaticText(m_text_panel, wxID_ANY, m_file_name, wxDefaultPosition, wxSize(FromDIP(300), FromDIP(20)), 0);
m_text_name->Wrap(FromDIP(290));
m_text_name = new wxStaticText(m_text_panel, wxID_ANY, m_file_name, wxDefaultPosition, wxSize(panel_size.x, -1), wxST_ELLIPSIZE_END);
m_text_name->Wrap(panel_size.x - FromDIP(10));
m_text_name->SetFont(::Label::Body_14);
m_input_name = new ::TextInput(m_text_panel, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, wxSize(FromDIP(300), FromDIP(35)), wxTE_PROCESS_ENTER);
m_input_name = new ::TextInput(m_text_panel, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, wxSize(panel_size.x, FromDIP(35)), wxTE_PROCESS_ENTER);
m_input_name->GetTextCtrl()->SetFont(::Label::Body_13);
m_input_name->SetFont(::Label::Body_14);
m_input_name->Hide();
@ -117,7 +119,7 @@ AuFile::AuFile(wxWindow *parent, fs::path file_path, wxString file_name, Auxilia
m_text_panel->SetSizer(m_text_sizer);
m_text_panel->Layout();
sizer_body->Add(0, 0, 0, wxTOP, FromDIP(300));
sizer_body->Add(0, 0, 0, wxTOP, panel_size.y - AUFILE_TEXT_HEIGHT);
sizer_body->Add(m_text_panel, 0, wxALIGN_CENTER, 0);
SetSizer(sizer_body);
@ -149,12 +151,26 @@ void AuFile::exit_rename_mode()
void AuFile::OnPaint(wxPaintEvent &event)
{
//wxPaintDC dc(this);
//render(dc);
wxBufferedPaintDC dc(this);
PrepareDC(dc);
wxPaintDC dc(this);
#ifdef __WXMSW__
wxSize size = GetSize();
wxMemoryDC memdc;
wxBitmap bmp(size.x, size.y);
memdc.SelectObject(bmp);
memdc.Blit({ 0, 0 }, size, &dc, { 0, 0 });
{
wxGCDC dc2(memdc);
PaintBackground(dc2);
PaintForeground(dc2);
}
memdc.SelectObject(wxNullBitmap);
dc.DrawBitmap(bmp, 0, 0);
#else
PaintBackground(dc);
PaintForeground(dc);
#endif
}
void AuFile::PaintBackground(wxDC &dc)
@ -169,21 +185,66 @@ void AuFile::PaintBackground(wxDC &dc)
dc.DrawRectangle(windowRect);
wxSize size = wxSize(FromDIP(300), FromDIP(300));
dc.SetPen(AUFILE_GREY200);
dc.SetBrush(AUFILE_GREY200);
dc.DrawRectangle(0,0,size.x,size.y);
dc.DrawBitmap(m_file_bitmap, (size.x - m_file_bitmap.GetSize().x) / 2, (size.y - m_file_bitmap.GetSize().y) / 2);
wxSize size = m_type == MODEL_PICTURE ? AUFILE_PICTURES_SIZE : AUFILE_SIZE;
if (m_type == AddFileButton)
{
auto pen_width = FromDIP(2);
dc.SetPen(wxPen(AUFILE_GREY500, pen_width));
dc.SetBrush(AUFILE_GREY200);
dc.DrawRoundedRectangle(pen_width / 2, pen_width / 2, size.x - pen_width / 2, size.y - pen_width / 2, AUFILE_ROUNDING);
auto line_length = FromDIP(50);
dc.DrawLine(wxPoint((size.x - line_length) / 2, size.y / 2), wxPoint((size.x + line_length) / 2, size.y / 2));
dc.DrawLine(wxPoint(size.x / 2, (size.y - line_length) / 2), wxPoint(size.x / 2, (size.y + line_length) / 2));
dc.SetFont(Label::Body_16);
auto sizet = dc.GetTextExtent(m_add_file);
auto pos = wxPoint(0, 0);
pos.x = (size.x - sizet.x) / 2;
pos.y = (size.y - 40); // to modify
dc.SetTextForeground(AUFILE_GREY500);
dc.DrawText(m_add_file, pos);
}
else {
dc.SetPen(AUFILE_GREY200);
dc.SetBrush(AUFILE_GREY200);
dc.DrawRoundedRectangle(0, 0, size.x, size.y, AUFILE_ROUNDING);
dc.DrawBitmap(m_file_bitmap.bmp(), (size.x - m_file_bitmap.GetBmpWidth()) / 2, (size.y - m_file_bitmap.GetBmpHeight()) / 2);
}
}
void AuFile::OnEraseBackground(wxEraseEvent &evt) {}
void AuFile::PaintForeground(wxDC &dc)
{
wxSize size = wxSize(FromDIP(300), FromDIP(300));
wxSize size = m_type == MODEL_PICTURE ? AUFILE_PICTURES_SIZE : AUFILE_SIZE;
if (m_hover) {
dc.DrawBitmap(m_file_edit_mask.bmp(), 0, size.y - m_file_edit_mask.GetBmpSize().y);
if (m_type == AddFileButton) {
auto pen_width = FromDIP(2);
dc.SetPen(wxPen(AUFILE_BRAND, pen_width));
dc.SetBrush(AUFILE_BRAND_TRANSPARENT);
dc.DrawRoundedRectangle(pen_width / 2, pen_width / 2, size.x - pen_width / 2, size.y - pen_width / 2, AUFILE_ROUNDING);
auto line_length = FromDIP(50);
dc.DrawLine(wxPoint((size.x - line_length) / 2, size.y / 2), wxPoint((size.x + line_length) / 2, size.y / 2));
dc.DrawLine(wxPoint(size.x / 2, (size.y - line_length) / 2), wxPoint(size.x / 2, (size.y + line_length) / 2));
auto sizet = dc.GetTextExtent(m_add_file);
auto pos = wxPoint(0, 0);
pos.x = (size.x - sizet.x) / 2;
pos.y = (size.y - 40); // to modify
dc.SetTextForeground(AUFILE_BRAND);
dc.DrawText(m_add_file, pos);
return;
}
if (m_type == MODEL_PICTURE) {
dc.DrawBitmap(m_file_edit_mask.bmp(), 0, size.y - m_file_edit_mask.GetBmpSize().y);
}
dc.SetFont(Label::Body_14);
dc.SetTextForeground(*wxWHITE);
if (m_type == MODEL_PICTURE) {
@ -210,11 +271,11 @@ void AuFile::PaintForeground(wxDC &dc)
dc.DrawRectangle(pos.x, pos.y, 2, FromDIP(30));
} else {
// right text
auto sizet = dc.GetTextExtent(cover_text_right);
/* auto sizet = dc.GetTextExtent(cover_text_right);
auto pos = wxPoint(0, 0);
pos.x = (size.x - sizet.x) / 2;
pos.y = (size.y - (m_file_edit_mask.GetBmpSize().y + sizet.y) / 2);
dc.DrawText(cover_text_right, pos);
pos.x = (size.x - sizet.x) / 2;
pos.y = (size.y - (m_file_edit_mask.GetBmpSize().y + sizet.y) / 2);
dc.DrawText(cover_text_right, pos);*/
}
}
@ -246,7 +307,7 @@ void AuFile::on_mouse_leave(wxMouseEvent &evt)
void AuFile::on_input_enter(wxCommandEvent &evt)
{
auto new_file_name = into_u8(m_input_name->GetTextCtrl()->GetValue());
auto new_file_name = m_input_name->GetTextCtrl()->GetValue();
auto m_valid_type = Valid;
wxString info_line;
@ -271,7 +332,7 @@ void AuFile::on_input_enter(wxCommandEvent &evt)
auto new_fullname = new_file_name + m_file_path.extension().string();
auto new_fullname_path = dir.string() + "/" + new_fullname;
wxString new_fullname_path = dir.wstring() + "/" + new_fullname;
fs::path new_dir_path(new_fullname_path.c_str());
@ -314,7 +375,7 @@ void AuFile::on_input_enter(wxCommandEvent &evt)
// post event
auto event = wxCommandEvent(EVT_AUXILIARY_UPDATE_RENAME);
event.SetString(wxString::Format("%s|%s|%s", s_default_folders[m_type], m_file_path.string(), new_dir_path.string()));
event.SetString(wxString::Format("%s|%s|%s", s_default_folders[m_type], m_file_path.wstring(), new_dir_path.wstring()));
event.SetEventObject(m_parent);
wxPostEvent(m_parent, event);
@ -328,12 +389,19 @@ void AuFile::on_input_enter(wxCommandEvent &evt)
void AuFile::on_dclick(wxMouseEvent &evt)
{
wxLaunchDefaultApplication(m_file_path.wstring(), 0);
if (m_type == AddFileButton)
return;
else
wxLaunchDefaultApplication(m_file_path.wstring(), 0);
}
void AuFile::on_mouse_left_up(wxMouseEvent &evt)
{
wxSize size = wxSize(FromDIP(300), FromDIP(300));
if (m_type == AddFileButton) {
return;
}
wxSize size = m_type == MODEL_PICTURE ? AUFILE_PICTURES_SIZE : AUFILE_SIZE;
auto pos = evt.GetPosition();
// set cover
@ -343,28 +411,36 @@ void AuFile::on_mouse_left_up(wxMouseEvent &evt)
auto cover_right = mask_size.x / 2;
auto cover_bottom = size.y;
if (pos.x > cover_left && pos.x < cover_right && pos.y > cover_top && pos.y < cover_bottom) { on_set_cover(); }
if (pos.x > cover_left && pos.x < cover_right && pos.y > cover_top && pos.y < cover_bottom) {
if(m_type == MODEL_PICTURE)
on_set_cover();
/* else
on_set_rename();*/
return;
}
// rename
auto rename_left = mask_size.x / 2;
auto rename_top = size.y - mask_size.y;
auto rename_right = mask_size.x;
auto rename_bottom = size.y;
if (pos.x > rename_left && pos.x < rename_right && pos.y > rename_top && pos.y < rename_bottom) { on_set_rename(); }
if (pos.x > rename_left && pos.x < rename_right && pos.y > rename_top && pos.y < rename_bottom) { on_set_rename(); return; }
// close
auto close_left = size.x - m_file_delete.GetBmpSize().x - FromDIP(15);
auto close_top = FromDIP(15);
auto close_right = size.x - FromDIP(15);
auto close_bottom = m_file_delete.GetBmpSize().y + FromDIP(15);
if (pos.x > close_left && pos.x < close_right && pos.y > close_top && pos.y < close_bottom) { on_set_delete(); }
if (pos.x > close_left && pos.x < close_right && pos.y > close_top && pos.y < close_bottom) { on_set_delete(); return; }
exit_rename_mode();
}
void AuFile::on_set_cover()
{
if (wxGetApp().plater()->model().model_info == nullptr) { wxGetApp().plater()->model().model_info = std::make_shared<ModelInfo>(); }
wxGetApp().plater()->model().model_info->cover_file = m_file_name.ToStdString();
wxGetApp().plater()->model().model_info->cover_file = std::string(m_file_name.ToUTF8().data());
auto full_path = m_file_path.branch_path();
auto full_root_path = full_path.branch_path();
@ -378,14 +454,15 @@ void AuFile::on_set_cover()
}
bool result = true;
wxImage thumbnail_img;;
wxImage thumbnail_img;
result = generate_image(m_file_path.string(), thumbnail_img, _3MF_COVER_SIZE);
if (result) {
auto cover_img_path = dir_path.string() + "/thumbnail_3mf.png";
thumbnail_img.SaveFile(encode_path(cover_img_path.c_str()));
}
result = generate_image(m_file_path.string(), thumbnail_img, PRINTER_THUMBNAIL_SMALL_SIZE, GERNERATE_IMAGE_CROP_VERTICAL);
result = generate_image(m_file_path.string(), thumbnail_img, PRINTER_THUMBNAIL_SMALL_SIZE);
if (result) {
auto small_img_path = dir_path.string() + "/thumbnail_small.png";
thumbnail_img.SaveFile(encode_path(small_img_path.c_str()));
@ -429,7 +506,7 @@ void AuFile::on_set_delete()
if (is_fine) {
auto evt = wxCommandEvent(EVT_AUXILIARY_UPDATE_DELETE);
evt.SetString(wxString::Format("%s|%s", s_default_folders[m_type], m_file_path.string()));
evt.SetString(wxString::Format("%s|%s", s_default_folders[m_type], m_file_path.wstring()));
evt.SetEventObject(m_parent);
wxPostEvent(m_parent, evt);
}
@ -468,11 +545,11 @@ void AuFile::msw_rescale()
}
image->Rescale(size.x, size.y);
m_file_bitmap = wxBitmap(*image);
m_file_bitmap.bmp() = wxBitmap(*image);
} else {
m_bitmap_excel = create_scaled_bitmap("placeholder_excel", nullptr, 300);
m_bitmap_pdf = create_scaled_bitmap("placeholder_pdf", nullptr, 300);
m_bitmap_txt = create_scaled_bitmap("placeholder_txt", nullptr, 300);
m_bitmap_excel = ScalableBitmap(this, "placeholder_excel", 168);
m_bitmap_pdf = ScalableBitmap(this, "placeholder_pdf", 168);
m_bitmap_txt = ScalableBitmap(this, "placeholder_txt", 168);
if (m_type == OTHERS) { m_file_bitmap = m_bitmap_txt; }
if (m_type == BILL_OF_MATERIALS) { m_file_bitmap = m_bitmap_excel; }
@ -504,7 +581,8 @@ AuFolderPanel::AuFolderPanel(wxWindow *parent, AuxiliaryFolderType type, wxWindo
m_button_add->SetMinSize(wxSize(-1, FromDIP(24)));
m_button_add->SetCornerRadius(FromDIP(12));
m_button_add->SetFont(Label::Body_14);
// m_button_add->Bind(wxEVT_LEFT_UP, &AuxiliaryPanel::on_add, this);
m_big_button_add = new AuFile(m_scrolledWindow, fs::path(), "", AddFileButton, -1);
/*m_button_del = new Button(m_scrolledWindow, _L("Delete"), "auxiliary_delete_file", 12, 12);
m_button_del->SetBackgroundColor(btn_bg_white);
@ -515,12 +593,18 @@ AuFolderPanel::AuFolderPanel(wxWindow *parent, AuxiliaryFolderType type, wxWindo
// m_button_del->Bind(wxEVT_LEFT_UP, &AuxiliaryPanel::on_delete, this);
sizer_top->Add(0, 0, 0, wxLEFT, FromDIP(10));
sizer_top->Add(m_button_add, 0, wxALL, 0);
m_gsizer_content = new wxWrapSizer(wxHORIZONTAL, wxWRAPSIZER_DEFAULT_FLAGS);
if (m_type == MODEL_PICTURE) {
sizer_top->Add(m_button_add, 0, wxALL, 0);
m_big_button_add->Hide();
}
else {
m_gsizer_content->Add(m_big_button_add, 0, wxALL, FromDIP(8));
m_button_add->Hide();
}
// sizer_top->Add(m_button_del, 0, wxALL, 0);
m_gsizer_content = new wxGridSizer(0, 3, FromDIP(18), FromDIP(18));
sizer_body->Add(sizer_top, 0, wxEXPAND | wxTOP, FromDIP(35));
sizer_body->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(30));
sizer_body->AddSpacer(FromDIP(14));
sizer_body->Add(m_gsizer_content, 0, 0, 0);
m_scrolledWindow->SetSizer(sizer_body);
m_scrolledWindow->Layout();
@ -529,18 +613,25 @@ AuFolderPanel::AuFolderPanel(wxWindow *parent, AuxiliaryFolderType type, wxWindo
this->SetSizer(sizer_main);
this->Layout();
m_button_add->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AuFolderPanel::on_add), NULL, this);
// m_button_del->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AuFolderPanel::on_delete), NULL, this);
m_big_button_add->Bind(wxEVT_LEFT_DOWN, [this](auto& e)
{
auto evt = wxCommandEvent(EVT_AUXILIARY_IMPORT);
evt.SetString(s_default_folders[m_type]);
evt.SetEventObject(m_parent);
wxPostEvent(m_parent, evt);
});
m_button_add->Bind(wxEVT_BUTTON, &AuFolderPanel::on_add, this);
}
void AuFolderPanel::clear()
{
for (auto i = 0; i < m_aufiles_list.GetCount(); i++) {
AuFiles *aufile = m_aufiles_list[i];
if (aufile->file != NULL) { aufile->file->Destroy(); }
if (aufile->file) { aufile->file->Destroy(); }
}
m_aufiles_list.clear();
m_gsizer_content->Layout();
Layout();
Refresh();
}
void AuFolderPanel::update(std::vector<fs::path> paths)
@ -551,7 +642,7 @@ void AuFolderPanel::update(std::vector<fs::path> paths)
auto name = encode_path(temp_name.c_str());
auto aufile = new AuFile(m_scrolledWindow, paths[i], name, m_type, wxID_ANY);
m_gsizer_content->Add(aufile, 0, 0, 0);
m_gsizer_content->Add(aufile, 0, wxALL, FromDIP(8));
auto af = new AuFiles;
af->path = paths[i].string();
af->file = aufile;
@ -570,7 +661,7 @@ void AuFolderPanel::msw_rescale()
}
}
void AuFolderPanel::on_add(wxCommandEvent &event)
void AuFolderPanel::on_add(wxCommandEvent& event)
{
auto evt = wxCommandEvent(EVT_AUXILIARY_IMPORT);
evt.SetString(s_default_folders[m_type]);
@ -582,8 +673,6 @@ void AuFolderPanel::on_delete(wxCommandEvent &event) { clear(); }
AuFolderPanel::~AuFolderPanel()
{
m_button_add->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AuFolderPanel::on_add), NULL, this);
// m_button_del->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(AuFolderPanel::on_delete), NULL, this);
}
void AuFolderPanel::update_cover()
@ -617,22 +706,29 @@ AuxiliaryPanel::AuxiliaryPanel(wxWindow *parent, wxWindowID id, const wxPoint &p
// delete event
Bind(EVT_AUXILIARY_UPDATE_DELETE, [this](wxCommandEvent &e) {
auto info_str = e.GetString();
auto parems = std::vector<std::string>{};
Split(info_str.ToStdString(), "|", parems);
wxArrayString parems;
wxStringTokenizer tokenizer(info_str, "|");
while (tokenizer.HasMoreTokens()) {
wxString token = tokenizer.GetNextToken();
parems.Add(token);
}
auto model = parems[0];
auto name = parems[1];
auto iter = m_paths_list.find(model);
auto iter = m_paths_list.find(model.ToStdString());
if (iter != m_paths_list.end()) {
auto list = iter->second;
for (auto i = 0; i < list.size(); i++) {
if (list[i] == name) {
if (list[i].wstring() == name) {
list.erase(std::begin(list) + i);
break;
}
}
m_paths_list[model] = list;
m_paths_list[model.ToStdString()] = list;
update_all_panel();
update_all_cover();
}
@ -642,24 +738,28 @@ AuxiliaryPanel::AuxiliaryPanel(wxWindow *parent, wxWindowID id, const wxPoint &p
Bind(EVT_AUXILIARY_UPDATE_RENAME, [this](wxCommandEvent &e) {
auto info_str = e.GetString();
auto parems = std::vector<std::string>{};
Split(info_str.ToStdString(), "|", parems);
wxArrayString parems;
wxStringTokenizer tokenizer(info_str, "|");
while (tokenizer.HasMoreTokens()) {
wxString token = tokenizer.GetNextToken();
parems.Add(token);
}
auto model = parems[0];
auto old_name = parems[1];
auto new_name = parems[2];
auto iter = m_paths_list.find(model);
auto iter = m_paths_list.find(model.ToStdString());
if (iter != m_paths_list.end()) {
auto list = iter->second;
for (auto i = 0; i < list.size(); i++) {
if (list[i] == old_name) {
list[i] = new_name;
if (list[i].wstring() == old_name) {
list[i] = fs::path(new_name.c_str());
break;
}
}
m_paths_list[model] = list;
m_paths_list[model.ToStdString()] = list;
}
});
}
@ -943,6 +1043,7 @@ void AuxiliaryPanel::update_all_panel()
{
std::map<std::string, std::vector<fs::path>>::iterator mit;
Freeze();
m_pictures_panel->clear();
m_bill_of_materials_panel->clear();
m_assembly_panel->clear();
@ -954,6 +1055,7 @@ void AuxiliaryPanel::update_all_panel()
if (mit->first == "Assembly Guide") { m_assembly_panel->update(mit->second); }
if (mit->first == "Others") { m_others_panel->update(mit->second); }
}
Thaw();
}
void AuxiliaryPanel::update_all_cover()
@ -977,7 +1079,7 @@ void AuxiliaryPanel::update_all_cover()
wxBoxSizer *m_sizer_body = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *m_sizer_designer = new wxBoxSizer(wxHORIZONTAL);
auto m_text_designer = new wxStaticText(this, wxID_ANY, _L("Designer"), wxDefaultPosition, wxSize(120, -1), 0);
auto m_text_designer = new wxStaticText(this, wxID_ANY, _L("Author"), wxDefaultPosition, wxSize(120, -1), 0);
m_text_designer->Wrap(-1);
m_sizer_designer->Add(m_text_designer, 0, wxALIGN_CENTER, 0);

View file

@ -48,8 +48,17 @@
#include "Widgets/SideTools.hpp"
#define AUFILE_GREY700 wxColour(107, 107, 107)
#define AUFILE_GREY500 wxColour(158, 158, 158)
#define AUFILE_GREY300 wxColour(238, 238, 238)
#define AUFILE_GREY200 wxColour(248, 248, 248)
#define AUFILE_BRAND wxColour(0, 174, 66)
#define AUFILE_BRAND_TRANSPARENT wxColour(215, 232, 222)
#define AUFILE_PICTURES_SIZE wxSize(FromDIP(300), FromDIP(300))
#define AUFILE_PICTURES_PANEL_SIZE wxSize(FromDIP(300), FromDIP(340))
#define AUFILE_SIZE wxSize(FromDIP(168), FromDIP(168))
#define AUFILE_PANEL_SIZE wxSize(FromDIP(168), FromDIP(208))
#define AUFILE_TEXT_HEIGHT FromDIP(40)
#define AUFILE_ROUNDING FromDIP(5)
enum AuxiliaryFolderType {
MODEL_PICTURE,
@ -58,6 +67,7 @@ enum AuxiliaryFolderType {
OTHERS,
THUMBNAILS,
DESIGNER,
AddFileButton,
};
const static std::array<wxString, 5> s_default_folders = {("Model Pictures"), ("Bill of Materials"), ("Assembly Guide"), ("Others"), (".thumbnails")};
@ -76,18 +86,19 @@ public:
wxStaticText* m_text_name {nullptr};
::TextInput* m_input_name {nullptr};
fs::path m_file_path;
wxString m_add_file;
wxString m_file_name;
wxString cover_text_left;
wxString cover_text_right;
wxString cover_text_cover;
wxBitmap m_file_bitmap;
ScalableBitmap m_file_bitmap;
ScalableBitmap m_file_cover;
ScalableBitmap m_file_edit_mask;
ScalableBitmap m_file_delete;
wxBitmap m_bitmap_excel;
wxBitmap m_bitmap_pdf;
wxBitmap m_bitmap_txt;
ScalableBitmap m_bitmap_excel;
ScalableBitmap m_bitmap_pdf;
ScalableBitmap m_bitmap_txt;
public:
AuFile(wxWindow *parent, fs::path file_path, wxString file_name, AuxiliaryFolderType type, wxWindowID id = wxID_ANY, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = wxTAB_TRAVERSAL);
@ -144,12 +155,13 @@ public:
public:
AuxiliaryFolderType m_type;
wxScrolledWindow * m_scrolledWindow{nullptr};
wxGridSizer * m_gsizer_content{nullptr};
wxWrapSizer * m_gsizer_content{nullptr};
Button * m_button_add{nullptr};
Button * m_button_del{nullptr};
AuFile * m_big_button_add{ nullptr };
AuFilesHash m_aufiles_list;
void on_add(wxCommandEvent &event);
void on_add(wxCommandEvent& event);
void on_delete(wxCommandEvent &event);
};

View file

@ -93,7 +93,7 @@ void BBLStatusBarSend::set_progress(int val)
return;
//add the logic for arrange/orient jobs, which don't call stop_busy
if (!m_sizer->IsShown(m_prog)) {
if (!m_prog->IsShown()) {
m_sizer->Show(m_prog);
m_sizer->Show(m_cancelbutton);
}

View file

@ -15,7 +15,7 @@
namespace Slic3r {
namespace GUI {
BindMachineDilaog::BindMachineDilaog(Plater *plater /*= nullptr*/)
BindMachineDialog::BindMachineDialog(Plater *plater /*= nullptr*/)
: DPIDialog(static_cast<wxWindow *>(wxGetApp().mainframe), wxID_ANY, _L("Log in printer"), wxDefaultPosition, wxDefaultSize, wxCAPTION)
{
std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str();
@ -92,12 +92,12 @@ namespace GUI {
m_avatar = new wxStaticBitmap(m_panel_right, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(FromDIP(60), FromDIP(60)), 0);
wxWebRequest request = wxWebSession::GetDefault().CreateRequest(this, wxGetApp().getAgent()->get_user_avatar());
if (!request.IsOk()) {
web_request = wxWebSession::GetDefault().CreateRequest(this, wxGetApp().getAgent()->get_user_avatar());
if (!web_request.IsOk()) {
// todo request fail
}
// Start the request
request.Start();
web_request.Start();
}
m_sizer_right_v->Add(m_avatar, 0, wxALIGN_CENTER, 0);
@ -172,23 +172,25 @@ namespace GUI {
Fit();
Centre(wxBOTH);
Bind(wxEVT_SHOW, &BindMachineDilaog::on_show, this);
Bind(wxEVT_SHOW, &BindMachineDialog::on_show, this);
m_button_bind->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BindMachineDilaog::on_bind_printer), NULL, this);
m_button_cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BindMachineDilaog::on_cancel), NULL, this);
this->Connect(EVT_BIND_MACHINE_FAIL, wxCommandEventHandler(BindMachineDilaog::on_bind_fail), NULL, this);
this->Connect(EVT_BIND_MACHINE_SUCCESS, wxCommandEventHandler(BindMachineDilaog::on_bind_success), NULL, this);
this->Connect(EVT_BIND_UPDATE_MESSAGE, wxCommandEventHandler(BindMachineDilaog::on_update_message), NULL, this);
Bind(wxEVT_CLOSE_WINDOW, &BindMachineDialog::on_close, this);
m_button_bind->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BindMachineDialog::on_bind_printer), NULL, this);
m_button_cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BindMachineDialog::on_cancel), NULL, this);
this->Connect(EVT_BIND_MACHINE_FAIL, wxCommandEventHandler(BindMachineDialog::on_bind_fail), NULL, this);
this->Connect(EVT_BIND_MACHINE_SUCCESS, wxCommandEventHandler(BindMachineDialog::on_bind_success), NULL, this);
this->Connect(EVT_BIND_UPDATE_MESSAGE, wxCommandEventHandler(BindMachineDialog::on_update_message), NULL, this);
m_simplebook->SetSelection(1);
}
BindMachineDilaog::~BindMachineDilaog()
BindMachineDialog::~BindMachineDialog()
{
m_button_bind->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BindMachineDilaog::on_bind_printer), NULL, this);
m_button_cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BindMachineDilaog::on_cancel), NULL, this);
this->Disconnect(EVT_BIND_MACHINE_FAIL, wxCommandEventHandler(BindMachineDilaog::on_bind_fail), NULL, this);
this->Disconnect(EVT_BIND_MACHINE_SUCCESS, wxCommandEventHandler(BindMachineDilaog::on_bind_success), NULL, this);
this->Disconnect(EVT_BIND_UPDATE_MESSAGE, wxCommandEventHandler(BindMachineDilaog::on_update_message), NULL, this);
m_button_bind->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BindMachineDialog::on_bind_printer), NULL, this);
m_button_cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(BindMachineDialog::on_cancel), NULL, this);
this->Disconnect(EVT_BIND_MACHINE_FAIL, wxCommandEventHandler(BindMachineDialog::on_bind_fail), NULL, this);
this->Disconnect(EVT_BIND_MACHINE_SUCCESS, wxCommandEventHandler(BindMachineDialog::on_bind_success), NULL, this);
this->Disconnect(EVT_BIND_UPDATE_MESSAGE, wxCommandEventHandler(BindMachineDialog::on_update_message), NULL, this);
}
//static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
@ -205,30 +207,49 @@ namespace GUI {
//}
void BindMachineDilaog::on_cancel(wxCommandEvent &event)
void BindMachineDialog::on_cancel(wxCommandEvent &event)
{
EndModal(wxID_CANCEL);
on_destroy();
EndModal(wxID_CANCEL);
}
void BindMachineDilaog::on_bind_fail(wxCommandEvent &event)
void BindMachineDialog::on_destroy()
{
if (m_bind_job) {
m_bind_job->cancel();
m_bind_job->join();
}
if (web_request.IsOk()) {
web_request.Cancel();
}
}
void BindMachineDialog::on_close(wxCloseEvent &event)
{
on_destroy();
event.Skip();
}
void BindMachineDialog::on_bind_fail(wxCommandEvent &event)
{
//m_status_text->SetLabel(_L("Would you like to log in this printer with current account?"));
m_simplebook->SetSelection(1);
}
void BindMachineDilaog::on_update_message(wxCommandEvent &event)
void BindMachineDialog::on_update_message(wxCommandEvent &event)
{
m_status_text->SetLabelText(event.GetString());
}
void BindMachineDilaog::on_bind_success(wxCommandEvent &event)
void BindMachineDialog::on_bind_success(wxCommandEvent &event)
{
EndModal(wxID_OK);
MessageDialog msg_wingow(nullptr, _L("Log in successful."), "", wxAPPLY | wxOK);
if (msg_wingow.ShowModal() == wxOK) { return; }
}
void BindMachineDilaog::on_bind_printer(wxCommandEvent &event)
void BindMachineDialog::on_bind_printer(wxCommandEvent &event)
{
//check isset info
if (m_machine_info == nullptr || m_machine_info == NULL) return;
@ -242,13 +263,13 @@ namespace GUI {
m_bind_job->start();
}
void BindMachineDilaog::on_dpi_changed(const wxRect &suggested_rect)
void BindMachineDialog::on_dpi_changed(const wxRect &suggested_rect)
{
m_button_bind->SetMinSize(BIND_DIALOG_BUTTON_SIZE);
m_button_cancel->SetMinSize(BIND_DIALOG_BUTTON_SIZE);
}
void BindMachineDilaog::on_show(wxShowEvent &event)
void BindMachineDialog::on_show(wxShowEvent &event)
{
//m_printer_name->SetLabelText(m_machine_info->get_printer_type_string());
m_printer_name->SetLabelText(from_u8(m_machine_info->dev_name));
@ -256,7 +277,7 @@ void BindMachineDilaog::on_show(wxShowEvent &event)
}
UnBindMachineDilaog::UnBindMachineDilaog(Plater *plater /*= nullptr*/)
UnBindMachineDialog::UnBindMachineDialog(Plater *plater /*= nullptr*/)
: DPIDialog(static_cast<wxWindow *>(wxGetApp().mainframe), wxID_ANY, _L("Log out printer"), wxDefaultPosition, wxDefaultSize, wxCAPTION)
{
std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % resources_dir()).str();
@ -402,24 +423,24 @@ UnBindMachineDilaog::UnBindMachineDilaog(Plater *plater /*= nullptr*/)
Fit();
Centre(wxBOTH);
Bind(wxEVT_SHOW, &UnBindMachineDilaog::on_show, this);
m_button_unbind->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(UnBindMachineDilaog::on_unbind_printer), NULL, this);
m_button_cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(UnBindMachineDilaog::on_cancel), NULL, this);
Bind(wxEVT_SHOW, &UnBindMachineDialog::on_show, this);
m_button_unbind->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(UnBindMachineDialog::on_unbind_printer), NULL, this);
m_button_cancel->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(UnBindMachineDialog::on_cancel), NULL, this);
}
UnBindMachineDilaog::~UnBindMachineDilaog()
UnBindMachineDialog::~UnBindMachineDialog()
{
m_button_unbind->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(UnBindMachineDilaog::on_unbind_printer), NULL, this);
m_button_cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(UnBindMachineDilaog::on_cancel), NULL, this);
m_button_unbind->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(UnBindMachineDialog::on_unbind_printer), NULL, this);
m_button_cancel->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(UnBindMachineDialog::on_cancel), NULL, this);
}
void UnBindMachineDilaog::on_cancel(wxCommandEvent &event)
void UnBindMachineDialog::on_cancel(wxCommandEvent &event)
{
EndModal(wxID_CANCEL);
}
void UnBindMachineDilaog::on_unbind_printer(wxCommandEvent &event)
void UnBindMachineDialog::on_unbind_printer(wxCommandEvent &event)
{
if (!wxGetApp().is_user_login()) {
m_status_text->SetLabelText(_L("Please log in first."));
@ -455,13 +476,13 @@ void UnBindMachineDilaog::on_unbind_printer(wxCommandEvent &event)
}
}
void UnBindMachineDilaog::on_dpi_changed(const wxRect &suggested_rect)
void UnBindMachineDialog::on_dpi_changed(const wxRect &suggested_rect)
{
m_button_unbind->SetMinSize(BIND_DIALOG_BUTTON_SIZE);
m_button_cancel->SetMinSize(BIND_DIALOG_BUTTON_SIZE);
}
void UnBindMachineDilaog::on_show(wxShowEvent &event)
void UnBindMachineDialog::on_show(wxShowEvent &event)
{
//m_printer_name->SetLabelText(m_machine_info->get_printer_type_string());
m_printer_name->SetLabelText(from_u8(m_machine_info->dev_name));

View file

@ -16,6 +16,7 @@
#include <wx/icon.h>
#include <wx/dialog.h>
#include <curl/curl.h>
#include <wx/webrequest.h>
#include "wxExtensions.hpp"
#include "Plater.hpp"
#include "Widgets/StepCtrl.hpp"
@ -42,7 +43,7 @@ struct MemoryStruct
size_t size;
};
class BindMachineDilaog : public DPIDialog
class BindMachineDialog : public DPIDialog
{
private:
wxStaticText * m_printer_name;
@ -54,14 +55,15 @@ private:
Button * m_button_cancel;
wxSimplebook *m_simplebook;
wxStaticBitmap *m_avatar;
wxWebRequest web_request;
MachineObject * m_machine_info{nullptr};
std::shared_ptr<BindJob> m_bind_job;
std::shared_ptr<BBLStatusBarBind> m_status_bar;
public:
BindMachineDilaog(Plater *plater = nullptr);
~BindMachineDilaog();
BindMachineDialog(Plater *plater = nullptr);
~BindMachineDialog();
void on_cancel(wxCommandEvent &event);
void on_bind_fail(wxCommandEvent &event);
void on_update_message(wxCommandEvent &event);
@ -70,9 +72,11 @@ public:
void on_dpi_changed(const wxRect &suggested_rect) override;
void update_machine_info(MachineObject *info) { m_machine_info = info; };
void on_show(wxShowEvent &event);
void on_close(wxCloseEvent& event);
void on_destroy();
};
class UnBindMachineDilaog : public DPIDialog
class UnBindMachineDialog : public DPIDialog
{
protected:
wxStaticText * m_printer_name;
@ -84,8 +88,8 @@ protected:
wxStaticBitmap *m_avatar;
public:
UnBindMachineDilaog(Plater *plater = nullptr);
~UnBindMachineDilaog();
UnBindMachineDialog(Plater *plater = nullptr);
~UnBindMachineDialog();
void on_cancel(wxCommandEvent &event);
void on_unbind_printer(wxCommandEvent &event);

View file

@ -93,6 +93,9 @@ void Camera::select_view(const std::string& direction)
look_at(m_target + m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ());
else if (direction == "topfront")
look_at(m_target - 0.707 * m_distance * Vec3d::UnitY() + 0.707 * m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY() + Vec3d::UnitZ());
else if (direction == "plate") {
look_at(m_target - 0.707 * m_distance * Vec3d::UnitY() + 0.707 * m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY() + Vec3d::UnitZ());
}
}
double Camera::get_fov() const

View file

@ -11,7 +11,6 @@
#include "MsgDialog.hpp"
#include "Plater.hpp"
#include "GUI_App.hpp"
#include "nlohmann/json.hpp"
#include <thread>
#include <mutex>
#include <codecvt>
@ -21,7 +20,6 @@
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
using namespace nlohmann;
namespace pt = boost::property_tree;
@ -259,63 +257,34 @@ wxString HMSItem::get_hms_msg_level_str(HMSMessageLevel level)
return "";
}
PRINTER_TYPE MachineObject::parse_printer_type(std::string type_str)
std::string MachineObject::parse_printer_type(std::string type_str)
{
if (type_str.compare("3DPrinter-P1") == 0) {
return PRINTER_TYPE::PRINTER_3DPrinter_P1;
} else if (type_str.compare("3DPrinter-X1") == 0) {
return PRINTER_TYPE::PRINTER_3DPrinter_X1;
if (type_str.compare("3DPrinter-X1") == 0) {
return "BL-P002";
} else if (type_str.compare("3DPrinter-X1-Carbon") == 0) {
return PRINTER_TYPE::PRINTER_3DPrinter_X1_Carbon;
}
BOOST_LOG_TRIVIAL(trace) << "unknown printer type: " << type_str;
return PRINTER_TYPE::PRINTER_3DPrinter_UKNOWN;
}
PRINTER_TYPE MachineObject::parse_iot_printer_type(std::string type_str)
{
if (type_str.compare("BL-P003") == 0) {
return PRINTER_TYPE::PRINTER_3DPrinter_P1;
} else if (type_str.compare("BL-P002") == 0) {
return PRINTER_TYPE::PRINTER_3DPrinter_X1;
return "BL-P001";
} else if (type_str.compare("BL-P001") == 0) {
return PRINTER_TYPE::PRINTER_3DPrinter_X1_Carbon;
}
BOOST_LOG_TRIVIAL(trace) << "unknown printer type: " << type_str;
return PRINTER_TYPE::PRINTER_3DPrinter_UKNOWN;
}
PRINTER_TYPE MachineObject::parse_preset_printer_type(std::string type_str)
{
return parse_iot_printer_type(type_str);
}
std::string MachineObject::get_preset_printer_model_name(PRINTER_TYPE printer_type)
{
if (printer_type == PRINTER_TYPE::PRINTER_3DPrinter_P1) {
return "Bambu Lab P1";
} else if (printer_type == PRINTER_TYPE::PRINTER_3DPrinter_X1) {
return "Bambu Lab X1";
} else if (printer_type == PRINTER_TYPE::PRINTER_3DPrinter_X1_Carbon) {
return "Bambu Lab X1 Carbon";
return type_str;
} else if (type_str.compare("BL-P003") == 0) {
return type_str;
} else {
return "";
return DeviceManager::parse_printer_type(type_str);
}
return "";
}
std::string MachineObject::get_preset_printer_model_name(std::string printer_type)
{
return DeviceManager::get_printer_display_name(printer_type);
}
wxString MachineObject::get_printer_type_display_str()
{
if (printer_type == PRINTER_TYPE::PRINTER_3DPrinter_P1)
return "Bambu Lab P1";
else if (printer_type == PRINTER_TYPE::PRINTER_3DPrinter_X1)
return "Bambu Lab X1";
else if (printer_type == PRINTER_TYPE::PRINTER_3DPrinter_X1_Carbon)
return "Bambu Lab X1 Carbon";
return _L("Unknown");
std::string display_name = get_preset_printer_model_name(printer_type);
if (!display_name.empty())
return display_name;
else
return _L("Unknown");
}
void MachineObject::set_access_code(std::string code)
@ -335,17 +304,6 @@ bool MachineObject::is_lan_mode_printer()
return result;
}
std::string MachineObject::get_printer_type_string()
{
if (printer_type == PRINTER_TYPE::PRINTER_3DPrinter_P1)
return "3DPrinter-P1";
else if (printer_type == PRINTER_TYPE::PRINTER_3DPrinter_X1)
return "3DPrinter-X1";
else if (printer_type == PRINTER_TYPE::PRINTER_3DPrinter_X1_Carbon)
return "3DPrinter-X1-Carbon";
return "3DPrinter";
}
MachineObject::MachineObject(NetworkAgent* agent, std::string name, std::string id, std::string ip)
:dev_name(name),
dev_id(id),
@ -370,7 +328,11 @@ MachineObject::MachineObject(NetworkAgent* agent, std::string name, std::string
ams_exist_bits = 0;
tray_exist_bits = 0;
tray_is_bbl_bits = 0;
ams_rfid_status = 0;
is_ams_need_update = false;
ams_insert_flag = false;
ams_power_on_flag = false;
ams_support_use_ams = false;
/* signals */
wifi_signal = "";
@ -1454,22 +1416,39 @@ int MachineObject::command_axis_control(std::string axis, double unit, double va
int MachineObject::command_start_calibration()
{
// fixed gcode file
json j;
j["print"]["command"] = "gcode_file";
j["print"]["param"] = "/usr/etc/print/auto_cali_for_user.gcode";
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
return this->publish_json(j.dump());
if (printer_type == "BL-P001"
|| printer_type == "BL-P002") {
// fixed gcode file
json j;
j["print"]["command"] = "gcode_file";
j["print"]["param"] = "/usr/etc/print/auto_cali_for_user.gcode";
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
return this->publish_json(j.dump());
} else {
json j;
j["print"]["command"] = "calibration";
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
return this->publish_json(j.dump());
}
}
int MachineObject::command_unload_filament()
{
// fixed gcode file
json j;
j["print"]["command"] = "gcode_file";
j["print"]["param"] = "/usr/etc/print/filament_unload.gcode";
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
return this->publish_json(j.dump());
if (printer_type == "BL-P001"
|| printer_type == "BL-P002") {
// fixed gcode file
json j;
j["print"]["command"] = "gcode_file";
j["print"]["param"] = "/usr/etc/print/filament_unload.gcode";
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
return this->publish_json(j.dump());
}
else {
json j;
j["print"]["command"] = "unload_filament";
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
return this->publish_json(j.dump());
}
}
@ -1617,6 +1596,7 @@ void MachineObject::reset()
iot_print_status = "";
print_status = "";
last_mc_print_stage = -1;
m_new_ver_list_exist = false;
subtask_ = nullptr;
@ -1694,6 +1674,40 @@ bool MachineObject::is_info_ready()
return false;
}
bool MachineObject::is_function_supported(PrinterFunction func)
{
std::string func_name;
switch (func) {
case FUNC_MONITORING:
func_name = "FUNC_MONITORING";
break;
case FUNC_TIMELAPSE:
func_name = "FUNC_TIMELAPSE";
break;
case FUNC_RECORDING:
func_name = "FUNC_RECORDING";
break;
case FUNC_FIRSTLAYER_INSPECT:
func_name = "FUNC_FIRSTLAYER_INSPECT";
break;
case FUNC_SPAGHETTI:
func_name = "FUNC_SPAGHETTI";
break;
case FUNC_FLOW_CALIBRATION:
func_name = "FUNC_FLOW_CALIBRATION";
break;
case FUNC_AUTO_LEVELING:
func_name = "FUNC_AUTO_LEVELING";
break;
case FUNC_CHAMBER_TEMP:
func_name = "FUNC_CHAMBER_TEMP";
break;
default:
return true;
}
return DeviceManager::is_function_supported(printer_type, func_name);
}
int MachineObject::publish_json(std::string json_str, int qos)
{
if (is_lan_mode_printer()) {
@ -1824,6 +1838,30 @@ int MachineObject::parse_json(std::string payload)
#pragma endregion
#pragma region online
// parse online info
try {
if (jj.contains("online")) {
if (jj["online"].contains("ahb")) {
if (jj["online"]["ahb"].get<bool>()) {
online_ahb = true;
} else {
online_ahb = false;
}
}
if (jj["online"].contains("rfid")) {
if (jj["online"]["rfid"].get<bool>()) {
online_rfid = true;
} else {
online_rfid = false;
}
}
}
} catch (...) {
;
}
#pragma endregion
#pragma region print_task
if (jj.contains("printer_type")) {
printer_type = parse_printer_type(jj["printer_type"].get<std::string>());
@ -2069,6 +2107,31 @@ int MachineObject::parse_json(std::string payload)
}
}
}
// new ver list
if (jj["upgrade_state"].contains("new_ver_list")) {
m_new_ver_list_exist = true;
new_ver_list.clear();
for (auto ver_item = jj["upgrade_state"]["new_ver_list"].begin(); ver_item != jj["upgrade_state"]["new_ver_list"].end(); ver_item++) {
ModuleVersionInfo ver_info;
if (ver_item->contains("name"))
ver_info.name = (*ver_item)["name"].get<std::string>();
else
continue;
if (ver_item->contains("cur_ver"))
ver_info.sw_ver = (*ver_item)["cur_ver"].get<std::string>();
if (ver_item->contains("new_ver"))
ver_info.sw_new_ver = (*ver_item)["new_ver"].get<std::string>();
if (ver_info.name == "ota") {
ota_new_version_number = ver_info.sw_new_ver;
}
new_ver_list.insert(std::make_pair(ver_info.name, ver_info));
}
} else {
new_ver_list.clear();
}
}
}
catch (...) {
@ -2181,6 +2244,10 @@ int MachineObject::parse_json(std::string payload)
if (jj["ams"].contains("tray_read_done_bits")) {
tray_read_done_bits = stol(jj["ams"]["tray_read_done_bits"].get<std::string>(), nullptr, 16);
}
if (jj["ams"].contains("tray_reading_bits")) {
tray_reading_bits = stol(jj["ams"]["tray_reading_bits"].get<std::string>(), nullptr, 16);
ams_support_use_ams = true;
}
if (jj["ams"].contains("tray_is_bbl_bits")) {
tray_is_bbl_bits = stol(jj["ams"]["tray_is_bbl_bits"].get<std::string>(), nullptr, 16);
}
@ -2194,6 +2261,15 @@ int MachineObject::parse_json(std::string payload)
if (jj["ams"].contains("tray_tar")) {
m_tray_tar = jj["ams"]["tray_tar"].get<std::string>();
}
if (jj["ams"].contains("insert_flag")) {
ams_insert_flag = jj["ams"]["insert_flag"].get<bool>();
}
if (jj["ams"].contains("ams_rfid_status"))
ams_rfid_status = jj["ams"]["ams_rfid_status"].get<int>();
if (jj["ams"].contains("power_on_flag")) {
ams_power_on_flag = jj["ams"]["power_on_flag"].get<bool>();
}
if (ams_exist_bits != last_ams_exist_bits
|| last_tray_exist_bits != last_tray_exist_bits
@ -2931,11 +3007,15 @@ std::map<std::string, MachineObject*> DeviceManager::get_my_machine_list()
std::map<std::string, MachineObject*> result;
for (auto it = userMachineList.begin(); it != userMachineList.end(); it++) {
if (!it->second)
continue;
if (!it->second->is_lan_mode_printer())
result.insert(std::make_pair(it->first, it->second));
}
for (auto it = localMachineList.begin(); it != localMachineList.end(); it++) {
if (!it->second)
continue;
if (it->second->has_access_right() && it->second->is_avaliable() && it->second->is_lan_mode_printer()) {
// remove redundant in userMachineList
if (result.find(it->first) == result.end()) {
@ -3004,7 +3084,7 @@ void DeviceManager::parse_user_print_info(std::string body)
if (!elem["dev_online"].is_null())
obj->m_is_online = elem["dev_online"].get<bool>();
if (elem.contains("dev_model_name") && !elem["dev_model_name"].is_null())
obj->printer_type = MachineObject::parse_iot_printer_type(elem["dev_model_name"].get<std::string>());
obj->printer_type = elem["dev_model_name"].get<std::string>();
if (!elem["task_status"].is_null())
obj->iot_print_status = elem["task_status"].get<std::string>();
if (elem.contains("dev_product_name") && !elem["dev_product_name"].is_null())
@ -3083,4 +3163,67 @@ void DeviceManager::load_last_machine()
}
}
json DeviceManager::function_table = json::object();
std::string DeviceManager::parse_printer_type(std::string type_str)
{
if (DeviceManager::function_table.contains("printers")) {
for (auto printer : DeviceManager::function_table["printers"]) {
if (printer.contains("model_id") && printer["model_id"].get<std::string>() == type_str) {
if (printer.contains("printer_type")) {
return printer["printer_type"].get<std::string>();
}
}
}
}
return "";
}
std::string DeviceManager::get_printer_display_name(std::string type_str)
{
if (DeviceManager::function_table.contains("printers")) {
for (auto printer : DeviceManager::function_table["printers"]) {
if (printer.contains("model_id") && printer["model_id"].get<std::string>() == type_str) {
if (printer.contains("display_name")) {
return printer["display_name"].get<std::string>();
}
}
}
}
return "";
}
bool DeviceManager::is_function_supported(std::string type_str, std::string function_name)
{
if (DeviceManager::function_table.contains("printers")) {
for (auto printer : DeviceManager::function_table["printers"]) {
if (printer.contains("model_id") && printer["model_id"].get<std::string>() == type_str) {
if (printer.contains("func")) {
if (printer["func"].contains(function_name))
return printer["func"][function_name].get<bool>();
}
}
}
}
return true;
}
bool DeviceManager::load_functional_config(std::string config_file)
{
std::ifstream json_file(config_file.c_str());
try {
if (json_file.is_open()) {
json_file >> DeviceManager::function_table;
return true;
} else {
BOOST_LOG_TRIVIAL(error) << "load functional config failed, file = " << config_file;
}
}
catch(...) {
BOOST_LOG_TRIVIAL(error) << "load functional config failed, file = " << config_file;
return false;
}
return true;
}
} // namespace Slic3r

View file

@ -7,6 +7,7 @@
#include <memory>
#include <chrono>
#include <boost/thread.hpp>
#include "nlohmann/json.hpp"
#include "libslic3r/ProjectTask.hpp"
#include "slic3r/Utils/json_diff.hpp"
#include "slic3r/Utils/NetworkAgent.hpp"
@ -32,17 +33,10 @@ inline int correct_filament_temperature(int filament_temp)
wxString get_stage_string(int stage);
using namespace nlohmann;
namespace Slic3r {
enum PRINTER_TYPE {
PRINTER_3DPrinter_UKNOWN,
PRINTER_3DPrinter_NONE,
PRINTER_3DPrinter_X1_Carbon, // BL-P001
PRINTER_3DPrinter_X1, // BL-P002
PRINTER_3DPrinter_P1, // BL-P003
};
enum PRINTING_STAGE {
PRINTING_STAGE_PRINTING = 0,
PRINTING_STAGE_BED_LEVELING,
@ -62,6 +56,18 @@ enum PRINTING_STAGE {
PRINTING_STAGE_COUNT
};
enum PrinterFunction {
FUNC_MONITORING = 0,
FUNC_TIMELAPSE,
FUNC_RECORDING,
FUNC_FIRSTLAYER_INSPECT,
FUNC_SPAGHETTI,
FUNC_FLOW_CALIBRATION,
FUNC_AUTO_LEVELING,
FUNC_CHAMBER_TEMP,
FUNC_MAX
};
enum PrintingSpeedLevel {
SPEED_LEVEL_INVALID = 0,
@ -105,6 +111,16 @@ enum AmsStatusMain {
AMS_STATUS_MAIN_UNKNOWN = 0xFF,
};
enum AmsRfidStatus {
AMS_RFID_IDLE = 0,
AMS_RFID_READING = 1,
AMS_RFID_GCODE_TRANS = 2,
AMS_RFID_GCODE_RUNNING = 3,
AMS_RFID_ASSITANT = 4,
AMS_RFID_SWITCH_FILAMENT= 5,
AMS_RFID_HAS_FILAMENT = 6
};
class AmsTray {
public:
AmsTray(std::string tray_id) {
@ -296,14 +312,13 @@ public:
std::string sn;
std::string hw_ver;
std::string sw_ver;
std::string sw_new_ver;
};
/* static members and functions */
static inline int m_sequence_id = 20000;
static PRINTER_TYPE parse_printer_type(std::string type_str);
static PRINTER_TYPE parse_iot_printer_type(std::string type_str);
static PRINTER_TYPE parse_preset_printer_type(std::string type_str);
static std::string get_preset_printer_model_name(PRINTER_TYPE printer_type);
static std::string parse_printer_type(std::string type_str);
static std::string get_preset_printer_model_name(std::string printer_type);
static bool is_bbl_filament(std::string tag_uid);
typedef std::function<void()> UploadedFn;
@ -321,8 +336,8 @@ public:
bool has_access_right() { return !access_code.empty(); }
void set_access_code(std::string code);
bool is_lan_mode_printer();
PRINTER_TYPE printer_type = PRINTER_3DPrinter_UKNOWN;
std::string get_printer_type_string();
//PRINTER_TYPE printer_type = PRINTER_3DPrinter_UKNOWN;
std::string printer_type; /* model_id */
wxString get_printer_type_display_str();
std::string product_name; // set by iot service, get /user/print
@ -344,6 +359,11 @@ public:
long tray_exist_bits = 0;
long tray_is_bbl_bits = 0;
long tray_read_done_bits = 0;
long tray_reading_bits = 0;
int ams_rfid_status = 0;
bool ams_insert_flag { false };
bool ams_power_on_flag { false };
bool ams_support_use_ams { false };
AmsStatusMain ams_status_main;
int ams_status_sub;
int ams_version = 0;
@ -373,6 +393,9 @@ public:
bool is_mapping_exceed_filament(std::vector<FilamentInfo>& result, int &exceed_index);
void reset_mapping_result(std::vector<FilamentInfo>& result);
/*online*/
bool online_rfid;
bool online_ahb;
/* temperature */
float nozzle_temp;
@ -413,6 +436,8 @@ public:
std::string ota_new_version_number;
std::string ahb_new_version_number;
std::map<std::string, ModuleVersionInfo> module_vers;
std::map<std::string, ModuleVersionInfo> new_ver_list;
bool m_new_ver_list_exist = false;
int upgrade_err_code = 0;
std::vector<FirmwareInfo> firmware_list;
@ -568,6 +593,7 @@ public:
void set_online_state(bool on_off);
bool is_online() { return m_is_online; }
bool is_info_ready();
bool is_function_supported(PrinterFunction func);
/* Msg for display MsgFn */
@ -630,6 +656,13 @@ public:
// get alive machine
std::map<std::string, MachineObject*> get_local_machine_list();
void load_last_machine();
static json function_table;
static std::string parse_printer_type(std::string type_str);
static std::string get_printer_display_name(std::string type_str);
static bool is_function_supported(std::string type_str, std::string function_name);
static bool load_functional_config(std::string config_file);
};
} // namespace Slic3r

View file

@ -51,6 +51,8 @@ DownloadProgressDialog::DownloadProgressDialog(wxString title)
Layout();
Fit();
CentreOnParent();
Bind(wxEVT_CLOSE_WINDOW, &DownloadProgressDialog::on_close, this);
}
bool DownloadProgressDialog::Show(bool show)
@ -91,6 +93,15 @@ bool DownloadProgressDialog::Show(bool show)
return DPIDialog::Show(show);
}
void DownloadProgressDialog::on_close(wxCloseEvent& event)
{
if (m_upgrade_job) {
m_upgrade_job->cancel();
m_upgrade_job->join();
}
event.Skip();
}
DownloadProgressDialog::~DownloadProgressDialog() {}
void DownloadProgressDialog::on_dpi_changed(const wxRect &suggested_rect) {}

View file

@ -33,6 +33,7 @@ class DownloadProgressDialog : public DPIDialog
{
protected:
bool Show(bool show) override;
void on_close(wxCloseEvent& event);
public:
DownloadProgressDialog(wxString title);
@ -42,7 +43,7 @@ public:
void update_release_note(std::string release_note, std::string version);
std::shared_ptr<BBLStatusBarSend> m_status_bar;
std::shared_ptr<UpgradeNetworkJob> m_upgrade_job;
std::shared_ptr<UpgradeNetworkJob> m_upgrade_job { nullptr };
wxPanel * m_panel_download;

View file

@ -4385,7 +4385,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
{
case EViewType::FeatureType:
{
append_headers({_u8L("Line type"), _u8L("Time"), _u8L("Percent"), "", _u8L("Display")}, offsets);
append_headers({_u8L("Line Type"), _u8L("Time"), _u8L("Percent"), "", _u8L("Display")}, offsets);
break;
}
case EViewType::Height: { imgui.title(_u8L("Layer Height (mm)")); break; }
@ -4998,6 +4998,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
ImGui::SameLine();
imgui.title(time_title);
std::string filament_str = _u8L("Filament");
std::string cost_str = _u8L("Cost");
std::string prepare_str = _u8L("Prepare time");
std::string print_str = _u8L("Model printing time");
std::string total_str = _u8L("Total");
@ -5006,7 +5007,10 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
if (time_mode.layers_times.empty())
max_len += ImGui::CalcTextSize(total_str.c_str()).x;
else {
max_len += std::max(ImGui::CalcTextSize(print_str.c_str()).x ,std::max(std::max(ImGui::CalcTextSize(prepare_str.c_str()).x, ImGui::CalcTextSize(total_str.c_str()).x), ImGui::CalcTextSize(filament_str.c_str()).x));
max_len += std::max(ImGui::CalcTextSize(cost_str.c_str()).x,
std::max(ImGui::CalcTextSize(print_str.c_str()).x,
std::max(std::max(ImGui::CalcTextSize(prepare_str.c_str()).x, ImGui::CalcTextSize(total_str.c_str()).x),
ImGui::CalcTextSize(filament_str.c_str()).x)));
}
//BBS display filament cost
@ -5031,6 +5035,17 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
auto it = std::find_if(time_mode.roles_times.begin(), time_mode.roles_times.end(), [role](const std::pair<ExtrusionRole, float>& item) { return role == item.first; });
return (it != time_mode.roles_times.end()) ? it->second : 0.0f;
};
//BBS: display cost of filaments
ImGui::Dummy({window_padding, window_padding});
ImGui::SameLine();
imgui.text(cost_str + ":");
ImGui::SameLine(max_len);
//char buf[64];
::sprintf(buf, "%.2f", ps.total_cost);
imgui.text(buf);
//BBS: start gcode is prepeare time
if (role_time(erCustom) != 0.0f) {
ImGui::Dummy({ window_padding, window_padding });

View file

@ -595,6 +595,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)
#endif
, m_in_render(false)
, m_main_toolbar(GLToolbar::Normal, "Main")
, m_separator_toolbar(GLToolbar::Normal, "Separator")
, m_assemble_view_toolbar(GLToolbar::Normal, "Assembly_View")
, m_return_toolbar()
, m_canvas_type(ECanvasType::CanvasView3D)
@ -1021,6 +1022,11 @@ void GLCanvas3D::enable_return_toolbar(bool enable)
m_return_toolbar.set_enabled(enable);
}
void GLCanvas3D::enable_separator_toolbar(bool enable)
{
m_separator_toolbar.set_enabled(enable);
}
void GLCanvas3D::enable_dynamic_background(bool enable)
{
m_dynamic_background_enabled = enable;
@ -2147,7 +2153,7 @@ void GLCanvas3D::bind_event_handlers()
m_canvas->Bind(wxEVT_PAINT, &GLCanvas3D::on_paint, this);
m_canvas->Bind(wxEVT_SET_FOCUS, &GLCanvas3D::on_set_focus, this);
m_event_handlers_bound = true;
m_canvas->Bind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
m_canvas->Bind(wxEVT_GESTURE_ZOOM, &GLCanvas3D::on_gesture, this);
m_canvas->Bind(wxEVT_GESTURE_ROTATE, &GLCanvas3D::on_gesture, this);
@ -2184,7 +2190,7 @@ void GLCanvas3D::unbind_event_handlers()
m_canvas->Unbind(wxEVT_PAINT, &GLCanvas3D::on_paint, this);
m_canvas->Unbind(wxEVT_SET_FOCUS, &GLCanvas3D::on_set_focus, this);
m_event_handlers_bound = false;
m_canvas->Unbind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
m_canvas->Unbind(wxEVT_GESTURE_ZOOM, &GLCanvas3D::on_gesture, this);
m_canvas->Unbind(wxEVT_GESTURE_ROTATE, &GLCanvas3D::on_gesture, this);
@ -2292,30 +2298,25 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
case WXK_CONTROL_M:
#endif /* __APPLE__ */
{
//#ifdef _WIN32
// if (wxGetApp().app_config->get("use_legacy_3DConnexion") == "1") {
//#endif //_WIN32
//#ifdef __APPLE__
// // On OSX use Cmd+Shift+M to "Show/Hide 3Dconnexion devices settings dialog"
// if ((evt.GetModifiers() & shiftMask) != 0) {
//#endif // __APPLE__
//
//#ifdef SUPPORT_3D_CONNEXION
// Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller();
// controller.show_settings_dialog(!controller.is_settings_dialog_shown());
// m_dirty = true;
//#endif
//#ifdef __APPLE__
// }
// else
// // and Cmd+M to minimize application
// wxGetApp().mainframe->Iconize();
//#endif // __APPLE__
//#ifdef _WIN32
// }
//#endif //_WIN32
post_event(SimpleEvent(EVT_GLTOOLBAR_CLONE));
#ifdef _WIN32
if (wxGetApp().app_config->get("use_legacy_3DConnexion") == "true") {
#endif //_WIN32
#ifdef __APPLE__
// On OSX use Cmd+Shift+M to "Show/Hide 3Dconnexion devices settings dialog"
if ((evt.GetModifiers() & shiftMask) != 0) {
#endif // __APPLE__
Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller();
controller.show_settings_dialog(!controller.is_settings_dialog_shown());
m_dirty = true;
#ifdef __APPLE__
}
else
// and Cmd+M to minimize application
wxGetApp().mainframe->Iconize();
#endif // __APPLE__
#ifdef _WIN32
}
#endif //_WIN32
break;
}
#ifdef __APPLE__
@ -2368,7 +2369,10 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
break;
// BBS
case '0': { select_view("topfront"); break; }
case '0': {
select_view("plate");
zoom_to_bed();
break; }
case '1': { select_view("top"); break; }
case '2': { select_view("bottom"); break; }
case '3': { select_view("front"); break; }
@ -2711,7 +2715,10 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
switch (keyCode) {
case '0':
case WXK_NUMPAD0: //0 on numpad
{ select_view("topfront"); break; }
{ select_view("plate");
zoom_to_bed();
break;
}
case '1':
case WXK_NUMPAD1: //1 on numpad
{ select_view("top"); break; }
@ -3332,8 +3339,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
else
rotate_target = volumes_bounding_box().center();
//BBS do not limit rotate in assemble view
//camera.rotate_local_with_target(Vec3d(rot.y(), rot.x(), 0.), rotate_target);
camera.rotate_on_sphere_with_target(rot.x(), rot.y(), true, rotate_target);
camera.rotate_local_with_target(Vec3d(rot.y(), rot.x(), 0.), rotate_target);
//camera.rotate_on_sphere_with_target(rot.x(), rot.y(), false, rotate_target);
}
else {
#ifdef SUPPORT_FEEE_CAMERA
@ -4994,6 +5001,9 @@ bool GLCanvas3D::_init_toolbars()
if (!_init_return_toolbar())
return false;
if (!_init_separator_toolbar())
return false;
#if 0
if (!_init_view_toolbar())
return false;
@ -5134,16 +5144,6 @@ bool GLCanvas3D::_init_main_toolbar()
if (!m_main_toolbar.add_item(item))
return false;
GLToolbarItem::Data sperate_item;
sperate_item.name = "seperatetag";
sperate_item.icon_filename = "seperator.svg";
sperate_item.sprite_id = ++item.sprite_id;
sperate_item.left.action_callback = [this]() { };
sperate_item.visibility_callback = []()->bool { return true; };
sperate_item.enabling_callback = []()->bool { return false; };
if (!m_main_toolbar.add_item(sperate_item))
return false;
return true;
}
@ -5207,16 +5207,6 @@ bool GLCanvas3D::_init_assemble_view_toolbar()
m_assemble_view_toolbar.set_separator_size(10);
m_assemble_view_toolbar.set_gap_size(4);
GLToolbarItem::Data sperate_item;
sperate_item.name = "start_seperator";
sperate_item.icon_filename = "seperator.svg";
sperate_item.sprite_id = 0;
sperate_item.left.action_callback = [this]() {};
sperate_item.visibility_callback = []()->bool { return true; };
sperate_item.enabling_callback = []()->bool { return false; };
if (!m_assemble_view_toolbar.add_item(sperate_item))
return false;
GLToolbarItem::Data item;
item.name = "assembly_view";
item.icon_filename = "toolbar_assemble.svg";
@ -5245,6 +5235,45 @@ bool GLCanvas3D::_init_return_toolbar()
return m_return_toolbar.init();
}
bool GLCanvas3D::_init_separator_toolbar()
{
if (!m_separator_toolbar.is_enabled())
return true;
BackgroundTexture::Metadata background_data;
background_data.filename = "toolbar_background.png";
background_data.left = 0;
background_data.top = 0;
background_data.right = 0;
background_data.bottom = 0;
if (!m_separator_toolbar.init(background_data))
{
// unable to init the toolbar texture, disable it
m_separator_toolbar.set_enabled(false);
return true;
}
m_separator_toolbar.set_layout_type(GLToolbar::Layout::Horizontal);
//BBS: assemble toolbar is at the top and right, we don't need the rounded-corner effect at the left side and the top side
m_separator_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Left);
m_separator_toolbar.set_vertical_orientation(GLToolbar::Layout::VO_Top);
m_separator_toolbar.set_border(5.0f);
GLToolbarItem::Data sperate_item;
sperate_item.name = "start_seperator";
sperate_item.icon_filename = "seperator.svg";
sperate_item.sprite_id = 0;
sperate_item.left.action_callback = [this]() {};
sperate_item.visibility_callback = []()->bool { return true; };
sperate_item.enabling_callback = []()->bool { return false; };
if (!m_separator_toolbar.add_item(sperate_item))
return false;
return true;
}
// BBS
#if 0
@ -5647,13 +5676,13 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with
switch (build_volume.type()) {
case BuildVolume::Type::Rectangle: {
const BoundingBox3Base<Vec3d> bed_bb = build_volume.bounding_volume().inflated(BuildVolume::SceneEpsilon);
m_volumes.set_print_volume({ 0, // circle
m_volumes.set_print_volume({ 0, // Rectangle
{ float(bed_bb.min.x()), float(bed_bb.min.y()), float(bed_bb.max.x()), float(bed_bb.max.y()) },
{ 0.0f, float(build_volume.printable_height()) } });
break;
}
case BuildVolume::Type::Circle: {
m_volumes.set_print_volume({ 1, // rectangle
m_volumes.set_print_volume({ 1, // Circle
{ unscaled<float>(build_volume.circle().center.x()), unscaled<float>(build_volume.circle().center.y()), unscaled<float>(build_volume.circle().radius + BuildVolume::SceneEpsilon), 0.0f },
{ 0.0f, float(build_volume.printable_height() + BuildVolume::SceneEpsilon) } });
break;
@ -5844,6 +5873,7 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale()
//BBS: GUI refactor: GLToolbar
m_main_toolbar.set_scale(sc);
m_assemble_view_toolbar.set_scale(sc);
m_separator_toolbar.set_scale(sc);
collapse_toolbar.set_scale(sc);
size *= m_retina_helper->get_scale_factor();
@ -5853,15 +5883,16 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale()
//BBS: GUI refactor: GLToolbar
m_main_toolbar.set_icons_size(GLGizmosManager::Default_Icons_Size * scale);
m_assemble_view_toolbar.set_icons_size(size);
m_separator_toolbar.set_icons_size(size);
collapse_toolbar.set_icons_size(size);
#endif // ENABLE_RETINA_GL
//BBS: GUI refactor: GLToolbar
#if BBS_TOOLBAR_ON_TOP
float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f;
float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : GLToolbar::Default_Icons_Size;
float top_tb_width = m_main_toolbar.get_width() + m_gizmos.get_scaled_total_width() + m_assemble_view_toolbar.get_width() + collapse_toolbar_width;
int items_cnt = m_main_toolbar.get_visible_items_cnt() + m_gizmos.get_selectable_icons_cnt() + m_assemble_view_toolbar.get_visible_items_cnt() + collapse_toolbar.get_visible_items_cnt();
float top_tb_width = m_main_toolbar.get_width() + m_gizmos.get_scaled_total_width() + m_assemble_view_toolbar.get_width() + m_separator_toolbar.get_width() + collapse_toolbar_width;
int items_cnt = m_main_toolbar.get_visible_items_cnt() + m_gizmos.get_selectable_icons_cnt() + m_assemble_view_toolbar.get_visible_items_cnt() + m_separator_toolbar.get_visible_items_cnt() + collapse_toolbar.get_visible_items_cnt();
float noitems_width = top_tb_width - size * items_cnt; // width of separators and borders in top toolbars
// calculate scale needed for items in all top toolbars
@ -5917,6 +5948,7 @@ void GLCanvas3D::_render_overlays()
//BBS: GUI refactor: GLToolbar
m_main_toolbar.set_scale(scale);
m_assemble_view_toolbar.set_scale(scale);
m_separator_toolbar.set_scale(scale);
wxGetApp().plater()->get_collapse_toolbar().set_scale(scale);
m_gizmos.set_overlay_scale(scale);
#else
@ -5929,10 +5961,13 @@ void GLCanvas3D::_render_overlays()
//BBS: GUI refactor: GLToolbar
m_main_toolbar.set_icons_size(gizmo_size);
m_assemble_view_toolbar.set_icons_size(gizmo_size);
m_separator_toolbar.set_icons_size(gizmo_size);
wxGetApp().plater()->get_collapse_toolbar().set_icons_size(size);
m_gizmos.set_overlay_icon_size(gizmo_size);
#endif // ENABLE_RETINA_GL
_render_separator_toolbar_right();
_render_separator_toolbar_left();
_render_main_toolbar();
//BBS: GUI refactor: GLToolbar
_render_imgui_select_plate_toolbar();
@ -6135,8 +6170,9 @@ void GLCanvas3D::_render_main_toolbar()
float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f;
float gizmo_width = m_gizmos.get_scaled_total_width();
float assemble_width = m_assemble_view_toolbar.get_width();
float separator_width = m_separator_toolbar.get_width();
float top = 0.5f * (float)cnv_size.get_height() * inv_zoom;
float left = std::max(-0.5f * cnv_size.get_width(), -0.5f * (m_main_toolbar.get_width() + gizmo_width + assemble_width - collapse_toolbar_width)) * inv_zoom;
float left = std::max(-0.5f * cnv_size.get_width(), -0.5f * (m_main_toolbar.get_width() + separator_width + gizmo_width + assemble_width - collapse_toolbar_width)) * inv_zoom;
#else
float gizmo_height = m_gizmos.get_scaled_total_height();
float space_height = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale();
@ -6327,8 +6363,9 @@ void GLCanvas3D::_render_assemble_view_toolbar() const
float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f;
float gizmo_width = m_gizmos.get_scaled_total_width();
float assemble_width = m_assemble_view_toolbar.get_width();
float separator_width = m_separator_toolbar.get_width();
float top = 0.5f * (float)cnv_size.get_height() * inv_zoom;
float main_toolbar_left = std::max(-0.5f * cnv_size.get_width(), -0.5f * (m_main_toolbar.get_width() + gizmo_width + assemble_width - collapse_toolbar_width)) * inv_zoom;
float main_toolbar_left = std::max(-0.5f * cnv_size.get_width(), -0.5f * (m_main_toolbar.get_width() + gizmo_width + assemble_width - separator_width - collapse_toolbar_width)) * inv_zoom;
float left = main_toolbar_left + (m_main_toolbar.get_width() + gizmo_width) * inv_zoom;
//float left = 0.5f * (m_main_toolbar.get_width() + gizmo_width - m_assemble_view_toolbar.get_width() + collapse_toolbar_width) * inv_zoom;
#else
@ -6397,6 +6434,48 @@ void GLCanvas3D::_render_return_toolbar() const
imgui.end();
}
void GLCanvas3D::_render_separator_toolbar_right() const
{
if (!m_separator_toolbar.is_enabled())
return;
Size cnv_size = get_canvas_size();
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar();
float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f;
float gizmo_width = m_gizmos.get_scaled_total_width();
float assemble_width = m_assemble_view_toolbar.get_width();
float separator_width = m_separator_toolbar.get_width();
float top = 0.5f * (float)cnv_size.get_height() * inv_zoom;
float main_toolbar_left = std::max(-0.5f * cnv_size.get_width(), -0.5f * (m_main_toolbar.get_width() + gizmo_width + assemble_width - collapse_toolbar_width)) * inv_zoom;
float left = main_toolbar_left + (m_main_toolbar.get_width() + gizmo_width) * inv_zoom;
m_separator_toolbar.set_position(top, left);
m_separator_toolbar.render(*this,GLToolbarItem::SeparatorLine);
}
void GLCanvas3D::_render_separator_toolbar_left() const
{
if (!m_separator_toolbar.is_enabled())
return;
Size cnv_size = get_canvas_size();
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar();
float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f;
float gizmo_width = m_gizmos.get_scaled_total_width();
float assemble_width = m_assemble_view_toolbar.get_width();
float separator_width = m_separator_toolbar.get_width();
float top = 0.5f * (float)cnv_size.get_height() * inv_zoom;
float main_toolbar_left = std::max(-0.5f * cnv_size.get_width(), -0.5f * (m_main_toolbar.get_width() + gizmo_width + assemble_width + separator_width - collapse_toolbar_width)) * inv_zoom;
float left = main_toolbar_left + (m_main_toolbar.get_width()) * inv_zoom;
m_separator_toolbar.set_position(top, left);
m_separator_toolbar.render(*this,GLToolbarItem::SeparatorLine);
}
void GLCanvas3D::_render_collapse_toolbar() const
{
GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar();
@ -6554,10 +6633,10 @@ void GLCanvas3D::_render_paint_toolbar() const
float gray = 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2];
if (gray < 80){
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), item_text.c_str());
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), item_text.c_str());
} else{
ImGui::TextColored(ImVec4(0.0f, 0.0f, 0.0f, 1.0f), item_text.c_str());
}
}
}
ImGui::AlignTextToFramePadding();
imgui.end();

View file

@ -399,6 +399,7 @@ private:
GLGizmosManager m_gizmos;
//BBS: GUI refactor: GLToolbar
mutable GLToolbar m_main_toolbar;
mutable GLToolbar m_separator_toolbar;
mutable IMToolbar m_sel_plate_toolbar;
mutable GLToolbar m_assemble_view_toolbar;
mutable IMReturnToolbar m_return_toolbar;
@ -667,6 +668,7 @@ public:
void enable_select_plate_toolbar(bool enable);
void enable_assemble_view_toolbar(bool enable);
void enable_return_toolbar(bool enable);
void enable_separator_toolbar(bool enable);
void enable_dynamic_background(bool enable);
void enable_labels(bool enable) { m_labels.enable(enable); }
void enable_slope(bool enable) { m_slope.enable(enable); }
@ -686,6 +688,8 @@ public:
float get_main_toolbar_width() { return m_main_toolbar.get_width();}
float get_assemble_view_toolbar_width() { return m_assemble_view_toolbar.get_width(); }
float get_assemble_view_toolbar_height() { return m_assemble_view_toolbar.get_height(); }
float get_separator_toolbar_width() { return m_separator_toolbar.get_width(); }
float get_separator_toolbar_height() { return m_separator_toolbar.get_height(); }
float get_collapse_toolbar_width();
float get_collapse_toolbar_height();
@ -923,6 +927,7 @@ private:
bool _update_imgui_select_plate_toolbar();
bool _init_assemble_view_toolbar();
bool _init_return_toolbar();
bool _init_separator_toolbar();
// BBS
//bool _init_view_toolbar();
bool _init_collapse_toolbar();
@ -967,6 +972,8 @@ private:
void _render_imgui_select_plate_toolbar() const;
void _render_assemble_view_toolbar() const;
void _render_return_toolbar() const;
void _render_separator_toolbar_right() const;
void _render_separator_toolbar_left() const;
void _render_collapse_toolbar() const;
// BBS
//void _render_view_toolbar() const;

View file

@ -538,7 +538,7 @@ bool GLToolbar::update_items_state()
return ret;
}
void GLToolbar::render(const GLCanvas3D& parent)
void GLToolbar::render(const GLCanvas3D& parent,GLToolbarItem::EType type)
{
if (!m_enabled || m_items.empty())
return;
@ -549,7 +549,7 @@ void GLToolbar::render(const GLCanvas3D& parent)
switch (m_layout.type)
{
default:
case Layout::Horizontal: { render_horizontal(parent); break; }
case Layout::Horizontal: { render_horizontal(parent,type); break; }
case Layout::Vertical: { render_vertical(parent); break; }
}
}
@ -1386,7 +1386,7 @@ void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighte
}
}
void GLToolbar::render_horizontal(const GLCanvas3D& parent)
void GLToolbar::render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType type)
{
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
float factor = inv_zoom * m_layout.scale;
@ -1404,6 +1404,8 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent)
float left = m_layout.left;
float top = m_layout.top;
float right = left + scaled_width;
if (type == GLToolbarItem::SeparatorLine)
right = left + scaled_width * 0.5;
float bottom = top - scaled_height;
render_background(left, top, right, bottom, scaled_border);

View file

@ -68,6 +68,7 @@ public:
//BBS: GUI refactor: GLToolbar
ActionWithText,
ActionWithTextImage,
SeparatorLine,
Num_Types
};
@ -400,7 +401,7 @@ public:
// returns true if any item changed its state
bool update_items_state();
void render(const GLCanvas3D& parent);
void render(const GLCanvas3D& parent,GLToolbarItem::EType type = GLToolbarItem::Action);
void render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighted_item);
bool on_mouse(wxMouseEvent& evt, GLCanvas3D& parent);
@ -429,7 +430,7 @@ private:
int contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const;
void render_background(float left, float top, float right, float bottom, float border) const;
void render_horizontal(const GLCanvas3D& parent);
void render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType type);
void render_vertical(const GLCanvas3D& parent);
bool generate_icons_texture();

View file

@ -1095,6 +1095,9 @@ void GUI_App::post_init()
hms_query->check_hms_info();
});
std::string functional_config_file = Slic3r::resources_dir() + "/config.json";
DeviceManager::load_functional_config(encode_path(functional_config_file.c_str()));
BOOST_LOG_TRIVIAL(info) << "finished post_init";
//BBS: remove the single instance currently
/*#ifdef _WIN32
@ -1481,6 +1484,19 @@ void GUI_App::restart_networking()
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(" exit, m_agent=%1%")%m_agent;
}
void GUI_App::remove_old_networking_plugins()
{
auto plugin_folder = boost::filesystem::path(wxStandardPaths::Get().GetUserDataDir().ToUTF8().data()) / "plugins";
if (boost::filesystem::exists(plugin_folder)) {
BOOST_LOG_TRIVIAL(info) << "[remove_old_networking_plugins] remove the directory "<<plugin_folder.string();
try {
fs::remove_all(plugin_folder);
} catch (...) {
BOOST_LOG_TRIVIAL(error) << "Failed removing the plugins directory " << plugin_folder.string();
}
}
}
int GUI_App::updating_bambu_networking()
{
DownloadProgressDialog dlg(_L("Downloading Bambu Network Plug-in"));
@ -1838,6 +1854,20 @@ bool GUI_App::on_init_inner()
CBaseException::set_log_folder(data_dir());
#endif
wxGetApp().Bind(wxEVT_QUERY_END_SESSION, [this](auto & e) {
if (mainframe) {
wxCloseEvent e2(wxEVT_CLOSE_WINDOW);
e2.SetCanVeto(true);
mainframe->GetEventHandler()->ProcessEvent(e2);
if (e2.GetVeto()) {
e.Veto();
return;
}
}
for (auto d : dialogStack)
d->EndModal(wxID_CANCEL);
});
std::map<std::string, std::string> extra_headers = get_extra_header();
Slic3r::Http::set_extra_headers(extra_headers);
@ -1896,10 +1926,20 @@ bool GUI_App::on_init_inner()
init_fonts();
if (m_last_config_version) {
if (*m_last_config_version < *Semver::parse(SLIC3R_VERSION))
check_older_app_config(*m_last_config_version, true);
} else {
check_older_app_config(Semver(), false);
int last_major = m_last_config_version->maj();
int last_minor = m_last_config_version->min();
int last_patch = m_last_config_version->patch()/100;
std::string studio_ver = SLIC3R_VERSION;
int cur_major = atoi(studio_ver.substr(0,2).c_str());
int cur_minor = atoi(studio_ver.substr(3,2).c_str());
int cur_patch = atoi(studio_ver.substr(6,2).c_str());
BOOST_LOG_TRIVIAL(info) << boost::format("last app version {%1%.%2%.%3%}, current version {%4%.%5%.%6%}")
%last_major%last_minor%last_patch%cur_major%cur_minor%cur_patch;
if ((last_major != cur_major)
||(last_minor != cur_minor)
||(last_patch != cur_patch)) {
remove_old_networking_plugins();
}
}
app_config->set("version", SLIC3R_VERSION);
@ -2538,21 +2578,21 @@ float GUI_App::toolbar_icon_scale(const bool is_limited/* = false*/) const
const float icon_sc = m_em_unit * 0.1f;
#endif // __APPLE__
return icon_sc;
//return icon_sc;
//const std::string& auto_val = app_config->get("toolkit_size");
const std::string& auto_val = app_config->get("toolkit_size");
//if (auto_val.empty())
// return icon_sc;
if (auto_val.empty())
return icon_sc;
//int int_val = 100;
//// correct value in respect to toolkit_size
//int_val = std::min(atoi(auto_val.c_str()), int_val);
int int_val = 100;
// correct value in respect to toolkit_size
int_val = std::min(atoi(auto_val.c_str()), int_val);
//if (is_limited && int_val < 50)
// int_val = 50;
if (is_limited && int_val < 50)
int_val = 50;
//return 0.01f * int_val * icon_sc;
return 0.01f * int_val * icon_sc;
}
void GUI_App::set_auto_toolbar_icon_scale(float scale) const
@ -2911,6 +2951,7 @@ void GUI_App::request_user_logout()
m_agent->set_user_selected_machine("");
/* delete old user settings */
m_device_manager->clean_user_info();
GUI::wxGetApp().sidebar().load_ams_list({});
GUI::wxGetApp().remove_user_presets();
GUI::wxGetApp().stop_sync_user_preset();
}
@ -2920,7 +2961,9 @@ int GUI_App::request_user_unbind(std::string dev_id)
{
int result = -1;
if (m_agent) {
return m_agent->unbind(dev_id);
result = m_agent->unbind(dev_id);
BOOST_LOG_TRIVIAL(info) << "request_user_unbind, dev_id = " << dev_id << ", result = " << result;
return result;
}
return result;
}
@ -3008,6 +3051,31 @@ std::string GUI_App::handle_web_request(std::string cmd)
}
}
}
else if (command_str.compare("homepage_delete_recentfile") == 0) {
if (root.get_child_optional("data") != boost::none) {
pt::ptree data_node = root.get_child("data");
boost::optional<std::string> path = data_node.get_optional<std::string>("path");
if (path.has_value()) {
this->request_remove_project(path.value());
}
}
}
else if (command_str.compare("homepage_delete_all_recentfile") == 0) {
this->request_remove_project("");
}
else if (command_str.compare("homepage_explore_recentfile") == 0) {
if (root.get_child_optional("data") != boost::none) {
pt::ptree data_node = root.get_child("data");
boost::optional<std::string> path = data_node.get_optional<std::string>("path");
if (path.has_value())
{
boost::filesystem::path NowFile(path.value());
std::string FolderPath = NowFile.parent_path().make_preferred().string();
desktop_open_any_folder(FolderPath);
}
}
}
else if (command_str.compare("homepage_open_hotspot") == 0) {
if (root.get_child_optional("data") != boost::none) {
pt::ptree data_node = root.get_child("data");
@ -3042,6 +3110,16 @@ std::string GUI_App::handle_web_request(std::string cmd)
wxPostEvent(mainframe, e);
}
}
else if (command_str.compare("userguide_wiki_open") == 0) {
if (root.get_child_optional("data") != boost::none) {
pt::ptree data_node = root.get_child("data");
boost::optional<std::string> path = data_node.get_optional<std::string>("url");
if (path.has_value()) {
wxLaunchDefaultBrowser(path.value());
}
}
}
}
}
catch (...) {
@ -3116,6 +3194,11 @@ void GUI_App::request_open_project(std::string project_id)
CallAfter([this, project_id] { mainframe->open_recent_project(-1, wxString::FromUTF8(project_id)); });
}
void GUI_App::request_remove_project(std::string project_id)
{
mainframe->remove_recent_project(-1, wxString::FromUTF8(project_id));
}
void GUI_App::handle_http_error(unsigned int status, std::string body)
{
// tips body size must less than 1024
@ -3335,6 +3418,10 @@ void GUI_App::remove_user_presets()
{
if (preset_bundle && m_agent) {
preset_bundle->remove_users_preset(*app_config);
std::string user_id = m_agent->get_user_id();
preset_bundle->remove_user_presets_directory(user_id);
//update ui
mainframe->update_side_preset_ui();
}

View file

@ -376,6 +376,7 @@ public:
void download_project(std::string project_id);
void request_project_download(std::string project_id);
void request_open_project(std::string project_id);
void request_remove_project(std::string project_id);
void handle_http_error(unsigned int status, std::string body);
void on_http_error(wxCommandEvent &evt);
@ -532,6 +533,7 @@ private:
bool on_init_network(bool try_backup = false);
void init_networking_callbacks();
void init_app_config();
void remove_old_networking_plugins();
//BBS set extra header for http request
std::map<std::string, std::string> get_extra_header();
void init_http_extra_header();

View file

@ -991,6 +991,7 @@ void MenuFactory::create_bbl_part_menu()
[]() { return plater()->can_split(true); }, m_parent);
menu->AppendSeparator();
append_menu_item_per_object_settings(menu);
append_menu_item_change_type(menu);
}
void MenuFactory::create_bbl_assemble_part_menu()
@ -1320,7 +1321,9 @@ void MenuFactory::append_menu_item_change_filament(wxMenu* menu)
for (int i = 1; i <= filaments_cnt; i++)
{
bool is_active_extruder = i == initial_extruder;
// BBS
//bool is_active_extruder = i == initial_extruder;
bool is_active_extruder = false;
int icon_idx = i == 0 ? 0 : i - 1;
const wxString& item_name = wxString::Format(_L("Filament %d"), i) +

View file

@ -1842,7 +1842,10 @@ void ObjectList::load_modifier(const wxArrayString& input_files, ModelObject& mo
new_volume->name = boost::filesystem::path(input_file).filename().string();
// set a default extruder value, since user can't add it manually
// BBS
new_volume->config.set_key_value("extruder", new ConfigOptionInt(1));
int extruder_id = 0;
if (model_object.config.has("extruder"))
extruder_id = model_object.config.opt_int("extruder");
new_volume->config.set_key_value("extruder", new ConfigOptionInt(extruder_id));
// update source data
new_volume->source.input_file = input_file;
new_volume->source.object_idx = obj_idx;
@ -1945,7 +1948,10 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
new_volume->name = into_u8(name);
// set a default extruder value, since user can't add it manually
// BBS
new_volume->config.set_key_value("extruder", new ConfigOptionInt(1));
int extruder_id = 0;
if (model_object.config.has("extruder"))
extruder_id = model_object.config.opt_int("extruder");
new_volume->config.set_key_value("extruder", new ConfigOptionInt(extruder_id));
new_volume->source.is_from_builtin_objects = true;
select_item([this, obj_idx, new_volume]() {
@ -1962,28 +1968,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx);
// apply the instance transform to all volumes and reset instance transform except the offset
{
const Geometry::Transformation &instance_transformation = model_object.instances[0]->get_transformation();
Vec3d original_instance_center = instance_transformation.get_offset();
const Transform3d &transformation_matrix = instance_transformation.get_matrix();
for (ModelVolume *volume : model_object.volumes) {
const Transform3d &volume_matrix = volume->get_matrix();
Transform3d new_matrix = transformation_matrix * volume_matrix;
volume->set_transformation(new_matrix);
}
model_object.instances[0]->set_transformation(Geometry::Transformation());
model_object.ensure_on_bed();
// keep new instance center the same as the original center
model_object.translate(-original_instance_center);
model_object.origin_translation += original_instance_center;
model_object.translate_instances(model_object.origin_translation);
model_object.origin_translation = Vec3d::Zero();
// update the cache data in selection to keep the data of ModelVolume and GLVolume are consistent
wxGetApp().plater()->update();
}
apply_object_instance_transfrom_to_all_volumes(&model_object);
selection_changed();
@ -2069,6 +2054,52 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name
#endif /* _DEBUG */
}
void ObjectList::load_mesh_part(const TriangleMesh& mesh, const wxString& name, bool center)
{
wxDataViewItem item = GetSelection();
// we can add volumes for Object or Instance
if (!item || !(m_objects_model->GetItemType(item) & (itObject | itInstance)))
return;
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
if (obj_idx < 0) return;
// Get object item, if Instance is selected
if (m_objects_model->GetItemType(item) & itInstance)
item = m_objects_model->GetItemById(obj_idx);
take_snapshot("Load Mesh Part");
ModelObject* mo = (*m_objects)[obj_idx];
// apply the instance transform to all volumes and reset instance transform except the offset
apply_object_instance_transfrom_to_all_volumes(mo);
ModelVolume* mv = mo->add_volume(mesh);
Vec3d instance_bbox = mo->mesh().bounding_box().size();
Vec3d offset = mv->get_offset() + Vec3d(0, 0, instance_bbox[2] / 2);
mv->set_offset(offset);
mv->name = name.ToStdString();
std::vector<ModelVolume*> volumes;
volumes.push_back(mv);
wxDataViewItemArray items = reorder_volumes_and_get_selection(obj_idx, [volumes](const ModelVolume* volume) {
return std::find(volumes.begin(), volumes.end(), volume) != volumes.end(); });
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx);
if (items.size() > 1) {
m_selection_mode = smVolume;
m_last_selected_item = wxDataViewItem(nullptr);
}
select_items(items);
selection_changed();
//BBS: notify partplate the modify
notify_instance_updated(obj_idx);
}
//BBS
void ObjectList::del_object(const int obj_idx, bool refresh_immediately)
{
@ -5006,5 +5037,28 @@ bool ObjectList::has_paint_on_segmentation()
return m_objects_model->HasInfoItem(InfoItemType::MmuSegmentation);
}
void ObjectList::apply_object_instance_transfrom_to_all_volumes(ModelObject *model_object) {
const Geometry::Transformation &instance_transformation = model_object->instances[0]->get_transformation();
Vec3d original_instance_center = instance_transformation.get_offset();
const Transform3d &transformation_matrix = instance_transformation.get_matrix();
for (ModelVolume *volume : model_object->volumes) {
const Transform3d &volume_matrix = volume->get_matrix();
Transform3d new_matrix = transformation_matrix * volume_matrix;
volume->set_transformation(new_matrix);
}
model_object->instances[0]->set_transformation(Geometry::Transformation());
model_object->ensure_on_bed();
// keep new instance center the same as the original center
model_object->translate(-original_instance_center);
model_object->origin_translation += original_instance_center;
model_object->translate_instances(model_object->origin_translation);
model_object->origin_translation = Vec3d::Zero();
// update the cache data in selection to keep the data of ModelVolume and GLVolume are consistent
wxGetApp().plater()->update();
}
} //namespace GUI
} //namespace Slic3r

View file

@ -281,6 +281,8 @@ public:
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
void load_shape_object(const std::string &type_name);
void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true);
// BBS
void load_mesh_part(const TriangleMesh& mesh, const wxString& name, bool center = true);
void del_object(const int obj_idx, bool refresh_immediately = true);
void del_subobject_item(wxDataViewItem& item);
void del_settings_from_config(const wxDataViewItem& parent_item);
@ -450,6 +452,9 @@ private:
void OnEditingStarted(wxDataViewEvent &event);
void OnEditingDone(wxDataViewEvent &event);
// apply the instance transform to all volumes and reset instance transform except the offset
void apply_object_instance_transfrom_to_all_volumes(ModelObject *model_object);
std::vector<int> m_columns_width;
};

View file

@ -73,16 +73,26 @@ void GridCellIconRenderer::Draw(wxGrid& grid,
//not changed
return;
}
if (!table->m_icon_col_width) {
//if (!table->m_icon_col_width) {
table->m_icon_row_height = grid.GetRowSize(row);
table->m_icon_col_width = grid.GetColSize(col);
}
//}
wxBitmap& bitmap = table->get_undo_bitmap();
int bitmap_width = bitmap.GetWidth();
int bitmap_height = bitmap.GetHeight();
int offset_x = (table->m_icon_col_width - bitmap_width)/2;
int offset_y = (table->m_icon_row_height - bitmap_height)/2;
#ifdef __WXOSX_COCOA__
auto lock_pos = wxPoint(rect.x + offset_x, rect.y + offset_y);
auto left = (28 - 12) / 2;
auto top = (32 - 12) / 2;
lock_pos.x += left;
lock_pos.y += top;
dc.DrawBitmap(bitmap, lock_pos);
#else
dc.DrawBitmap(bitmap, wxPoint(rect.x + offset_x, rect.y + offset_y));
#endif
//dc.SetPen(*wxGREEN_PEN);
//dc.SetBrush(*wxTRANSPARENT_BRUSH);
@ -148,17 +158,15 @@ void GridCellFilamentsEditor::Create(wxWindow* parent,
if ( !m_allowOthers )
style |= wxCB_READONLY;
wxBitmapComboBox *bitmap_combo = new wxBitmapComboBox(parent, id, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
m_choices,
style);
::ComboBox *bitmap_combo = new ComboBox(parent, id, wxEmptyString,
wxDefaultPosition, wxSize(((*m_icons)[0])->GetWidth() + 10, -1), 0, nullptr, CB_NO_DROP_ICON | CB_NO_TEXT | wxCB_READONLY);
if (m_icons) {
int array_count = m_choices.GetCount();
int icon_count = m_icons->size();
for (int i = 0; i < array_count; i++)
{
wxBitmap* bitmap = (i < icon_count) ? (*m_icons)[i] : (*m_icons)[0];
bitmap_combo->SetItemBitmap(i, *bitmap);
bitmap_combo->Append(m_choices[i], *bitmap);
}
}
m_control = bitmap_combo;
@ -214,7 +222,10 @@ void GridCellFilamentsEditor::BeginEdit(int row, int col, wxGrid* grid)
}
//m_value = grid->GetTable()->GetValue(row, col);
Reset(); // this updates combo box to correspond to m_value
//Reset(); // this updates combo box to correspond to m_value
int pos = Combo()->FindString(m_value);
if (pos == wxNOT_FOUND) pos = 0;
Combo()->SetSelection(pos);
Combo()->SetFocus();
@ -223,7 +234,7 @@ void GridCellFilamentsEditor::BeginEdit(int row, int col, wxGrid* grid)
// choice is made in it under OS X. The bug is almost certainly due to a
// problem in focus events generation logic but it's not obvious to fix and
// for now this at least allows to use wxGrid.
Combo()->Popup();
//Combo()->Popup();
#endif
if (evtHandler)
@ -254,7 +265,6 @@ bool GridCellFilamentsEditor::EndEdit(int WXUNUSED(row),
return true;
}
wxGridActivationResult GridCellFilamentsEditor::TryActivate(int row, int col, wxGrid* grid, const wxGridActivationSource& actSource)
{
ObjectGridTable *table = dynamic_cast<ObjectGridTable *>(grid->GetTable());
@ -305,55 +315,222 @@ void GridCellFilamentsEditor::DoActivate(int row, int col, wxGrid* grid)
}
}
// ----------------------------------------------------------------------------
// GridCellFilamentsRenderer
// ----------------------------------------------------------------------------
void GridCellFilamentsRenderer::Draw(wxGrid& grid,
wxGridCellAttr& attr,
wxDC& dc,
const wxRect& rect,
int row, int col,
bool isSelected)
void GridCellFilamentsRenderer::Draw(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, const wxRect &rect, int row, int col, bool isSelected)
{
ObjectGridTable *table = dynamic_cast<ObjectGridTable *>(grid.GetTable());
wxRect text_rect = rect;
ObjectGridTable *table = dynamic_cast<ObjectGridTable *>(grid.GetTable());
wxRect text_rect = rect;
if (table) {
ObjectGridTable::ObjectGridCol* grid_col = table->get_grid_col(col);
ObjectGridTable::ObjectGridRow* grid_row = table->get_grid_row(row - 1);
ConfigOptionInt& cur_option = dynamic_cast<ConfigOptionInt&>((*grid_row)[(ObjectGridTable::GridColType)col]);
ObjectGridTable::ObjectGridCol *grid_col = table->get_grid_col(col);
ObjectGridTable::ObjectGridRow *grid_row = table->get_grid_row(row - 1);
ConfigOptionInt & cur_option = dynamic_cast<ConfigOptionInt &>((*grid_row)[(ObjectGridTable::GridColType) col]);
wxBitmap* bitmap = table->get_color_bitmap((cur_option.value >= 1)?cur_option.value-1:cur_option.value);
int bitmap_width = bitmap->GetWidth();
int bitmap_height = bitmap->GetHeight();
int offset_x = grid_cell_border_width;
int offset_y = (rect.height > bitmap_height)?(rect.height - bitmap_height)/2 : grid_cell_border_height;
wxBitmap *bitmap = table->get_color_bitmap((cur_option.value >= 1) ? cur_option.value - 1 : cur_option.value);
int bitmap_width = bitmap->GetWidth();
int bitmap_height = bitmap->GetHeight();
int offset_x = grid_cell_border_width;
int offset_y = (rect.height > bitmap_height) ? (rect.height - bitmap_height) / 2 : grid_cell_border_height;
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(wxBrush(attr.GetBackgroundColour()));
dc.DrawRectangle(rect);
dc.DrawBitmap(*bitmap, wxPoint(rect.x + offset_x, rect.y + offset_y));
text_rect.x += bitmap_width + grid_cell_border_width *2;
text_rect.width -= (bitmap_width + grid_cell_border_width *2);
text_rect.x += bitmap_width + grid_cell_border_width * 2;
text_rect.width -= (bitmap_width + grid_cell_border_width * 2);
}
//wxGridCellChoiceRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
}
wxSize GridCellFilamentsRenderer::GetBestSize(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, int WXUNUSED(row), int WXUNUSED(col))
{
wxSize size{48, -1};
return size;
}
GridCellFilamentsRenderer *GridCellFilamentsRenderer::Clone() const { return new GridCellFilamentsRenderer(); }
// ----------------------------------------------------------------------------
// GridCellFilamentsRenderer
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// GridCellChoiceEditor
// ----------------------------------------------------------------------------
GridCellChoiceEditor::GridCellChoiceEditor(const wxArrayString &choices)
: wxGridCellChoiceEditor(choices)
{}
GridCellChoiceEditor::GridCellChoiceEditor(size_t count, const wxString choices[])
: wxGridCellChoiceEditor(count, choices)
{}
wxGridCellEditor *GridCellChoiceEditor::Clone() const
{
GridCellChoiceEditor *editor = new GridCellChoiceEditor;
editor->m_allowOthers = m_allowOthers;
editor->m_choices = m_choices;
return editor;
}
void GridCellChoiceEditor::Create(wxWindow *parent, wxWindowID id, wxEvtHandler *evtHandler)
{
int style = wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB | wxBORDER_NONE;
if (!m_allowOthers) style |= wxCB_READONLY;
::ComboBox *bitmap_combo = new ComboBox(parent, id, wxEmptyString, wxDefaultPosition, wxSize(100, -1), 0, nullptr, wxCB_READONLY);
bitmap_combo->SetFont(::Label::Body_12);
int array_count = m_choices.GetCount();
for (int i = 0; i < array_count; i++) {
bitmap_combo->Append(m_choices[i]);
}
m_control = bitmap_combo;
wxGridCellEditor::Create(parent, id, evtHandler);
}
void GridCellChoiceEditor::SetSize(const wxRect &rect)
{
wxGridCellChoiceEditor::SetSize(rect);
}
void GridCellChoiceEditor::OnComboCloseUp(wxCommandEvent &evt) { wxGridCellChoiceEditor::OnComboCloseUp(evt); }
void GridCellChoiceEditor::BeginEdit(int row, int col, wxGrid *grid)
{
wxGridCellEditorEvtHandler *evtHandler = NULL;
if (m_control) {
// This event handler is needed to properly dismiss the editor when the popup is closed
m_control->Bind(wxEVT_COMBOBOX_CLOSEUP, &GridCellChoiceEditor::OnComboCloseUp, this);
evtHandler = wxDynamicCast(m_control->GetEventHandler(), wxGridCellEditorEvtHandler);
}
// Don't immediately end if we get a kill focus event within BeginEdit
if (evtHandler) evtHandler->SetInSetFocus(true);
m_value = grid->GetTable()->GetValue(row, col);
// Reset(); // this updates combo box to correspond to m_value
int pos = Combo()->FindString(m_value);
if (pos == wxNOT_FOUND) pos = 0;
Combo()->SetSelection(pos);
Combo()->SetFocus();
#ifdef __WXOSX_COCOA__
// This is a work around for the combobox being simply dismissed when a
// choice is made in it under OS X. The bug is almost certainly due to a
// problem in focus events generation logic but it's not obvious to fix and
// for now this at least allows to use wxGrid.
//Combo()->Popup();
#endif
if (evtHandler) {
// When dropping down the menu, a kill focus event
// happens after this point, so we can't reset the flag yet.
#if !defined(__WXGTK20__)
evtHandler->SetInSetFocus(false);
#endif
}
}
bool GridCellChoiceEditor::EndEdit(int WXUNUSED(row), int WXUNUSED(col), const wxGrid *WXUNUSED(grid), const wxString &WXUNUSED(oldval), wxString *newval)
{
const wxString value = Combo()->GetValue();
if (value == m_value) return false;
m_value = value;
if (newval) *newval = value;
return true;
}
wxGridActivationResult GridCellChoiceEditor::TryActivate(int row, int col, wxGrid *grid, const wxGridActivationSource &actSource)
{
ObjectGridTable * table = dynamic_cast<ObjectGridTable *>(grid->GetTable());
ObjectGridTable::ObjectGridCol *grid_col = table->get_grid_col(col);
ObjectGridTable::ObjectGridRow *grid_row = table->get_grid_row(row - 1);
if (actSource.GetOrigin() == wxGridActivationSource::Key) {
const wxKeyEvent &key_event = actSource.GetKeyEvent();
int keyCode = key_event.GetKeyCode();
wxString choice;
int digital_value = keyCode - '0';
if ((digital_value >= 1) && (digital_value <= 9))
m_cached_value = digital_value;
else
m_cached_value = -1;
if (m_cached_value != -1) {
if (m_cached_value <= grid_col->choice_count) {
choice = grid_col->choices[m_cached_value - 1];
return wxGridActivationResult::DoChange(choice);
} else {
return wxGridActivationResult::DoNothing();
}
} else
return wxGridActivationResult::DoNothing();
} else {
m_cached_value = -1;
return wxGridActivationResult::DoEdit();
}
}
void GridCellChoiceEditor::DoActivate(int row, int col, wxGrid *grid)
{
if (m_cached_value != -1) {
ObjectGridTable * table = dynamic_cast<ObjectGridTable *>(grid->GetTable());
ObjectGridTable::ObjectGridCol *grid_col = table->get_grid_col(col);
ObjectGridTable::ObjectGridRow *grid_row = table->get_grid_row(row - 1);
if (m_cached_value <= grid_col->choice_count) {
wxString choice = grid_col->choices[m_cached_value - 1];
table->SetValue(row, col, choice);
// Combo()->SetValue(choice);
}
m_cached_value = -1;
}
}
void GridCellComboBoxRenderer::Draw(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, const wxRect &rect, int row, int col, bool isSelected)
{
ObjectGridTable *table = dynamic_cast<ObjectGridTable *>(grid.GetTable());
wxRect text_rect = rect;
if (table) {
ObjectGridTable::ObjectGridCol *grid_col = table->get_grid_col(col);
ObjectGridTable::ObjectGridRow *grid_row = table->get_grid_row(row - 1);
ConfigOptionInt & cur_option = dynamic_cast<ConfigOptionInt &>((*grid_row)[(ObjectGridTable::GridColType) col]);
wxBitmap *bitmap = table->get_color_bitmap((cur_option.value >= 1) ? cur_option.value - 1 : cur_option.value);
int bitmap_width = bitmap->GetWidth();
int bitmap_height = bitmap->GetHeight();
int offset_x = grid_cell_border_width;
int offset_y = (rect.height > bitmap_height) ? (rect.height - bitmap_height) / 2 : grid_cell_border_height;
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(wxBrush(attr.GetBackgroundColour()));
dc.DrawRectangle(rect);
dc.DrawBitmap(*bitmap, wxPoint(rect.x + offset_x, rect.y + offset_y));
text_rect.x += bitmap_width + grid_cell_border_width * 2;
text_rect.width -= (bitmap_width + grid_cell_border_width * 2);
}
wxGridCellChoiceRenderer::Draw(grid, attr, dc, text_rect, row, col, isSelected);
}
wxSize GridCellFilamentsRenderer::GetBestSize(wxGrid& grid,
wxGridCellAttr& attr,
wxDC& dc,
int WXUNUSED(row),
int WXUNUSED(col))
wxSize GridCellComboBoxRenderer::GetBestSize(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, int WXUNUSED(row), int WXUNUSED(col))
{
wxSize size{128, -1};
wxSize size{48, -1};
return size;
}
GridCellFilamentsRenderer *GridCellFilamentsRenderer::Clone() const
{
return new GridCellFilamentsRenderer();
}
GridCellComboBoxRenderer *GridCellComboBoxRenderer::Clone() const { return new GridCellComboBoxRenderer(); }
// ----------------------------------------------------------------------------
// wxGridCellSupportEditor
@ -466,7 +643,58 @@ void GridCellSupportRenderer::Draw(wxGrid& grid,
wxRendererNative::Get().DrawCheckBox( &grid, dc, text_rect, flags );
}*/
wxGridCellBoolRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
//wxGridCellBoolRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
ObjectGridTable * table = dynamic_cast<ObjectGridTable *>(grid.GetTable());
ObjectGridTable::ObjectGridCol *grid_col = table->get_grid_col(col);
ObjectGridTable::ObjectGridRow *grid_row = table->get_grid_row(row - 1);
ConfigOptionBool & cur_option = dynamic_cast<ConfigOptionBool &>((*grid_row)[(ObjectGridTable::GridColType) col]);
auto height = grid.GetRowSize(row);
auto width = grid.GetColSize(col);
wxGridCellRenderer::Draw(grid, attr, dc, rect, row, col, isSelected);
if (cur_option.value) {
auto check_on = create_scaled_bitmap("check_on", nullptr, 18);
dc.SetPen(*wxTRANSPARENT_PEN);
auto offsetx = 0;
auto offsety = 0;
#ifdef __WXOSX_MAC__
offsetx = (width - 18) / 2;
offsety = (height - 18) / 2;
#else
offsetx = (width - check_on.GetSize().x) / 2;
offsety = (height - check_on.GetSize().y) / 2;
#endif // __WXOSX_MAC__
dc.DrawBitmap(check_on, rect.x + offsetx, rect.y + offsety);
//dc.SetBrush(wxBrush(wxColour(0x00, 0xAE, 0x42)));
//dc.DrawBitmap(check_on, (width - check_on.GetSize().x) / 2, (height - check_on.GetSize().y) / 2);
} else {
auto check_off = create_scaled_bitmap("check_off_focused", nullptr, 18);
dc.SetPen(*wxTRANSPARENT_PEN);
auto offsetx = 0;
auto offsety = 0;
#ifdef __WXOSX_MAC__
offsetx = (width - 18) / 2;
offsety = (height - 18) / 2;
#else
offsetx = (width - check_off.GetSize().x) / 2;
offsety = (height - check_off.GetSize().y) / 2;
#endif // __WXOSX_MAC__
dc.DrawBitmap(check_off, rect.x + offsetx, rect.y + offsety);
//dc.SetBrush(wxBrush(wxColour(0x00, 0xAE, 0x42)));
//dc.DrawBitmap(check_off, (width - check_off.GetSize().x) / 2, (height - check_off.GetSize().y) / 2);
}
}
wxSize GridCellSupportRenderer::GetBestSize(wxGrid& grid,
@ -1497,6 +1725,7 @@ wxString ObjectGridTable::convert_filament_string(int index, wxString& filament_
else
result_str = filament_str;
result_str = "";
return result_str;
}
@ -1515,12 +1744,12 @@ void ObjectGridTable::init_cols(ObjectGrid *object_grid)
// printable for object
ObjectGridCol* col = new ObjectGridCol(coBool, "printable", ObjectGridTable::category_all, true, false, true, false, wxALIGN_CENTRE);
ObjectGridCol *col = new ObjectGridCol(coBool, "printable", ObjectGridTable::category_all, true, false, true, false, wxALIGN_CENTRE);
col->size = object_grid->GetTextExtent(L("Printable")).x;
m_col_data.push_back(col);
// reset icon for printable
col = new ObjectGridCol(coBool, "printable_reset", ObjectGridTable::category_all, true, true, false, false, wxALIGN_CENTRE);
col = new ObjectGridCol(coBool, "printable_reset", ObjectGridTable::category_all, true, true, false, false, wxALIGN_LEFT);
m_col_data.push_back(col);
//first column for plate_index
@ -1532,7 +1761,7 @@ void ObjectGridTable::init_cols(ObjectGrid *object_grid)
m_col_data.push_back(col);*/
//3th column: for object/volume name
col = new ObjectGridCol(coString, "name", ObjectGridTable::category_all, false, false, true, false, wxALIGN_CENTRE);
col = new ObjectGridCol(coString, "name", ObjectGridTable::category_all, false, false, true, false, wxALIGN_LEFT);
col->size = 200;
m_col_data.push_back(col);
@ -1549,21 +1778,21 @@ void ObjectGridTable::init_cols(ObjectGrid *object_grid)
m_col_data.push_back(col);
//object layer height
col = new ObjectGridCol(coFloat, "layer_height", L("Quality"), true, false, true, true, wxALIGN_CENTRE);
col->size = object_grid->GetTextExtent(L("Layer height")).x;
col = new ObjectGridCol(coFloat, "layer_height", L("Quality"), true, false, true, true, wxALIGN_CENTRE);
col->size = object_grid->GetTextExtent(L("Layer height")).x - 28;
m_col_data.push_back(col);
//reset icon for extruder_id
col = new ObjectGridCol(coFloat, "layer_height_reset", L("Quality"), true, true, false, false, wxALIGN_CENTRE);
col = new ObjectGridCol(coFloat, "layer_height_reset", L("Quality"), true, true, false, false, wxALIGN_LEFT);
m_col_data.push_back(col);
//object/volume perimeters
col = new ObjectGridCol(coInt, "wall_loops", L("Strength"), false, false, true, true, wxALIGN_CENTRE);
col = new ObjectGridCol(coInt, "wall_loops", L("Strength"), false, false, true, true, wxALIGN_CENTRE);
col->size = object_grid->GetTextExtent(L("Wall loops")).x;
m_col_data.push_back(col);
//reset icon for perimeters
col = new ObjectGridCol(coInt, "wall_loops_reset", L("Strength"), false, true, false, false, wxALIGN_CENTRE);
col = new ObjectGridCol(coInt, "wall_loops_reset", L("Strength"), false, true, false, false, wxALIGN_LEFT);
m_col_data.push_back(col);
//object/volume fill density
@ -1572,7 +1801,7 @@ void ObjectGridTable::init_cols(ObjectGrid *object_grid)
m_col_data.push_back(col);
//reset icon for fill density
col = new ObjectGridCol(coPercent, "fill_density_reset", L("Strength"), false, true, false, false, wxALIGN_CENTRE);
col = new ObjectGridCol(coPercent, "fill_density_reset", L("Strength"), false, true, false, false, wxALIGN_LEFT);
m_col_data.push_back(col);
//support material
@ -1585,23 +1814,23 @@ void ObjectGridTable::init_cols(ObjectGrid *object_grid)
m_col_data.push_back(col);
//Bed Adhesion
col = new ObjectGridCol(coEnum, "brim_type", L("Support"), true, false, true, true, wxALIGN_CENTRE);
col = new ObjectGridCol(coEnum, "brim_type", L("Support"), true, false, true, true, wxALIGN_LEFT);
col->size = object_grid->GetTextExtent(L("Auto Brim")).x + 8; //add 8 for border
col->choices = brim_choices;
col->choice_count = WXSIZEOF(brim_choices);
m_col_data.push_back(col);
//reset icon for Bed Adhesion
col = new ObjectGridCol(coEnum, "brim_type_reset", L("Support"), true, true, false, false, wxALIGN_CENTRE);
col = new ObjectGridCol(coEnum, "brim_type_reset", L("Support"), true, true, false, false, wxALIGN_LEFT);
m_col_data.push_back(col);
//object/volume speed
col = new ObjectGridCol(coFloat, "inner_wall_speed", L("Speed"), false, false, true, true, wxALIGN_CENTRE);
col = new ObjectGridCol(coFloat, "inner_wall_speed", L("Speed"), false, false, true, true, wxALIGN_LEFT);
col->size = object_grid->GetTextExtent(L("Inner wall speed")).x;
m_col_data.push_back(col);
//reset icon for speed
col = new ObjectGridCol(coFloat, "inner_wall_speed_reset", L("Speed"), false, true, false, false, wxALIGN_CENTRE);
col = new ObjectGridCol(coFloat, "inner_wall_speed_reset", L("Speed"), false, true, false, false, wxALIGN_LEFT);
m_col_data.push_back(col);
return;
@ -1922,17 +2151,20 @@ void ObjectGridTable::update_row_properties()
switch (grid_col->type)
{
case coString:
grid_table->SetCellEditor(row, col, new wxGridCellAutoWrapStringEditor());
grid_table->SetCellRenderer(row, col, new wxGridCellAutoWrapStringRenderer());
if (col == ObjectGridTable::col_plate_index)
grid_table->SetReadOnly(row, col);
grid_table->SetCellEditor(row, col, new GridCellTextEditor());
//grid_table->SetCellRenderer(row, col, new wxGridCellAutoWrapStringRenderer());
grid_table->SetCellFitMode(row, col, wxGridFitMode::Ellipsize());
break;
case coBool:
grid_table->SetCellEditor(row, col, new GridCellSupportEditor());
//grid_table->SetCellEditor(row, col, new wxGridCellBoolEditor());
//grid_table->SetCellRenderer(row, col, new GridCellSupportRenderer());
grid_table->SetCellRenderer(row, col, new wxGridCellBoolRenderer());
grid_table->SetCellRenderer(row, col, new GridCellSupportRenderer());
break;
case coInt:
grid_table->SetCellEditor(row, col, new wxGridCellNumberEditor());
grid_table->SetCellEditor(row, col, new GridCellTextEditor());
grid_table->SetCellRenderer(row, col, new wxGridCellNumberRenderer());
break;
case coEnum:
@ -1940,26 +2172,21 @@ void ObjectGridTable::update_row_properties()
GridCellFilamentsEditor *filament_editor = new GridCellFilamentsEditor(grid_col->choice_count, grid_col->choices, false, &m_panel->m_color_bitmaps);
grid_table->SetCellEditor(row, col, filament_editor);
grid_table->SetCellRenderer(row, col, new GridCellFilamentsRenderer());
}
else
grid_table->SetCellEditor(row, col, new wxGridCellChoiceEditor(grid_col->choice_count, grid_col->choices));
} else {
GridCellChoiceEditor *combo_editor = new GridCellChoiceEditor(grid_col->choice_count, grid_col->choices);
grid_table->SetCellEditor(row, col, combo_editor);
grid_table->SetCellRenderer(row, col, new wxGridCellChoiceRenderer());
//new wxGridCellChoiceEditor(grid_col->choice_count, grid_col->choices));
}
break;
case coFloat:
grid_table->SetCellEditor(row, col, new wxGridCellFloatEditor(6,2));
grid_table->SetCellRenderer(row, col, new wxGridCellFloatRenderer(6,2));
grid_table->SetCellEditor(row, col, new GridCellTextEditor());
grid_table->SetCellRenderer(row, col, new wxGridCellFloatRenderer(6,1));
break;
case coPercent:
{
/*wxGridCellFloatEditor *float_editor = new wxGridCellFloatEditor(6,2);
wxFloatingPointValidator<float> *float_validator = new wxFloatingPointValidator<float>(3, nullptr, wxNUM_VAL_ZERO_AS_BLANK);
float_validator->SetRange(0.f, 100.f);
float_editor->SetValidator(*float_validator);
if (rows < 3)
m_object_grid->SetCellEditor(row, col, float_editor);
else*/
grid_table->SetCellEditor(row, col, new wxGridCellFloatEditor(6,2));
grid_table->SetCellRenderer(row, col, new wxGridCellFloatRenderer(6,2));
grid_table->SetCellEditor(row, col, new GridCellTextEditor());
grid_table->SetCellRenderer(row, col, new wxGridCellFloatRenderer(6,1));
break;
}
default:
@ -2049,7 +2276,7 @@ void ObjectGridTable::sort_row_data(compare_row_func sort_func)
std::copy(new_grid_rows.begin(), new_grid_rows.end(), m_grid_data.begin());
new_grid_rows.clear();
update_row_properties();
//update_row_properties();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" finished, this %1%, row_data size %2%") %this % m_grid_data.size();
}
@ -2165,7 +2392,7 @@ bool ObjectGridTable::OnCellLeftClick(int row, int col, ConfigOptionType &type)
if (row == 0) {
sort_by_col(col);
} else if (col >= col_name) {
} else if (col >= col_printable_reset) {
ObjectGridRow *grid_row = m_grid_data[row - 1];
ObjectGridCol* grid_col = m_col_data[col];
ObjectGridCol* grid_col_2 = m_col_data[col - 1];
@ -2401,11 +2628,10 @@ ObjectTablePanel::ObjectTablePanel( wxWindow* parent, wxWindowID id, const wxPoi
//m_object_grid_table->SetAttrProvider(new MyGridCellAttrProvider);
//m_object_grid->AssignTable(m_object_grid_table);
m_side_window = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(460),FromDIP(480)), wxVSCROLL);
m_side_window = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(440),FromDIP(480)), wxVSCROLL);
m_side_window->SetScrollRate( 0, 5 );
m_page_sizer = new wxBoxSizer(wxVERTICAL);
m_page_top_sizer = new wxBoxSizer(wxHORIZONTAL);
//m_page_top_sizer = new wxBoxSizer(wxHORIZONTAL);
m_side_window->SetBackgroundColour(wxColour(0xff, 0xff, 0xff));
m_side_window->SetSizer(m_page_sizer);
m_side_window->SetScrollbars(1, 20, 1, 2);
@ -2444,7 +2670,7 @@ ObjectTablePanel::ObjectTablePanel( wxWindow* parent, wxWindowID id, const wxPoi
m_side_window->SetFont(::Label::Body_12);
m_object_settings = new ObjectTableSettings(m_side_window, m_object_grid_table);
m_object_settings->Hide();
m_page_sizer->Add(m_page_top_sizer, 0, wxEXPAND | wxALIGN_CENTER_HORIZONTAL, 0);
//m_page_sizer->Add(m_page_top_sizer, 0, wxALIGN_CENTER_HORIZONTAL, 0);
m_page_sizer->Add(m_object_settings->get_sizer(), 1, wxEXPAND | wxALL, 2 );
auto m_line_left = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(1,-1), wxTAB_TRAVERSAL);
@ -2464,7 +2690,7 @@ ObjectTablePanel::ObjectTablePanel( wxWindow* parent, wxWindowID id, const wxPoi
int ObjectTablePanel::init_bitmap()
{
m_undo_bitmap = create_scaled_bitmap("lock_normal", nullptr, 24);
m_undo_bitmap = create_scaled_bitmap("lock_normal", nullptr, 18);
m_color_bitmaps = get_extruder_color_icons();
return 0;
@ -2535,33 +2761,39 @@ void ObjectTablePanel::load_data()
//construct tables
//m_object_grid->CreateGrid(rows, cols, wxGridSelectCells);
#if HAS_COL_HEADER
m_object_grid->SetColLabelValue(ObjectGridTable::col_printable, L("Printable"));
m_object_grid->SetColLabelAlignment(wxALIGN_LEFT, wxALIGN_CENTER);
m_object_grid->SetColLabelValue(ObjectGridTable::col_printable, _L("Printable"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_printable_reset, "");
m_object_grid->SetColLabelValue(ObjectGridTable::col_plate_index, L("Plate"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_plate_index, _L("Plate"));
/*m_object_grid->SetColLabelValue(ObjectGridTable::col_assemble_name, L("Module"));*/
m_object_grid->SetColLabelValue(ObjectGridTable::col_name, L("Name"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_filaments, L("Filament"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_name, _L("Name"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_filaments, _L("Filament"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_filaments_reset, "");
m_object_grid->SetColLabelValue(ObjectGridTable::col_layer_height, L("Layer height"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_layer_height, _L("Layer height"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_layer_height_reset, "");
m_object_grid->SetColLabelValue(ObjectGridTable::col_wall_loops, L("Wall loops"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_wall_loops, _L("Wall loops"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_wall_loops_reset, "");
m_object_grid->SetColLabelValue(ObjectGridTable::col_fill_density, L("Infill density(%)"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_fill_density, _L("Infill density(%)"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_fill_density_reset, "");
m_object_grid->SetColLabelValue(ObjectGridTable::col_enable_support, L("Support"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_enable_support, _L("Support"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_enable_support_reset, "");
m_object_grid->SetColLabelValue(ObjectGridTable::col_brim_type, L("Brim"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_brim_type, _L("Brim"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_brim_type_reset, "");
m_object_grid->SetColLabelValue(ObjectGridTable::col_speed_perimeter, L("Inner wall speed"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_speed_perimeter, _L("Inner wall speed"));
m_object_grid->SetColLabelValue(ObjectGridTable::col_speed_perimeter_reset, "");
m_object_grid->SetLabelFont(Label::Head_13);
m_object_grid->SetLabelTextColour(wxColour(0x30,0x3a,0x3c));
m_object_grid->SetLabelBackgroundColour(wxColour(0xff,0xff,0xff));
m_object_grid->SetLabelBackgroundColour(wxColour(0xff, 0xff, 0xff));
#else
m_object_grid->HideColLabels();
#endif
m_object_grid->HideRowLabels();
m_object_grid->EnableGridLines (true);
m_object_grid->EnableGridLines (false);
m_object_grid->EnableDragColSize(false);
m_object_grid->EnableDragGridSize(false);
m_object_grid->EnableDragRowSize(false);
/*set the first row as label*/
//format
@ -2570,7 +2802,7 @@ void ObjectTablePanel::load_data()
//attr->SetBackgroundColour(wxColour(191, 191, 255));
attr->SetBackgroundColour(*wxWHITE);
attr->SetTextColour(*wxBLACK);
attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
attr->SetReadOnly(true);
m_object_grid->SetRowAttr (0, attr);
#if HAS_COL_HEADER
@ -2580,7 +2812,7 @@ void ObjectTablePanel::load_data()
m_object_grid->SetCellSize(0, ObjectGridTable::col_printable, 1, 2);
//m_object_grid->SetCellSize(0, ObjectGridTable::col_assemble_name, 1, 1);
m_object_grid->SetCellSize(0, ObjectGridTable::col_name, 1, 1);
m_object_grid->SetCellSize(0, ObjectGridTable::col_filaments, 1, 2);
m_object_grid->SetCellSize(0, ObjectGridTable::col_filaments, 1, 1);
m_object_grid->SetCellSize(0, ObjectGridTable::col_layer_height, 1, 2);
m_object_grid->SetCellSize(0, ObjectGridTable::col_wall_loops, 1, 2);
m_object_grid->SetCellSize(0, ObjectGridTable::col_fill_density, 1, 2);
@ -2588,14 +2820,8 @@ void ObjectTablePanel::load_data()
m_object_grid->SetCellSize(0, ObjectGridTable::col_brim_type, 1, 2);
m_object_grid->SetCellSize(0, ObjectGridTable::col_speed_perimeter, 1, 2);
//m_object_grid->SetSelectionForeground(wxColour(255, 0, 0));
//m_object_grid->SetSelectionBackground(wxColour(0, 255, 0));
//wxGridCellAutoWrapStringEditor* string_editor = new wxGridCellAutoWrapStringEditor();
//wxGridCellBoolEditor* bool_editor = new wxGridCellBoolEditor();
//wxGridCellFloatEditor* float_editor = new wxGridCellFloatEditor();
//wxGridCellNumberEditor* number_editor = new wxGridCellNumberEditor();
//wxGridCellChoiceEditor* choice_editor = new wxGridCellChoiceEditor();
//wxGridCellEnumEditor* enum_editor = new wxGridCellEnumEditor();
//m_object_grid->SetSelectionForeground(wxColour(0xDB,0xFD,0xE7));
//m_object_grid->SetSelectionBackground(*wxWHITE);
for (int col = 0; col < cols; col++)
{
@ -2627,45 +2853,39 @@ void ObjectTablePanel::load_data()
switch (grid_col->type)
{
case coString:
m_object_grid->SetCellEditor(row, col, new wxGridCellAutoWrapStringEditor());
m_object_grid->SetCellRenderer(row, col, new wxGridCellAutoWrapStringRenderer());
m_object_grid->SetCellEditor(row, col, new GridCellTextEditor());
//m_object_grid->SetCellRenderer(row, col, new wxGridCellAutoWrapStringRenderer());
m_object_grid->SetCellFitMode(row, col, wxGridFitMode::Ellipsize());
break;
case coBool:
m_object_grid->SetCellEditor(row, col, new GridCellSupportEditor());
//m_object_grid->SetCellEditor(row, col, new wxGridCellBoolEditor());
//m_object_grid->SetCellRenderer(row, col, new GridCellSupportRenderer());
m_object_grid->SetCellRenderer(row, col, new wxGridCellBoolRenderer());
m_object_grid->SetCellRenderer(row, col, new GridCellSupportRenderer());
break;
case coInt:
m_object_grid->SetCellEditor(row, col, new wxGridCellNumberEditor());
m_object_grid->SetCellEditor(row, col, new GridCellTextEditor());
m_object_grid->SetCellRenderer(row, col, new wxGridCellNumberRenderer());
break;
case coEnum:
if (col == ObjectGridTable::col_filaments) {
GridCellFilamentsEditor *filament_editor = new GridCellFilamentsEditor(grid_col->choice_count, grid_col->choices, false, &m_color_bitmaps);
m_object_grid->SetCellEditor(row, col, filament_editor);
//m_object_grid->SetCellEditor(row, col, new wxGridCellChoiceEditor(grid_col->choice_count, grid_col->choices));
m_object_grid->SetCellRenderer(row, col, new GridCellFilamentsRenderer());
} else {
GridCellChoiceEditor *combo_editor = new GridCellChoiceEditor(grid_col->choice_count, grid_col->choices);
m_object_grid->SetCellEditor(row, col, combo_editor);
m_object_grid->SetCellRenderer(row, col, new wxGridCellChoiceRenderer());
}
else
m_object_grid->SetCellEditor(row, col, new wxGridCellChoiceEditor(grid_col->choice_count, grid_col->choices));
break;
case coFloat:
m_object_grid->SetCellEditor(row, col, new wxGridCellFloatEditor(6,2));
m_object_grid->SetCellRenderer(row, col, new wxGridCellFloatRenderer(6,2));
m_object_grid->SetCellEditor(row, col, new GridCellTextEditor());
m_object_grid->SetCellRenderer(row, col, new wxGridCellFloatRenderer(6,1));
break;
case coPercent:
{
/*wxGridCellFloatEditor *float_editor = new wxGridCellFloatEditor(6,2);
wxFloatingPointValidator<float> *float_validator = new wxFloatingPointValidator<float>(3, nullptr, wxNUM_VAL_ZERO_AS_BLANK);
float_validator->SetRange(0.f, 100.f);
float_editor->SetValidator(*float_validator);
if (rows < 3)
m_object_grid->SetCellEditor(row, col, float_editor);
else*/
m_object_grid->SetCellEditor(row, col, new wxGridCellFloatEditor(6,2));
m_object_grid->SetCellRenderer(row, col, new wxGridCellFloatRenderer(6,2));
m_object_grid->SetCellEditor(row, col, new GridCellTextEditor());
m_object_grid->SetCellRenderer(row, col, new wxGridCellFloatRenderer(6,1));
break;
}
default:
@ -2681,34 +2901,98 @@ void ObjectTablePanel::load_data()
}
}
}
m_object_grid->Fit();
for (int i = 0; i < ObjectGridTable::col_max; i++)
{
ObjectGridTable::ObjectGridCol *grid_col = m_object_grid_table->get_grid_col(i);
if (grid_col->size > 0) {
int fit_size1 = m_object_grid->GetColSize(i);
m_object_grid->SetColSize(i, grid_col->size);
//if (grid_col->size < fit_size1) m_object_grid->SetColSize(i, grid_col->size);
}
}
m_object_grid->Fit();
for (int i = 0; i < ObjectGridTable::col_max; i++) {
switch (i) {
case ObjectGridTable::col_printable: {
m_object_grid->SetColSize(i, m_object_grid->GetColSize(i) - FromDIP(28));
break;
}
//else {
//adjust the left col
//int delta = grid_col->size - fit_size1;
//grid_col = m_object_grid_table->get_grid_col(i - 1);
//int fit_size2 = m_object_grid->GetColSize(i-1);
//grid_col->size = fit_size2 - delta;
//m_object_grid->SetColSize(i, grid_col->size);
//}
case ObjectGridTable::col_printable_reset:
m_object_grid->SetColSize(i, FromDIP(28));
break;
case ObjectGridTable::col_name:
m_object_grid->SetColSize(i, FromDIP(100));
break;
case ObjectGridTable::col_filaments:
m_object_grid->SetColSize(i, FromDIP(52));
break;
case ObjectGridTable::col_filaments_reset:
m_object_grid->SetColSize(i, FromDIP(28));
break;
case ObjectGridTable::col_layer_height: {
auto width = m_object_grid->GetColSize(i) - FromDIP(28);
if (width < m_object_grid->GetTextExtent(("000.00")).x) {
width = m_object_grid->GetTextExtent(("000.00")).x;
}
m_object_grid->SetColSize(i, width);
break;
}
case ObjectGridTable::col_layer_height_reset:
m_object_grid->SetColSize(i, FromDIP(28));
break;
case ObjectGridTable::col_wall_loops:
m_object_grid->SetColSize(i, m_object_grid->GetColSize(i) - FromDIP(28));
break;
case ObjectGridTable::col_wall_loops_reset:
m_object_grid->SetColSize(i, FromDIP(28));
break;
case ObjectGridTable::col_fill_density:
m_object_grid->SetColSize(i, m_object_grid->GetColSize(i) - FromDIP(28));
break;
case ObjectGridTable::col_fill_density_reset:
m_object_grid->SetColSize(i, FromDIP(28));
break;
case ObjectGridTable::col_enable_support:
m_object_grid->SetColSize(i, m_object_grid->GetColSize(i) - FromDIP(28));
break;
case ObjectGridTable::col_enable_support_reset:
m_object_grid->SetColSize(i, FromDIP(28));
break;
case ObjectGridTable::col_brim_type:
m_object_grid->SetColSize(i, FromDIP(56));
break;
case ObjectGridTable::col_brim_type_reset:
m_object_grid->SetColSize(i, FromDIP(28));
break;
case ObjectGridTable::col_speed_perimeter: {
auto width = m_object_grid->GetColSize(i) - FromDIP(28);
if (width < m_object_grid->GetTextExtent(("000.00")).x) { width = m_object_grid->GetTextExtent(("000.00")).x; }
m_object_grid->SetColSize(i, width);
break;
}
case ObjectGridTable::col_speed_perimeter_reset:
m_object_grid->SetColSize(i, FromDIP(28));
break;
}
}
//set col size after fit
/* ObjectGridTable::ObjectGridCol* grid_col = m_object_grid_table->get_grid_col(ObjectGridTable::col_brim_type);
grid_col->size = m_object_grid->GetTextExtent(grid_col->choices[0]).x + 30;
m_object_grid->SetColSize(ObjectGridTable::col_brim_type, grid_col->size);
grid_col = m_object_grid_table->get_grid_col(ObjectGridTable::col_filaments);
grid_col->size = 128;
m_object_grid->SetColSize(ObjectGridTable::col_filaments, grid_col->size);*/
m_object_grid->SetGridLineColour(*wxWHITE);
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", finished, got %1% rows, %2% cols") %m_object_grid_table->GetNumberRows() %m_object_grid_table->GetNumberCols() ;
}
@ -2794,7 +3078,6 @@ void ObjectTablePanel::OnSelectCell( wxGridEvent& ev )
m_cur_row = row;
m_cur_col = col;
ev.Skip();
}
@ -2866,7 +3149,7 @@ void ObjectTablePanel::resetAllValuesInSideWindow(int row, bool is_object, Model
// ObjectTableDialog
// ----------------------------------------------------------------------------
ObjectTableDialog::ObjectTableDialog(wxWindow* parent, Plater* platerObj, Model *modelObj, wxSize maxSize)
: GUI::DPIDialog(parent, wxID_ANY, _L("Object/Part Setting"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX | wxRESIZE_BORDER)
: GUI::DPIDialog(parent, wxID_ANY, _L("Object/Part Setting"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
,
m_model(modelObj), m_plater(platerObj)
{
@ -2906,6 +3189,7 @@ ObjectTableDialog::ObjectTableDialog(wxWindow* parent, Plater* platerObj, Model
m_main_sizer->Add(m_line_top, 0, wxEXPAND, 0);
m_obj_panel = new ObjectTablePanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE, wxEmptyString, m_plater, m_model);
m_obj_panel->SetBackgroundColour(wxColour(0x00,0xAE,0x42));
//m_top_sizer->Add(m_obj_panel, 1, wxALL | wxEXPAND, 5);
wxSize panel_size = m_obj_panel->get_init_size();
@ -2937,19 +3221,20 @@ ObjectTableDialog::ObjectTableDialog(wxWindow* parent, Plater* platerObj, Model
//this->Layout();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", created, this %1%, m_obj_panel %2%") %this % m_obj_panel;
m_main_sizer->Add(m_obj_panel, 1, wxEXPAND|wxTOP,2);
m_main_sizer->Add(m_obj_panel, 1, wxEXPAND|wxLEFT,FromDIP(10));
SetSizer(m_main_sizer);
Layout();
Fit();
Layout();
}
ObjectTableDialog::~ObjectTableDialog()
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", this %1%, m_obj_panel %2%") %this % m_obj_panel;
#ifdef __WXOSX_MAC__
if (m_obj_panel) {
delete m_obj_panel;
m_obj_panel = nullptr;
}
#endif
}
void ObjectTableDialog::Popup(int obj_idx, int vol_idx, wxPoint position /*= wxDefaultPosition*/)
@ -2957,7 +3242,8 @@ void ObjectTableDialog::Popup(int obj_idx, int vol_idx, wxPoint position /*= wxD
m_obj_panel->sort_by_default();
m_obj_panel->SetSelection(obj_idx, vol_idx);
this->SetPosition(position);
//this->SetPosition(position);
Centre(wxBOTH);
this->ShowModal();
}
@ -2981,23 +3267,88 @@ void ObjectTableDialog::on_sys_color_changed()
void ObjectTableDialog::OnClose(wxCloseEvent &evt)
{
this->GetSize(&g_dialog_width, &g_dialog_height);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", g_dialog_width %1%, g_dialog_height %2%") %g_dialog_width % g_dialog_height;
#ifdef __WINDOWS__
if (m_obj_panel) {
delete m_obj_panel;
m_obj_panel = nullptr;
}
DestroyChildren();
Destroy();
#endif
#ifdef __WXOSX_MAC__
evt.Skip();
#endif
}
void ObjectTableDialog::OnSize(wxSizeEvent& event)
{
SetSize(wxSize(-1, FromDIP(480)));
SetMinSize(wxSize(-1, FromDIP(480)));
SetMaxSize(wxSize(-1, FromDIP(480)));
return;
wxSize new_size = event.GetSize();
if ((new_size.GetWidth() > g_dialog_max_width) || (new_size.GetHeight() > g_dialog_max_height)) {
int width = (new_size.GetWidth() > g_dialog_max_width)?new_size.GetWidth():g_dialog_max_width;
int height = (new_size.GetHeight() > g_dialog_max_height)?new_size.GetHeight():g_dialog_max_height;
this->SetMaxSize(wxSize(width, height));
}
event.Skip();
//SetSize(wxSize(-1, FromDIP(480)));
//SetMinSize(wxSize(-1, FromDIP(480)));
//SetMaxSize(wxSize(-1, FromDIP(480)));
//return;
//wxSize new_size = event.GetSize();
//if ((new_size.GetWidth() > g_dialog_max_width) || (new_size.GetHeight() > g_dialog_max_height)) {
// int width = (new_size.GetWidth() > g_dialog_max_width)?new_size.GetWidth():g_dialog_max_width;
// int height = (new_size.GetHeight() > g_dialog_max_height)?new_size.GetHeight():g_dialog_max_height;
// this->SetMaxSize(wxSize(width, height));
//}
//event.Skip();
}
// ----------------------------------------------------------------------------
// GridCellTextEditor
// ----------------------------------------------------------------------------
GridCellTextEditor::GridCellTextEditor() : wxGridCellTextEditor() {}
GridCellTextEditor::~GridCellTextEditor() {}
void GridCellTextEditor::Create(wxWindow *parent, wxWindowID id, wxEvtHandler *evtHandler)
{
::TextInput *text_input = new ::TextInput(parent, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, wxSize(-1, -1), wxTE_PROCESS_ENTER);
m_control = text_input;
wxGridCellEditor::Create(parent, id, evtHandler);
}
void GridCellTextEditor::StartingKey(wxKeyEvent &event) {}
void GridCellTextEditor::SetSize(const wxRect &rect) { wxGridCellTextEditor::SetSize(rect); }
void GridCellTextEditor::BeginEdit(int row, int col, wxGrid *grid)
{
ObjectGridTable * table = dynamic_cast<ObjectGridTable *>(grid->GetTable());
ObjectGridTable::ObjectGridCol *grid_col = table->get_grid_col(col);
ObjectGridTable::ObjectGridRow *grid_row = table->get_grid_row(row - 1);
auto val = table->GetValue(row, col);
Text()->GetTextCtrl()->SetValue(val);
Text()->SetFocus();
}
bool GridCellTextEditor::EndEdit(int row, int col, const wxGrid *grid, const wxString &WXUNUSED(oldval), wxString *newval)
{
ObjectGridTable * table = dynamic_cast<ObjectGridTable *>(grid->GetTable());
ObjectGridTable::ObjectGridCol *grid_col = table->get_grid_col(col);
ObjectGridTable::ObjectGridRow *grid_row = table->get_grid_row(row - 1);
wxCHECK_MSG(m_control, false, "wxGridCellTextEditor must be created first!");
const wxString value = Text()->GetTextCtrl()->GetValue();
if (value == m_value) return false;
m_value = value;
if (newval) *newval = m_value;
return true;
}
void GridCellTextEditor::ApplyEdit(int row, int col, wxGrid *grid)
{
grid->GetTable()->SetValue(row, col, m_value);
m_value.clear();
}
} // namespace GUI

View file

@ -23,6 +23,10 @@
#include "OptionsGroup.hpp"
#include "GUI_Factories.hpp"
#include "GUI_ObjectTableSettings.hpp"
#include "Widgets/TextInput.hpp"
class ComboBox;
class TextInput;
namespace Slic3r {
@ -50,11 +54,31 @@ public:
virtual GridCellIconRenderer *Clone() const wxOVERRIDE;
};
// the editor for string data allowing to choose from the list of strings
class GridCellTextEditor : public wxGridCellTextEditor
{
public:
GridCellTextEditor();
~GridCellTextEditor();
virtual void Create(wxWindow *parent, wxWindowID id, wxEvtHandler *evtHandler) wxOVERRIDE;
void StartingKey(wxKeyEvent &event) wxOVERRIDE;
virtual void SetSize(const wxRect &rect) wxOVERRIDE;
virtual void BeginEdit(int row, int col, wxGrid *grid) wxOVERRIDE;
virtual bool EndEdit(int row, int col, const wxGrid *grid, const wxString &oldval, wxString *newval) wxOVERRIDE;
virtual void ApplyEdit(int row, int col, wxGrid *grid) wxOVERRIDE;
protected:
::TextInput *Text() const { return (::TextInput *) m_control; }
wxDECLARE_NO_COPY_CLASS(GridCellTextEditor);
private:
wxString m_value;
};
class GridCellFilamentsEditor : public wxGridCellChoiceEditor
{
public:
// if !allowOthers, user can't type a string not in choices array
GridCellFilamentsEditor(size_t count = 0,
const wxString choices[] = NULL,
bool allowOthers = false,
@ -78,7 +102,7 @@ public:
virtual void DoActivate(int row, int col, wxGrid* grid) wxOVERRIDE;
protected:
wxBitmapComboBox *Combo() const { return (wxBitmapComboBox *)m_control; }
::ComboBox *Combo() const { return (::ComboBox *)m_control; }
void OnComboCloseUp(wxCommandEvent& evt);
std::vector<wxBitmap*>* m_icons;
@ -108,6 +132,44 @@ public:
virtual GridCellFilamentsRenderer *Clone() const wxOVERRIDE;
};
class GridCellChoiceEditor : public wxGridCellChoiceEditor
{
public:
GridCellChoiceEditor(size_t count = 0, const wxString choices[] = NULL);
GridCellChoiceEditor(const wxArrayString &choices);
virtual void Create(wxWindow *parent, wxWindowID id, wxEvtHandler *evtHandler) wxOVERRIDE;
virtual void SetSize(const wxRect &rect) wxOVERRIDE;
virtual wxGridCellEditor *Clone() const wxOVERRIDE;
virtual void BeginEdit(int row, int col, wxGrid *grid) wxOVERRIDE;
virtual bool EndEdit(int row, int col, const wxGrid *grid, const wxString &oldval, wxString *newval) wxOVERRIDE;
virtual wxGridActivationResult TryActivate(int row, int col, wxGrid *grid, const wxGridActivationSource &actSource) wxOVERRIDE;
virtual void DoActivate(int row, int col, wxGrid *grid) wxOVERRIDE;
protected:
::ComboBox *Combo() const { return (::ComboBox *) m_control; }
void OnComboCloseUp(wxCommandEvent &evt);
wxDECLARE_NO_COPY_CLASS(GridCellChoiceEditor);
private:
int m_cached_value{-1};
};
class GridCellComboBoxRenderer : public wxGridCellChoiceRenderer
{
public:
virtual void Draw(wxGrid &grid, wxGridCellAttr &attr, wxDC &dc, const wxRect &rect, int row, int col, bool isSelected) wxOVERRIDE;
virtual wxSize GetBestSize(wxGrid &WXUNUSED(grid), wxGridCellAttr &attr, wxDC &dc, int WXUNUSED(row), int WXUNUSED(col)) wxOVERRIDE;
virtual GridCellComboBoxRenderer *Clone() const wxOVERRIDE;
};
class GridCellSupportEditor : public wxGridCellBoolEditor
{
public:
@ -115,7 +177,6 @@ public:
virtual void DoActivate(int row, int col, wxGrid* grid) wxOVERRIDE;
private:
// These functions modify or use m_value.
void SetValueFromGrid(int row, int col, wxGrid* grid);
void SetGridFromValue(int row, int col, wxGrid* grid) const;
@ -150,6 +211,7 @@ public:
virtual GridCellSupportRenderer *Clone() const wxOVERRIDE;
};
//ObjectGrid for the param setting table
class ObjectGrid : public wxGrid
{

View file

@ -22,6 +22,10 @@ namespace Slic3r
namespace GUI
{
wxDEFINE_EVENT(EVT_LOCK_DISABLE, wxCommandEvent);
wxDEFINE_EVENT(EVT_LOCK_ENABLE, wxCommandEvent);
OTG_Settings::OTG_Settings(wxWindow* parent, const bool staticbox) :
m_parent(parent)
{
@ -162,10 +166,23 @@ bool ObjectTableSettings::update_settings_list(bool is_object, bool is_multiple_
auto btn = new ScalableButton(parent, wxID_ANY, m_bmp_reset);
btn->SetToolTip(_(L("Reset parameter")));
#ifdef __WINDOWS__
btn->SetBackgroundColour(*wxWHITE);
#endif // DEBUG
btn->SetBitmapFocus(m_bmp_reset_focus.bmp());
btn->SetBitmapHover(m_bmp_reset_focus.bmp());
#ifdef __WINDOWS__
btn->SetBitmapDisabled(m_bmp_reset_disable.bmp());
#endif
#ifdef __WXOSX_MAC__
btn->Bind(EVT_LOCK_DISABLE, [this, btn](auto &e) { btn->SetBitmap(m_bmp_reset_disable.bmp()); });
btn->Bind(EVT_LOCK_ENABLE, [this, btn](auto &e) { btn->SetBitmap(m_bmp_reset_focus.bmp()); });
#endif
btn->Bind(wxEVT_BUTTON, [btn, opt_key, this, is_object, object, config, group_category](wxEvent &event) {
//wxGetApp().plater()->take_snapshot(from_u8((boost::format(_utf8(L("Reset Option %s"))) % opt_key).str()));
@ -185,6 +202,14 @@ bool ObjectTableSettings::update_settings_list(bool is_object, bool is_multiple_
m_current_config.apply(config->get(), true);
update_config_values(is_object, object, config, group_category);
this->m_parent->Thaw();
#ifdef __WXOSX_MAC__
if (!btn->IsEnabled()) {
btn->SetBitmap(m_bmp_reset_disable.bmp());
} else {
btn->SetBitmap(m_bmp_reset_focus.bmp());
}
#endif
});
(const_cast<Line&>(line)).extra_widget_win = btn;
return btn;
@ -211,7 +236,9 @@ bool ObjectTableSettings::update_settings_list(bool is_object, bool is_multiple_
ctrl->SetBitmap_(m_bmp_reset);
ctrl->SetBitmapFocus(m_bmp_reset_focus.bmp());
ctrl->SetBitmapHover(m_bmp_reset_focus.bmp());
#ifdef __WINDOWS__
ctrl->SetBitmapDisabled(m_bmp_reset_disable.bmp());
#endif
};
const bool is_extruders_cat = cat.first == "Extruders";
@ -308,10 +335,22 @@ int ObjectTableSettings::update_extra_column_visible_status(ConfigOptionsGroup*
if (line) {
if ((config->has(opt.name)) && reset_window&&reset_window->IsEnabled()) {
line->extra_widget_win->Enable();
#ifdef __WXOSX_MAC__
wxCommandEvent event(EVT_LOCK_ENABLE);
event.SetEventObject(line->extra_widget_win);
wxPostEvent(line->extra_widget_win, event);
#endif
count++;
}
else
} else {
line->extra_widget_win->Disable();
#ifdef __WXOSX_MAC__
wxCommandEvent event(EVT_LOCK_DISABLE);
event.SetEventObject(line->extra_widget_win);
wxPostEvent(line->extra_widget_win, event);
#endif
}
}
}
wxGridSizer* grid_sizer = option_group->get_grid_sizer();

View file

@ -76,7 +76,8 @@ public:
void resetAllValues(int row, bool is_object, ModelObject* object, ModelConfig* config, const std::string& category);
void msw_rescale();
};
wxDECLARE_EVENT(EVT_LOCK_DISABLE, wxCommandEvent);
wxDECLARE_EVENT(EVT_LOCK_ENABLE, wxCommandEvent);
}}
#endif // slic3r_GUI_ObjectTableSettings_hpp_

View file

@ -78,6 +78,7 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig
//BBS: GUI refactor: GLToolbar
m_canvas->enable_select_plate_toolbar(false);
m_canvas->enable_assemble_view_toolbar(true);
m_canvas->enable_separator_toolbar(true);
m_canvas->enable_labels(true);
m_canvas->enable_slope(true);
@ -446,40 +447,45 @@ void Preview::update_layers_slider_mode()
// multi-extruder printer profile , but whole model is printed by only one extruder
// false -> multi-extruder printer profile , and model is printed by several extruders
bool one_extruder_printed_model = true;
bool can_change_color = true;
// extruder used for whole model for multi-extruder printer profile
int only_extruder = -1;
// BBS
if (wxGetApp().filaments_cnt() > 1) {
const ModelObjectPtrs &objects = wxGetApp().plater()->model().objects;
//const ModelObjectPtrs& objects = wxGetApp().plater()->model().objects;
auto plate_extruders = wxGetApp().plater()->get_partplate_list().get_curr_plate()->get_extruders();
for (auto extruder : plate_extruders) {
if (extruder != plate_extruders[0])
can_change_color = false;
}
// check if whole model uses just only one extruder
if (!objects.empty()) {
const int extruder = objects[0]->config.has("extruder") ? objects[0]->config.option("extruder")->getInt() : 0;
if (!plate_extruders.empty()) {
//const int extruder = objects[0]->config.has("extruder") ? objects[0]->config.option("extruder")->getInt() : 0;
const int extruder = plate_extruders[0];
only_extruder = extruder;
// auto is_one_extruder_printed_model = [objects, extruder]() {
// for (ModelObject *object : objects) {
// if (object->config.has("extruder") && object->config.option("extruder")->getInt() != extruder) /*return false*/;
auto is_one_extruder_printed_model = [objects, extruder]() {
for (ModelObject *object : objects) {
if (object->config.has("extruder") && object->config.option("extruder")->getInt() != extruder) return false;
// for (ModelVolume *volume : object->volumes)
// if ((volume->config.has("extruder") && volume->config.option("extruder")->getInt() != extruder) || !volume->mmu_segmentation_facets.empty()) return false;
for (ModelVolume *volume : object->volumes)
if ((volume->config.has("extruder") && volume->config.option("extruder")->getInt() != extruder) || !volume->mmu_segmentation_facets.empty()) return false;
// for (const auto &range : object->layer_config_ranges)
// if (range.second.has("extruder") && range.second.option("extruder")->getInt() != extruder) return false;
// }
// return true;
// };
for (const auto &range : object->layer_config_ranges)
if (range.second.has("extruder") && range.second.option("extruder")->getInt() != extruder) return false;
}
return true;
};
if (is_one_extruder_printed_model())
only_extruder = extruder;
else
one_extruder_printed_model = false;
// if (is_one_extruder_printed_model())
// only_extruder = extruder;
// else
// one_extruder_printed_model = false;
}
}
IMSlider *m_layers_slider = m_canvas->get_gcode_viewer().get_layers_slider();
m_layers_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder);
m_layers_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder, can_change_color);
}
void Preview::update_layers_slider_from_canvas(wxKeyEvent &event)
@ -763,6 +769,7 @@ bool AssembleView::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrint
//BBS: GUI refactor: GLToolbar
m_canvas->enable_assemble_view_toolbar(false);
m_canvas->enable_return_toolbar(true);
m_canvas->enable_separator_toolbar(false);
// BBS: set volume_selection_mode to Volume
m_canvas->get_selection().set_volume_selection_mode(Selection::Volume);

View file

@ -450,7 +450,7 @@ bool generate_image(const std::string &filename, wxImage &image, wxSize img_size
if (method == GERNERATE_IMAGE_RESIZE) {
float h_factor = img.GetHeight() / (float) image.GetHeight();
float w_factor = img.GetWidth() / (float) image.GetWidth();
float factor = std::max(h_factor, w_factor);
float factor = std::min(h_factor, w_factor);
int tar_height = (int) ((float) img.GetHeight() / factor);
int tar_width = (int) ((float) img.GetWidth() / factor);
img = img.Rescale(tar_width, tar_height);
@ -465,9 +465,11 @@ bool generate_image(const std::string &filename, wxImage &image, wxSize img_size
return false;
}
image.ConvertAlphaToMask(image.GetMaskRed(), image.GetMaskGreen(), image.GetMaskBlue());
//image.ConvertAlphaToMask(image.GetMaskRed(), image.GetMaskGreen(), image.GetMaskBlue());
return true;
}
std::deque<wxDialog*> dialogStack;
}
}

View file

@ -104,6 +104,8 @@ struct DpiChangedEvent : public wxEvent {
wxDECLARE_EVENT(EVT_DPI_CHANGED_SLICER, DpiChangedEvent);
#endif // !wxVERSION_EQUAL_OR_GREATER_THAN
extern std::deque<wxDialog*> dialogStack;
template<class P> class DPIAware : public P
{
public:
@ -189,6 +191,19 @@ public:
event.Skip();
on_sys_color_changed();
});
if (std::is_same<wxDialog, P>::value) {
this->Bind(wxEVT_CHAR_HOOK, [this](wxKeyEvent& e) {
if (e.GetKeyCode() == WXK_ESCAPE) {
//if (this->IsModal())
// this->EndModal(wxID_CANCEL);
//else
this->Close();
}
else
e.Skip();
});
}
}
virtual ~DPIAware() {}
@ -208,6 +223,14 @@ public:
on_sys_color_changed();
}
#endif
int ShowModal()
{
dialogStack.push_front(this);
int r = wxDialog::ShowModal();
dialogStack.pop_front();
return r;
}
protected:
virtual void on_dpi_changed(const wxRect &suggested_rect) = 0;

View file

@ -209,6 +209,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
const float space_size = m_imgui->get_style_scaling() * 8;
const float clipping_slider_left = m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x + m_imgui->scaled(1.5f);
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.5f);
const float gap_fill_slider_left = m_imgui->calc_text_size(m_desc.at("gap_fill")).x + m_imgui->scaled(1.5f);
@ -220,7 +221,6 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
const float tips_width = m_imgui->calc_text_size(_L("Auto support threshold angle: ") + " 90 ").x + m_imgui->scaled(1.5f);
const float minimal_slider_width = m_imgui->scaled(4.f);
float slider_width_times = 1.5;
float caption_max = 0.f;
float total_text_max = 0.f;
@ -233,12 +233,10 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
const float sliders_left_width = std::max(std::max(cursor_slider_left, clipping_slider_left), std::max(highlight_slider_left, gap_fill_slider_left));
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
float window_width = minimal_slider_width + sliders_left_width + slider_icon_width;
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
window_width = std::max(window_width, total_text_max);
window_width = std::max(window_width, buttons_width);
window_width = std::max(window_width, tips_width);
const float sliders_width = m_imgui->scaled(7.0f);
const float drag_left_width = ImGui::GetStyle().WindowPadding.x + sliders_left_width + sliders_width - space_size;
float drag_pos_times = 0.7;
@ -252,15 +250,22 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
if (i != 0) ImGui::SameLine((empty_button_width + m_imgui->scaled(1.75f)) * i + m_imgui->scaled(1.3f));
if (m_current_tool == tool_icons[i]) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.81f, 0.81f, 0.81f, 1.0f)); // r, g, b, a
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.81f, 0.81f, 0.81f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.81f, 0.81f, 0.81f, 1.0f));
}
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0);
if (m_current_tool == tool_icons[i]) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); // r, g, b, a
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.86f, 0.99f, 0.91f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.86f, 0.99f, 0.91f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0);
}
bool btn_clicked = ImGui::Button(into_u8(btn_name).c_str());
if (m_current_tool == tool_icons[i])
{
ImGui::PopStyleColor(4);
ImGui::PopStyleVar(2);
}
ImGui::PopStyleVar(1);
if (m_current_tool == tool_icons[i])ImGui::PopStyleColor(3);
if (btn_clicked && m_current_tool != tool_icons[i]) {
m_current_tool = tool_icons[i];
@ -287,9 +292,9 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("cursor_size"));
ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width);
ImGui::PushItemWidth(sliders_width);
m_imgui->bbl_slider_float_style("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true);
ImGui::SameLine(window_width - drag_pos_times * slider_icon_width);
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##cursor_radius_input", &m_cursor_radius, 0.05f, 0.0f, 0.0f, "%.2f");
} else if (m_current_tool == ImGui::SphereButtonIcon) {
@ -299,9 +304,9 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("cursor_size"));
ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width);
ImGui::PushItemWidth(sliders_width);
m_imgui->bbl_slider_float_style("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true);
ImGui::SameLine(window_width - drag_pos_times * slider_icon_width);
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##cursor_radius_input", &m_cursor_radius, 0.05f, 0.0f, 0.0f, "%.2f");
} else if (m_current_tool == ImGui::FillButtonIcon) {
@ -312,13 +317,13 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
m_imgui->text(m_desc.at("smart_fill_angle"));
std::string format_str = std::string("%.f") + I18N::translate_utf8("", "Face angle threshold, placed after the number with no whitespace in between.");
ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width);
ImGui::PushItemWidth(sliders_width);
if (m_imgui->bbl_slider_float_style("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data(), 1.0f, true))
for (auto& triangle_selector : m_triangle_selectors) {
triangle_selector->seed_fill_unselect_all_triangles();
triangle_selector->request_update_render_data();
}
ImGui::SameLine(window_width - drag_pos_times * slider_icon_width);
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##smart_fill_angle_input", &m_smart_fill_angle, 0.05f, 0.0f, 0.0f, "%.2f");
} else if (m_current_tool == ImGui::GapFillIcon) {
@ -328,10 +333,10 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc["gap_area"] + ":");
ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width);
ImGui::PushItemWidth(sliders_width);
std::string format_str = std::string("%.2f") + I18N::translate_utf8("", "Triangle patch area threshold,""triangle patch will be merged to neighbor if its area is less than threshold");
m_imgui->bbl_slider_float_style("##gap_area", &TriangleSelectorPatch::gap_area, TriangleSelectorPatch::GapAreaMin, TriangleSelectorPatch::GapAreaMax, format_str.data(), 1.0f, true);
ImGui::SameLine(window_width - drag_pos_times * slider_icon_width);
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##gap_area_input", &TriangleSelectorPatch::gap_area, 0.05f, 0.0f, 0.0f, "%.2f");
}
@ -349,7 +354,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::SetCursorPosY(slider_start_position_y);
std::string format_str = std::string("%.f");
ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width);
ImGui::PushItemWidth(sliders_width);
wxString tooltip = _L("Highlight faces according to overhang angle.");
if (m_imgui->bbl_slider_float_style("##angle_threshold_deg", &m_highlight_by_angle_threshold_deg, 0.f, 90.f, format_str.data(), 1.0f, true, tooltip)) {
m_parent.set_slope_normal_angle(90.f - m_highlight_by_angle_threshold_deg);
@ -369,7 +374,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
}
ImGui::EndTooltip();
}
ImGui::SameLine(window_width - drag_pos_times * slider_icon_width);
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##angle_threshold_deg_input", &m_highlight_by_angle_threshold_deg, 0.05f, 0.0f, 0.0f, "%.2f");
@ -380,10 +385,10 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
auto clp_dist = float(m_c->object_clipper()->get_position());
ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width);
ImGui::PushItemWidth(sliders_width);
bool b_bbl_slider_float = m_imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
ImGui::SameLine(window_width - drag_pos_times * slider_icon_width);
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_drag_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");

View file

@ -328,16 +328,16 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
// BBS
ImGuiWrapper::push_toolbar_style(m_parent.get_scale());
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.0f, 16.0f));
GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
const float clipping_slider_left = m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x + m_imgui->scaled(1.f);
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
const float space_size = m_imgui->get_style_scaling() * 8;
const float clipping_slider_left = m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x + m_imgui->scaled(1.5f);
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.5f);
const float smart_fill_slider_left = m_imgui->calc_text_size(m_desc.at("smart_fill_angle")).x + m_imgui->scaled(1.5f);
const float edge_detect_slider_left = m_imgui->calc_text_size(m_desc.at("edge_detection")).x + m_imgui->scaled(1.f);
const float gap_area_slider_left = m_imgui->calc_text_size(m_desc.at("gap_area")).x + m_imgui->scaled(1.5f);
const float height_range_slider_left = m_imgui->calc_text_size(m_desc.at("height_range")).x + m_imgui->scaled(1.5f);
const float gap_area_slider_left = m_imgui->calc_text_size(m_desc.at("gap_area")).x + m_imgui->scaled(1.5f) + space_size;
const float height_range_slider_left = m_imgui->calc_text_size(m_desc.at("height_range")).x + m_imgui->scaled(2.f);
const float remove_btn_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
const float filter_btn_width = m_imgui->calc_text_size(m_desc.at("perform")).x + m_imgui->scaled(1.f);
@ -358,7 +358,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
const float height_max_width = std::max(clipping_slider_left,height_range_slider_left);
const float sliders_left_width = std::max(smart_fill_slider_left,
std::max(cursor_slider_left, std::max(edge_detect_slider_left, std::max(gap_area_slider_left, std::max(height_range_slider_left,
clipping_slider_left)))));
clipping_slider_left))))) + space_size;
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
float window_width = minimal_slider_width + sliders_left_width + slider_icon_width;
const int max_filament_items_per_line = 8;
@ -369,8 +369,10 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
window_width = std::max(window_width, buttons_width);
window_width = std::max(window_width, max_filament_items_per_line * filament_item_width + +m_imgui->scaled(0.5f));
const float sliders_width = m_imgui->scaled(7.0f);
const float drag_left_width = ImGui::GetStyle().WindowPadding.x + sliders_width - space_size;
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
float slider_width_times = 1.5;
ImDrawList * draw_list = ImGui::GetWindowDrawList();
ImVec2 pos = ImGui::GetCursorScreenPos();
static float color_button_high = 25.0;
@ -383,14 +385,15 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
float start_pos_x = ImGui::GetCursorPos().x;
const ImVec2 max_label_size = ImGui::CalcTextSize("99", NULL, true);
const float item_spacing = m_imgui->scaled(0.8f);
for (int extruder_idx = 0; extruder_idx < m_extruders_colors.size(); extruder_idx++) {
size_t n_extruder_colors = std::min((size_t)EnforcerBlockerType::ExtruderMax, m_extruders_colors.size());
for (int extruder_idx = 0; extruder_idx < n_extruder_colors; extruder_idx++) {
const std::array<float, 4> &extruder_color = m_extruders_colors[extruder_idx];
ImVec4 color_vec(extruder_color[0], extruder_color[1], extruder_color[2], extruder_color[3]);
std::string color_label = std::string("##extruder color ") + std::to_string(extruder_idx);
std::string item_text = std::to_string(extruder_idx + 1);
const ImVec2 label_size = ImGui::CalcTextSize(item_text.c_str(), NULL, true);
const ImVec2 button_size(max_label_size.x + m_imgui->scaled(0.5f), 0.f);
const ImVec2 button_size(max_label_size.x + m_imgui->scaled(0.5f),0.f);
float button_offset = start_pos_x;
if (extruder_idx % max_filament_items_per_line != 0) {
@ -402,13 +405,19 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGuiColorEditFlags flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip;
if (m_selected_extruder_idx != extruder_idx) flags |= ImGuiColorEditFlags_NoBorder;
#ifdef __APPLE__
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.4f, 0.4f, 0.4f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0);
bool color_picked = ImGui::ColorButton(color_label.c_str(), color_vec, flags, button_size);
ImGui::PopStyleVar(1);
ImGui::PopStyleVar(2);
ImGui::PopStyleColor(1);
#else
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 2.0);
bool color_picked = ImGui::ColorButton(color_label.c_str(), color_vec, flags, button_size);
ImGui::PopStyleVar(2);
ImGui::PopStyleColor(1);
#endif
color_button_high = ImGui::GetCursorPos().y - color_button - 2.0;
if (color_picked) { m_selected_extruder_idx = extruder_idx; }
@ -418,12 +427,14 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
// draw filament id
float gray = 0.299 * extruder_color[0] + 0.587 * extruder_color[1] + 0.114 * extruder_color[2];
ImGui::SameLine(button_offset + (button_size.x - label_size.x) / 2.f);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, {10.0,15.0});
if (gray * 255.f < 80.f)
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), item_text.c_str());
else
ImGui::TextColored(ImVec4(0.0f, 0.0f, 0.0f, 1.0f), item_text.c_str());
}
ImGui::PopStyleVar();
}
//ImGui::NewLine();
ImGui::Dummy(ImVec2(0.0f, ImGui::GetFontSize() * 0.1));
@ -432,20 +443,26 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
std::array<wchar_t, 6> tool_icons = { ImGui::CircleButtonIcon,ImGui::SphereButtonIcon, ImGui::TriangleButtonIcon, ImGui::HeightRangeIcon, ImGui::FillButtonIcon, ImGui::GapFillIcon };
std::array<wxString, 6> tool_tips = { _L("Circle"), _L("Sphere"), _L("Triangle"), _L("Height Range"), _L("Fill"), _L("Gap Fill") };
for (int i = 0; i < tool_icons.size(); i++) {
std::string str_label = std::string("##");
std::string str_label = std::string("");
std::wstring btn_name = tool_icons[i] + boost::nowide::widen(str_label);
if (i != 0) ImGui::SameLine((empty_button_width + m_imgui->scaled(1.75f)) * i + m_imgui->scaled(0.5f));
if (m_current_tool == tool_icons[i]) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.81f, 0.81f, 0.81f, 1.0f)); // r, g, b, a
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.81f, 0.81f, 0.81f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.81f, 0.81f, 0.81f, 1.0f));
}
if (i != 0) ImGui::SameLine((empty_button_width + m_imgui->scaled(1.75f)) * i + m_imgui->scaled(1.5f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0);
if (m_current_tool == tool_icons[i]) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); // r, g, b, a
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.86f, 0.99f, 0.91f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.86f, 0.99f, 0.91f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0);
}
bool btn_clicked = ImGui::Button(into_u8(btn_name).c_str());
if (m_current_tool == tool_icons[i])
{
ImGui::PopStyleColor(4);
ImGui::PopStyleVar(2);
}
ImGui::PopStyleVar(1);
if (m_current_tool == tool_icons[i]) ImGui::PopStyleColor(3);
if (btn_clicked && m_current_tool != tool_icons[i]) {
m_current_tool = tool_icons[i];
@ -475,9 +492,9 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("cursor_size"));
ImGui::SameLine(circle_max_width);
ImGui::PushItemWidth(window_width - circle_max_width - slider_width_times * slider_icon_width);
ImGui::PushItemWidth(sliders_width);
m_imgui->bbl_slider_float_style("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true);
ImGui::SameLine(window_width - slider_icon_width);
ImGui::SameLine(drag_left_width + circle_max_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##cursor_radius_input", &m_cursor_radius, 0.05f, 0.0f, 0.0f, "%.2f");
@ -488,9 +505,9 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
auto clp_dist = float(m_c->object_clipper()->get_position());
ImGui::SameLine(circle_max_width);
ImGui::PushItemWidth(window_width - circle_max_width - slider_width_times * slider_icon_width);
ImGui::PushItemWidth(sliders_width);
bool slider_clp_dist = m_imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
ImGui::SameLine(window_width - slider_icon_width);
ImGui::SameLine(drag_left_width + circle_max_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
@ -505,9 +522,9 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
auto clp_dist = float(m_c->object_clipper()->get_position());
ImGui::SameLine(clipping_slider_left);
ImGui::PushItemWidth(window_width - clipping_slider_left - slider_width_times * slider_icon_width);
ImGui::PushItemWidth(sliders_width);
bool slider_clp_dist = m_imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
ImGui::SameLine(window_width - slider_icon_width);
ImGui::SameLine(drag_left_width + clipping_slider_left);
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
@ -524,13 +541,13 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
std::string format_str = std::string("%.f") + I18N::translate_utf8("°", "Face angle threshold,"
"placed after the number with no whitespace in between.");
ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(window_width - sliders_left_width - slider_width_times * slider_icon_width);
ImGui::PushItemWidth(sliders_width);
if (m_imgui->bbl_slider_float_style("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data(), 1.0f, true))
for (auto &triangle_selector : m_triangle_selectors) {
triangle_selector->seed_fill_unselect_all_triangles();
triangle_selector->request_update_render_data();
}
ImGui::SameLine(window_width - slider_icon_width);
ImGui::SameLine(drag_left_width + sliders_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##smart_fill_angle_input", &m_smart_fill_angle, 0.05f, 0.0f, 0.0f, "%.2f");
} else {
@ -544,9 +561,9 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
auto clp_dist = float(m_c->object_clipper()->get_position());
ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(window_width - sliders_left_width - slider_width_times * slider_icon_width);
ImGui::PushItemWidth(sliders_width);
bool slider_clp_dist = m_imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
ImGui::SameLine(window_width - slider_icon_width);
ImGui::SameLine(drag_left_width + sliders_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
@ -558,10 +575,10 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc["height_range"] + ":");
ImGui::SameLine(height_max_width);
ImGui::PushItemWidth(window_width - height_max_width - slider_width_times * slider_icon_width);
ImGui::PushItemWidth(sliders_width);
std::string format_str = std::string("%.2f") + I18N::translate_utf8("mm", "Heigh range," "Facet in [cursor z, cursor z + height] will be selected.");
m_imgui->bbl_slider_float_style("##cursor_height", &m_cursor_height, CursorHeightMin, CursorHeightMax, format_str.data(), 1.0f, true);
ImGui::SameLine(window_width - slider_icon_width);
ImGui::SameLine(drag_left_width + height_max_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##cursor_height_input", &m_cursor_height, 0.05f, 0.0f, 0.0f, "%.2f");
@ -572,9 +589,9 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
auto clp_dist = float(m_c->object_clipper()->get_position());
ImGui::SameLine(height_max_width);
ImGui::PushItemWidth(window_width - height_max_width - slider_width_times * slider_icon_width);
ImGui::PushItemWidth(sliders_width);
bool slider_clp_dist = m_imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
ImGui::SameLine(window_width - slider_icon_width);
ImGui::SameLine(drag_left_width + height_max_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
@ -586,10 +603,10 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc["gap_area"] + ":");
ImGui::SameLine(gap_area_slider_left);
ImGui::PushItemWidth(window_width - gap_area_slider_left - slider_width_times * slider_icon_width);
ImGui::PushItemWidth(sliders_width);
std::string format_str = std::string("%.2f") + I18N::translate_utf8("", "Triangle patch area threshold,""triangle patch will be merged to neighbor if its area is less than threshold");
m_imgui->bbl_slider_float_style("##gap_area", &TriangleSelectorPatch::gap_area, TriangleSelectorPatch::GapAreaMin, TriangleSelectorPatch::GapAreaMax, format_str.data(), 1.0f, true);
ImGui::SameLine(window_width - slider_icon_width);
ImGui::SameLine(drag_left_width + gap_area_slider_left);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##gap_area_input", &TriangleSelectorPatch::gap_area, 0.05f, 0.0f, 0.0f, "%.2f");
}
@ -638,7 +655,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
GizmoImguiEnd();
// BBS
ImGui::PopStyleVar(1);
ImGuiWrapper::pop_toolbar_style();
}
@ -689,7 +705,8 @@ void GLGizmoMmuSegmentation::init_model_triangle_selectors()
const TriangleMesh* mesh = &mv->mesh();
m_triangle_selectors.emplace_back(std::make_unique<TriangleSelectorPatch>(*mesh, ebt_colors, 0.2));
// Reset of TriangleSelector is done inside TriangleSelectorMmGUI's constructor, so we don't need it to perform it again in deserialize().
m_triangle_selectors.back()->deserialize(mv->mmu_segmentation_facets.get_data(), false);
EnforcerBlockerType max_ebt = (EnforcerBlockerType)std::min(m_extruders_colors.size(), (size_t)EnforcerBlockerType::ExtruderMax);
m_triangle_selectors.back()->deserialize(mv->mmu_segmentation_facets.get_data(), false, max_ebt);
m_triangle_selectors.back()->request_update_render_data();
m_volumes_extruder_idxs.push_back(mv->extruder_id());
}
@ -754,6 +771,7 @@ void GLGizmoMmuSegmentation::on_set_state()
if (get_state() == Off) {
ModelObject* mo = m_c->selection_info()->model_object();
if (mo) Slic3r::save_object_mesh(*mo);
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE));
}
}

View file

@ -1091,7 +1091,7 @@ void TriangleSelectorPatch::render(ImGuiWrapper* imgui)
void TriangleSelectorPatch::update_triangles_per_type()
{
m_triangle_patches.resize((int)EnforcerBlockerType::ExtruderMax);
m_triangle_patches.resize((int)EnforcerBlockerType::ExtruderMax + 1);
for (int i = 0; i < m_triangle_patches.size(); i++) {
auto& patch = m_triangle_patches[i];
patch.type = (EnforcerBlockerType)i;

View file

@ -178,17 +178,11 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
const float space_size = m_imgui->get_style_scaling() * 8;
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
m_imgui->calc_text_size(m_desc.at("reset_direction")).x)
+ m_imgui->scaled(1.5f);
const float cursor_size_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
const float cursor_type_radio_left = m_imgui->calc_text_size(m_desc["cursor_type"]).x + m_imgui->scaled(1.f);
const float cursor_type_radio_sphere = m_imgui->calc_text_size(m_desc["sphere"]).x + m_imgui->scaled(2.5f);
const float cursor_type_radio_circle = m_imgui->calc_text_size(m_desc["circle"]).x + m_imgui->scaled(2.5f);
const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
const float minimal_slider_width = m_imgui->scaled(4.f);
const float empty_button_width = m_imgui->calc_button_size("").x;
float caption_max = 0.f;
@ -197,19 +191,12 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x);
total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x);
}
total_text_max += caption_max + m_imgui->scaled(1.f);
caption_max += m_imgui->scaled(1.f);
float slider_width_times = 1.5;
const float sliders_left_width = std::max(cursor_size_slider_left, clipping_slider_left);
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
float window_width = minimal_slider_width + sliders_left_width + slider_icon_width;
#else
float window_width = minimal_slider_width + sliders_left_width;
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
window_width = std::max(window_width, total_text_max);
window_width = std::max(window_width, button_width);
window_width = std::max(window_width, cursor_type_radio_left + cursor_type_radio_sphere + cursor_type_radio_circle);
const float sliders_width = m_imgui->scaled(7.0f);
const float drag_left_width = ImGui::GetStyle().WindowPadding.x + sliders_left_width + sliders_width - space_size;
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
@ -222,17 +209,22 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
std::wstring btn_name = tool_icons[i] + boost::nowide::widen(str_label);
if (i != 0) ImGui::SameLine((empty_button_width + m_imgui->scaled(1.75f)) * i + m_imgui->scaled(1.3f));
if (m_current_tool == tool_icons[i]) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.81f, 0.81f, 0.81f, 1.0f)); // r, g, b, a
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.81f, 0.81f, 0.81f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.81f, 0.81f, 0.81f, 1.0f));
}
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0);
if (m_current_tool == tool_icons[i]) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); // r, g, b, a
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.86f, 0.99f, 0.91f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.86f, 0.99f, 0.91f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.68f, 0.26f, 1.00f));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0);
}
bool btn_clicked = ImGui::Button(into_u8(btn_name).c_str());
if (m_current_tool == tool_icons[i])
{
ImGui::PopStyleColor(4);
ImGui::PopStyleVar(2);
}
ImGui::PopStyleVar(1);
if (m_current_tool == tool_icons[i])ImGui::PopStyleColor(3);
if (btn_clicked && m_current_tool != tool_icons[i]) {
m_current_tool = tool_icons[i];
for (auto& triangle_selector : m_triangle_selectors) {
@ -263,9 +255,9 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
m_imgui->text(m_desc.at("cursor_size"));
ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(window_width - sliders_left_width - slider_width_times * slider_icon_width);
ImGui::PushItemWidth(sliders_width);
m_imgui->bbl_slider_float_style("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true);
ImGui::SameLine(window_width - slider_icon_width);
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##cursor_radius_input", &m_cursor_radius, 0.05f, 0.0f, 0.0f, "%.2f");
@ -275,10 +267,10 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
auto clp_dist = float(m_c->object_clipper()->get_position());
ImGui::SameLine(sliders_left_width);
ImGui::PushItemWidth(window_width - sliders_left_width - slider_width_times * slider_icon_width);
ImGui::PushItemWidth(sliders_width);
bool slider_clp_dist = m_imgui->bbl_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
ImGui::SameLine(window_width - slider_icon_width);
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position(clp_dist, true); }

View file

@ -0,0 +1,210 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoText.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/ImGuiWrapper.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/Shape/TextShape.hpp"
#include <numeric>
#include <GL/glew.h>
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif
#include <imgui/imgui_internal.h>
namespace Slic3r {
namespace GUI {
static double g_normal_precise = 0.0015;
GLGizmoText::GLGizmoText(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
{
}
bool GLGizmoText::on_init()
{
m_avail_font_names = init_occt_fonts();
m_shortcut_key = WXK_CONTROL_T;
return true;
}
void GLGizmoText::on_set_state()
{
}
CommonGizmosDataID GLGizmoText::on_get_requirements() const
{
return CommonGizmosDataID::SelectionInfo;
}
std::string GLGizmoText::on_get_name() const
{
return _u8L("Text shape");
}
bool GLGizmoText::on_is_activable() const
{
// This is assumed in GLCanvas3D::do_rotate, do not change this
// without updating that function too.
return m_parent.get_selection().is_single_full_instance();
}
void GLGizmoText::on_render()
{
// TODO:
}
void GLGizmoText::on_render_for_picking()
{
// TODO:
}
void GLGizmoText::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);
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_ScrollbarBg, ImGuiWrapper::COL_WINDOW_BG);
ImGui::PushStyleColor(ImGuiCol_Button, { 1.00f, 1.00f, 1.00f, 0.0f });
}
void GLGizmoText::pop_combo_style()
{
ImGui::PopStyleVar(2);
ImGui::PopStyleColor(7);
}
// BBS
void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit)
{
const float win_h = ImGui::GetWindowHeight();
y = std::min(y, bottom_limit - win_h);
GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f);
static float last_y = 0.0f;
static float last_h = 0.0f;
const float currt_scale = m_parent.get_scale();
ImGuiWrapper::push_toolbar_style(currt_scale);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0,5.0) * currt_scale);
GizmoImguiBegin("Text", ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
float space_size = m_imgui->get_style_scaling() * 8;
float font_cap = m_imgui->calc_text_size(_L("Font")).x;
float size_cap = m_imgui->calc_text_size(_L("Size")).x;
float thickness_cap = m_imgui->calc_text_size(_L("Thickness")).x;
float input_cap = m_imgui->calc_text_size(_L("Input text")).x;
float caption_size = std::max(std::max(font_cap, size_cap), std::max(thickness_cap, input_cap)) + space_size + ImGui::GetStyle().WindowPadding.x;
float input_text_size = m_imgui->scaled(12.0f);
float button_size = ImGui::GetFrameHeight();
float input_size = input_text_size - button_size * 2 - ImGui::GetStyle().ItemSpacing.x * 4;
ImTextureID normal_B = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TEXT_B);
ImTextureID press_B_hover = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TEXT_B_HOVER);
ImTextureID press_B_press = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TEXT_B_PRESS);
ImTextureID normal_T = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TEXT_T);
ImTextureID press_T_hover = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TEXT_T_HOVER);
ImTextureID press_T_press = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TEXT_T_PRESS);
// adjust window position to avoid overlap the view toolbar
if (last_h != win_h || last_y != y) {
// ask canvas for another frame to render the window in the correct position
m_imgui->set_requires_extra_frame();
if (last_h != win_h)
last_h = win_h;
if (last_y != y)
last_y = y;
}
ImGui::AlignTextToFramePadding();
const char** cstr_font_names = (const char**)calloc(m_avail_font_names.size(), sizeof(const char*));
for (int i = 0; i < m_avail_font_names.size(); i++)
cstr_font_names[i] = m_avail_font_names[i].c_str();
m_imgui->text(_L("Font"));
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(input_text_size + ImGui::GetFrameHeight() * 2);
push_combo_style(currt_scale);
if (ImGui::BBLBeginCombo("##Font", cstr_font_names[m_curr_font_idx], 0)) {
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(4.0f, 0.0f) * currt_scale);
for (int i = 0; i < m_avail_font_names.size(); i++) {
const bool is_selected = (m_curr_font_idx == i);
if (ImGui::BBLSelectable(cstr_font_names[i], is_selected)) {
m_curr_font_idx = i;
m_font_name = cstr_font_names[m_curr_font_idx];
}
if (is_selected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::PopStyleVar(2);
ImGui::EndCombo();
}
pop_combo_style();
ImGui::AlignTextToFramePadding();
m_imgui->text(_L("Size"));
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(input_size);
ImGui::InputFloat("###font_size", &m_font_size, 0.0f, 0.0f, "%.2f");
if (m_font_size < 3.0f)m_font_size = 3.0f;
ImGui::SameLine();
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {0.0,0.0});
ImGui::BBLImageButton(normal_B,press_B_hover,press_B_press,{button_size,button_size},m_bold);
ImGui::SameLine();
ImGui::BBLImageButton(normal_T,press_T_hover,press_T_press,{button_size,button_size},m_italic);
ImGui::PopStyleVar(2);
ImGui::AlignTextToFramePadding();
m_imgui->text(_L("Thickness"));
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(input_text_size);
ImGui::InputFloat("###text_thickness", &m_thickness,0.0f, 0.0f, "%.2f");
if (m_thickness < 0.1f)m_thickness = 0.1f;
ImGui::AlignTextToFramePadding();
m_imgui->text(_L("Input text"));
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(input_text_size);
ImGui::InputText("", m_text, sizeof(m_text));
ImGui::Separator();
m_imgui->disabled_begin(m_text[0] == '\0' || m_text[0] == ' ');
float offset = caption_size + input_text_size - m_imgui->calc_text_size(_L("Add")).x - space_size;
ImGui::Dummy({0.0, 0.0});
ImGui::SameLine(offset);
bool add_clicked = m_imgui->button(_L("Add"));
if (add_clicked) {
TriangleMesh mesh;
load_text_shape(m_text, m_font_name.c_str(), m_font_size, m_thickness, m_bold, m_italic, mesh);
ObjectList* obj_list = wxGetApp().obj_list();
obj_list->load_mesh_part(mesh, "text_shape");
}
m_imgui->disabled_end();
GizmoImguiEnd();
ImGui::PopStyleVar();
ImGuiWrapper::pop_toolbar_style();
}
} // namespace GUI
} // namespace Slic3r

Some files were not shown because too many files have changed in this diff Show more