mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06: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
	
	 SoftFever
						SoftFever