mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-12-11 16:00:17 -07:00
Merge remote-tracking branch 'remote/master' into SoftFever
This commit is contained in:
commit
4fd174175c
298 changed files with 13879 additions and 6228 deletions
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
112
src/libslic3r/AABBTreeLines.hpp
Normal file
112
src/libslic3r/AABBTreeLines.hpp
Normal 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_ */
|
||||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 << " ";
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 ®ion : by_region)
|
||||
if (! region.perimeters.empty()) {
|
||||
m_config.apply(print.get_print_region(®ion - &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!
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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_
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
291
src/libslic3r/Geometry/Bicubic.hpp
Normal file
291
src/libslic3r/Geometry/Bicubic.hpp
Normal 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 */
|
||||
218
src/libslic3r/Geometry/Curves.hpp
Normal file
218
src/libslic3r/Geometry/Curves.hpp
Normal 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_ */
|
||||
|
|
@ -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 ¢er,
|
||||
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 ¢er,
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
142
src/libslic3r/NormalUtils.cpp
Normal file
142
src/libslic3r/NormalUtils.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
69
src/libslic3r/NormalUtils.hpp
Normal file
69
src/libslic3r/NormalUtils.hpp
Normal 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_
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)) {}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ public:
|
|||
{
|
||||
profile_ = profile;
|
||||
prediction = 0;
|
||||
weight = 0.0f;
|
||||
}
|
||||
|
||||
BBLSliceInfo(const BBLSliceInfo& obj) {
|
||||
|
|
|
|||
203
src/libslic3r/Shape/TextShape.cpp
Normal file
203
src/libslic3r/Shape/TextShape.cpp
Normal 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
|
||||
12
src/libslic3r/Shape/TextShape.hpp
Normal file
12
src/libslic3r/Shape/TextShape.hpp
Normal 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_
|
||||
183
src/libslic3r/ShortEdgeCollapse.cpp
Normal file
183
src/libslic3r/ShortEdgeCollapse.cpp
Normal 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
|
||||
|
||||
16
src/libslic3r/ShortEdgeCollapse.hpp
Normal file
16
src/libslic3r/ShortEdgeCollapse.hpp
Normal 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_ */
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
71
src/libslic3r/TriangleSetSampling.cpp
Normal file
71
src/libslic3r/TriangleSetSampling.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
20
src/libslic3r/TriangleSetSampling.hpp
Normal file
20
src/libslic3r/TriangleSetSampling.hpp
Normal 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_ */
|
||||
|
|
@ -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__
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) == "<") {
|
||||
ret += s.substr(pos, i - pos) + "<";
|
||||
i += 4;
|
||||
pos = i;
|
||||
}
|
||||
else if (s.substr(i, 4) == ">") {
|
||||
ret += s.substr(pos, i - pos) + ">";
|
||||
i += 4;
|
||||
pos = i;
|
||||
}
|
||||
else if (s.substr(i, 5) == "&") {
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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) +
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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_
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
210
src/slic3r/GUI/Gizmos/GLGizmoText.cpp
Normal file
210
src/slic3r/GUI/Gizmos/GLGizmoText.cpp
Normal 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
Loading…
Add table
Add a link
Reference in a new issue