mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 02:41:17 +00:00
Merge remote-tracking branch 'remote/main' into local-fp
This commit is contained in:
@@ -26,6 +26,7 @@ add_subdirectory(libslic3r)
|
||||
|
||||
if (SLIC3R_GUI)
|
||||
add_subdirectory(imgui)
|
||||
add_subdirectory(imguizmo)
|
||||
add_subdirectory(hidapi)
|
||||
include_directories(hidapi/include)
|
||||
|
||||
@@ -101,6 +102,15 @@ if (SLIC3R_GUI)
|
||||
add_subdirectory(slic3r)
|
||||
endif()
|
||||
|
||||
if(ORCA_TOOLS)
|
||||
# OrcaSlicer_profile_validator
|
||||
add_executable(OrcaSlicer_profile_validator OrcaSlicer_profile_validator.cpp)
|
||||
target_link_libraries(OrcaSlicer_profile_validator libslic3r boost_headeronly libcurl OpenSSL::SSL OpenSSL::Crypto)
|
||||
target_compile_definitions(OrcaSlicer_profile_validator PRIVATE -DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x602 -DBOOST_SYSTEM_USE_UTF8)
|
||||
if(WIN32)
|
||||
target_link_libraries(OrcaSlicer_profile_validator bcrypt.lib)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Create a slic3r executable
|
||||
# Process mainfests for various platforms.
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using namespace Slic3r;
|
||||
namespace po = boost::program_options;
|
||||
|
||||
void generate_custom_presets(PresetBundle* preset_bundle, AppConfig& app_config)
|
||||
{
|
||||
struct cus_preset
|
||||
{
|
||||
std::string name;
|
||||
std::string parent_name;
|
||||
};
|
||||
// create user presets
|
||||
auto createCustomPrinters = [&](Preset::Type type) {
|
||||
std::vector<cus_preset> custom_preset;
|
||||
PresetCollection* collection = nullptr;
|
||||
if (type == Preset::TYPE_PRINT)
|
||||
collection = &preset_bundle->prints;
|
||||
else if (type == Preset::TYPE_FILAMENT)
|
||||
collection = &preset_bundle->filaments;
|
||||
else if (type == Preset::TYPE_PRINTER)
|
||||
collection = &preset_bundle->printers;
|
||||
else
|
||||
return;
|
||||
custom_preset.reserve(collection->size());
|
||||
for (auto& parent : collection->get_presets()) {
|
||||
if (!parent.is_system)
|
||||
continue;
|
||||
auto new_name = parent.name + "_orca_test";
|
||||
if (parent.vendor)
|
||||
new_name = parent.vendor->name + "_" + new_name;
|
||||
custom_preset.push_back({new_name, parent.name});
|
||||
}
|
||||
for (auto p : custom_preset) {
|
||||
// Creating a new preset.
|
||||
auto parent = collection->find_preset(p.parent_name);
|
||||
if (type == Preset::TYPE_FILAMENT)
|
||||
parent->config.set_key_value("filament_start_gcode",
|
||||
new ConfigOptionStrings({"this_is_orca_test_filament_start_gcode_mock"}));
|
||||
else if (type == Preset::TYPE_PRINT)
|
||||
parent->config.set_key_value("filename_format", new ConfigOptionString("this_is_orca_test_filename_format_mock"));
|
||||
else if (type == Preset::TYPE_PRINTER)
|
||||
parent->config.set_key_value("machine_start_gcode",
|
||||
new ConfigOptionString("this_is_orca_test_machine_start_gcode_mock"));
|
||||
|
||||
collection->save_current_preset(p.name, false, false, parent);
|
||||
|
||||
}
|
||||
};
|
||||
createCustomPrinters(Preset::TYPE_PRINTER);
|
||||
createCustomPrinters(Preset::TYPE_FILAMENT);
|
||||
createCustomPrinters(Preset::TYPE_PRINT);
|
||||
|
||||
std::string user_sub_folder = DEFAULT_USER_FOLDER_NAME;
|
||||
const std::string dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/" + user_sub_folder;
|
||||
|
||||
fs::path user_folder(data_dir() + "/" + PRESET_USER_DIR);
|
||||
if (!fs::exists(user_folder))
|
||||
fs::create_directory(user_folder);
|
||||
|
||||
fs::path folder(dir_user_presets);
|
||||
if (!fs::exists(folder))
|
||||
fs::create_directory(folder);
|
||||
std::vector<std::string> need_to_delete_list; // store setting ids of preset
|
||||
|
||||
preset_bundle->prints.save_user_presets(dir_user_presets, PRESET_PRINT_NAME, need_to_delete_list);
|
||||
preset_bundle->filaments.save_user_presets(dir_user_presets, PRESET_FILAMENT_NAME, need_to_delete_list);
|
||||
preset_bundle->printers.save_user_presets(dir_user_presets, PRESET_PRINTER_NAME, need_to_delete_list);
|
||||
|
||||
std::cout << "Custom presets generated successfully" << std::endl;
|
||||
}
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
po::options_description desc("Orca Profile Validator\nUsage");
|
||||
// clang-format off
|
||||
desc.add_options()("help,h", "help")
|
||||
("path,p", po::value<std::string>()->default_value("../../../resources/profiles"), "profile folder")
|
||||
("vendor,v", po::value<std::string>()->default_value(""), "Vendor name. Optional, all profiles present in the folder will be validated if not specified")
|
||||
("generate_presets,g", po::value<bool>()->default_value(false), "Generate user presets for mock test")
|
||||
("log_level,l", po::value<int>()->default_value(2), "Log level. Optional, default is 2 (warning). Higher values produce more detailed logs.");
|
||||
// clang-format on
|
||||
|
||||
po::variables_map vm;
|
||||
try {
|
||||
po::store(po::parse_command_line(argc, argv, desc), vm);
|
||||
|
||||
if (vm.count("help")) {
|
||||
std::cout << desc << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
po::notify(vm);
|
||||
} catch (const po::error& e) {
|
||||
std::cerr << "Error: " << e.what() << "\n";
|
||||
std::cerr << desc << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string path = vm["path"].as<std::string>();
|
||||
std::string vendor = vm["vendor"].as<std::string>();
|
||||
int log_level = vm["log_level"].as<int>();
|
||||
bool generate_user_preset = vm["generate_presets"].as<bool>();
|
||||
|
||||
// check if path is valid, and return error if not
|
||||
if (!fs::exists(path) || !fs::is_directory(path)) {
|
||||
std::cerr << "Error: " << path << " is not a valid directory\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// std::cout<<"path: "<<path<<std::endl;
|
||||
// std::cout<<"vendor: "<<vendor<<std::endl;
|
||||
// std::cout<<"log_level: "<<log_level<<std::endl;
|
||||
|
||||
set_data_dir(path);
|
||||
|
||||
auto user_dir = fs::path(Slic3r::data_dir()) / PRESET_USER_DIR;
|
||||
user_dir.make_preferred();
|
||||
if (!fs::exists(user_dir))
|
||||
fs::create_directory(user_dir);
|
||||
|
||||
set_logging_level(log_level);
|
||||
auto preset_bundle = new PresetBundle();
|
||||
// preset_bundle->setup_directories();
|
||||
preset_bundle->set_is_validation_mode(true);
|
||||
preset_bundle->set_vendor_to_validate(vendor);
|
||||
|
||||
preset_bundle->set_default_suppressed(true);
|
||||
AppConfig app_config;
|
||||
app_config.set("preset_folder", "default");
|
||||
|
||||
if(generate_user_preset)
|
||||
preset_bundle->remove_user_presets_directory("default");
|
||||
|
||||
try {
|
||||
auto preset_substitutions = preset_bundle->load_presets(app_config, ForwardCompatibilitySubstitutionRule::EnableSystemSilent);
|
||||
} catch (const std::exception& ex) {
|
||||
BOOST_LOG_TRIVIAL(error) << ex.what();
|
||||
std::cout << "Validation failed" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (generate_user_preset) {
|
||||
generate_custom_presets(preset_bundle, app_config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (preset_bundle->has_errors()) {
|
||||
std::cout << "Validation failed" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Validation completed successfully" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
project(imguizmo)
|
||||
|
||||
add_library(imguizmo STATIC
|
||||
ImGuizmo.h
|
||||
ImGuizmo.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(imguizmo PRIVATE imgui)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,282 @@
|
||||
// https://github.com/CedricGuillemet/ImGuizmo
|
||||
// v 1.89 WIP
|
||||
//
|
||||
// The MIT License(MIT)
|
||||
//
|
||||
// Copyright(c) 2021 Cedric Guillemet
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files(the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions :
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// History :
|
||||
// 2019/11/03 View gizmo
|
||||
// 2016/09/11 Behind camera culling. Scaling Delta matrix not multiplied by source matrix scales. local/world rotation and translation fixed. Display message is incorrect (X: ... Y:...) in local mode.
|
||||
// 2016/09/09 Hatched negative axis. Snapping. Documentation update.
|
||||
// 2016/09/04 Axis switch and translation plan autohiding. Scale transform stability improved
|
||||
// 2016/09/01 Mogwai changed to Manipulate. Draw debug cube. Fixed inverted scale. Mixing scale and translation/rotation gives bad results.
|
||||
// 2016/08/31 First version
|
||||
//
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// Future (no order):
|
||||
//
|
||||
// - Multi view
|
||||
// - display rotation/translation/scale infos in local/world space and not only local
|
||||
// - finish local/world matrix application
|
||||
// - OPERATION as bitmask
|
||||
//
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// Example
|
||||
#if 0
|
||||
void EditTransform(const Camera& camera, matrix_t& matrix)
|
||||
{
|
||||
static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE);
|
||||
static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD);
|
||||
if (ImGui::IsKeyPressed(90))
|
||||
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
|
||||
if (ImGui::IsKeyPressed(69))
|
||||
mCurrentGizmoOperation = ImGuizmo::ROTATE;
|
||||
if (ImGui::IsKeyPressed(82)) // r Key
|
||||
mCurrentGizmoOperation = ImGuizmo::SCALE;
|
||||
if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
|
||||
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE))
|
||||
mCurrentGizmoOperation = ImGuizmo::ROTATE;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE))
|
||||
mCurrentGizmoOperation = ImGuizmo::SCALE;
|
||||
float matrixTranslation[3], matrixRotation[3], matrixScale[3];
|
||||
ImGuizmo::DecomposeMatrixToComponents(matrix.m16, matrixTranslation, matrixRotation, matrixScale);
|
||||
ImGui::InputFloat3("Tr", matrixTranslation, 3);
|
||||
ImGui::InputFloat3("Rt", matrixRotation, 3);
|
||||
ImGui::InputFloat3("Sc", matrixScale, 3);
|
||||
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, matrix.m16);
|
||||
|
||||
if (mCurrentGizmoOperation != ImGuizmo::SCALE)
|
||||
{
|
||||
if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL))
|
||||
mCurrentGizmoMode = ImGuizmo::LOCAL;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("World", mCurrentGizmoMode == ImGuizmo::WORLD))
|
||||
mCurrentGizmoMode = ImGuizmo::WORLD;
|
||||
}
|
||||
static bool useSnap(false);
|
||||
if (ImGui::IsKeyPressed(83))
|
||||
useSnap = !useSnap;
|
||||
ImGui::Checkbox("", &useSnap);
|
||||
ImGui::SameLine();
|
||||
vec_t snap;
|
||||
switch (mCurrentGizmoOperation)
|
||||
{
|
||||
case ImGuizmo::TRANSLATE:
|
||||
snap = config.mSnapTranslation;
|
||||
ImGui::InputFloat3("Snap", &snap.x);
|
||||
break;
|
||||
case ImGuizmo::ROTATE:
|
||||
snap = config.mSnapRotation;
|
||||
ImGui::InputFloat("Angle Snap", &snap.x);
|
||||
break;
|
||||
case ImGuizmo::SCALE:
|
||||
snap = config.mSnapScale;
|
||||
ImGui::InputFloat("Scale Snap", &snap.x);
|
||||
break;
|
||||
}
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
|
||||
ImGuizmo::Manipulate(camera.mView.m16, camera.mProjection.m16, mCurrentGizmoOperation, mCurrentGizmoMode, matrix.m16, NULL, useSnap ? &snap.x : NULL);
|
||||
}
|
||||
#endif
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_IMGUI_API
|
||||
#include "imconfig.h"
|
||||
#endif
|
||||
#ifndef IMGUI_API
|
||||
#define IMGUI_API
|
||||
#endif
|
||||
|
||||
#ifndef IMGUIZMO_NAMESPACE
|
||||
#define IMGUIZMO_NAMESPACE ImGuizmo
|
||||
#endif
|
||||
|
||||
namespace IMGUIZMO_NAMESPACE
|
||||
{
|
||||
// call inside your own window and before Manipulate() in order to draw gizmo to that window.
|
||||
// Or pass a specific ImDrawList to draw to (e.g. ImGui::GetForegroundDrawList()).
|
||||
IMGUI_API void SetDrawlist(ImDrawList* drawlist = nullptr);
|
||||
|
||||
// call BeginFrame right after ImGui_XXXX_NewFrame();
|
||||
IMGUI_API void BeginFrame();
|
||||
|
||||
// this is necessary because when imguizmo is compiled into a dll, and imgui into another
|
||||
// globals are not shared between them.
|
||||
// More details at https://stackoverflow.com/questions/19373061/what-happens-to-global-and-static-variables-in-a-shared-library-when-it-is-dynam
|
||||
// expose method to set imgui context
|
||||
IMGUI_API void SetImGuiContext(ImGuiContext* ctx);
|
||||
|
||||
// return true if mouse cursor is over any gizmo control (axis, plan or screen component)
|
||||
IMGUI_API bool IsOver();
|
||||
|
||||
// return true if mouse IsOver or if the gizmo is in moving state
|
||||
IMGUI_API bool IsUsing();
|
||||
|
||||
// return true if any gizmo is in moving state
|
||||
IMGUI_API bool IsUsingAny();
|
||||
|
||||
// enable/disable the gizmo. Stay in the state until next call to Enable.
|
||||
// gizmo is rendered with gray half transparent color when disabled
|
||||
IMGUI_API void Enable(bool enable);
|
||||
|
||||
// helper functions for manualy editing translation/rotation/scale with an input float
|
||||
// translation, rotation and scale float points to 3 floats each
|
||||
// Angles are in degrees (more suitable for human editing)
|
||||
// example:
|
||||
// float matrixTranslation[3], matrixRotation[3], matrixScale[3];
|
||||
// ImGuizmo::DecomposeMatrixToComponents(gizmoMatrix.m16, matrixTranslation, matrixRotation, matrixScale);
|
||||
// ImGui::InputFloat3("Tr", matrixTranslation, 3);
|
||||
// ImGui::InputFloat3("Rt", matrixRotation, 3);
|
||||
// ImGui::InputFloat3("Sc", matrixScale, 3);
|
||||
// ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, gizmoMatrix.m16);
|
||||
//
|
||||
// These functions have some numerical stability issues for now. Use with caution.
|
||||
IMGUI_API void DecomposeMatrixToComponents(const float* matrix, float* translation, float* rotation, float* scale);
|
||||
IMGUI_API void RecomposeMatrixFromComponents(const float* translation, const float* rotation, const float* scale, float* matrix);
|
||||
|
||||
IMGUI_API void SetRect(float x, float y, float width, float height);
|
||||
// default is false
|
||||
IMGUI_API void SetOrthographic(bool isOrthographic);
|
||||
|
||||
// Render a cube with face color corresponding to face normal. Usefull for debug/tests
|
||||
IMGUI_API void DrawCubes(const float* view, const float* projection, const float* matrices, int matrixCount);
|
||||
IMGUI_API void DrawGrid(const float* view, const float* projection, const float* matrix, const float gridSize);
|
||||
|
||||
// call it when you want a gizmo
|
||||
// Needs view and projection matrices.
|
||||
// matrix parameter is the source matrix (where will be gizmo be drawn) and might be transformed by the function. Return deltaMatrix is optional
|
||||
// translation is applied in world space
|
||||
enum OPERATION
|
||||
{
|
||||
TRANSLATE_X = (1u << 0),
|
||||
TRANSLATE_Y = (1u << 1),
|
||||
TRANSLATE_Z = (1u << 2),
|
||||
ROTATE_X = (1u << 3),
|
||||
ROTATE_Y = (1u << 4),
|
||||
ROTATE_Z = (1u << 5),
|
||||
ROTATE_SCREEN = (1u << 6),
|
||||
SCALE_X = (1u << 7),
|
||||
SCALE_Y = (1u << 8),
|
||||
SCALE_Z = (1u << 9),
|
||||
BOUNDS = (1u << 10),
|
||||
SCALE_XU = (1u << 11),
|
||||
SCALE_YU = (1u << 12),
|
||||
SCALE_ZU = (1u << 13),
|
||||
|
||||
TRANSLATE = TRANSLATE_X | TRANSLATE_Y | TRANSLATE_Z,
|
||||
ROTATE = ROTATE_X | ROTATE_Y | ROTATE_Z | ROTATE_SCREEN,
|
||||
SCALE = SCALE_X | SCALE_Y | SCALE_Z,
|
||||
SCALEU = SCALE_XU | SCALE_YU | SCALE_ZU, // universal
|
||||
UNIVERSAL = TRANSLATE | ROTATE | SCALEU
|
||||
};
|
||||
|
||||
inline OPERATION operator|(OPERATION lhs, OPERATION rhs)
|
||||
{
|
||||
return static_cast<OPERATION>(static_cast<int>(lhs) | static_cast<int>(rhs));
|
||||
}
|
||||
|
||||
enum MODE
|
||||
{
|
||||
LOCAL,
|
||||
WORLD
|
||||
};
|
||||
|
||||
IMGUI_API bool Manipulate(const float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float* deltaMatrix = NULL, const float* snap = NULL, const float* localBounds = NULL, const float* boundsSnap = NULL);
|
||||
//
|
||||
// Please note that this cubeview is patented by Autodesk : https://patents.google.com/patent/US7782319B2/en
|
||||
// It seems to be a defensive patent in the US. I don't think it will bring troubles using it as
|
||||
// other software are using the same mechanics. But just in case, you are now warned!
|
||||
//
|
||||
IMGUI_API bool ViewManipulate(float* view, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
|
||||
|
||||
// use this version if you did not call Manipulate before and you are just using ViewManipulate
|
||||
IMGUI_API bool ViewManipulate(float* view, const float* projection, OPERATION operation, MODE mode, float* matrix, float length, ImVec2 position, ImVec2 size, ImU32 backgroundColor);
|
||||
|
||||
IMGUI_API void SetID(int id);
|
||||
|
||||
// return true if the cursor is over the operation's gizmo
|
||||
IMGUI_API bool IsOver(OPERATION op);
|
||||
IMGUI_API void SetGizmoSizeClipSpace(float value);
|
||||
|
||||
// Allow axis to flip
|
||||
// When true (default), the guizmo axis flip for better visibility
|
||||
// When false, they always stay along the positive world/local axis
|
||||
IMGUI_API void AllowAxisFlip(bool value);
|
||||
|
||||
// Configure the limit where axis are hidden
|
||||
IMGUI_API void SetAxisLimit(float value);
|
||||
// Configure the limit where planes are hiden
|
||||
IMGUI_API void SetPlaneLimit(float value);
|
||||
|
||||
enum COLOR
|
||||
{
|
||||
DIRECTION_X, // directionColor[0]
|
||||
DIRECTION_Y, // directionColor[1]
|
||||
DIRECTION_Z, // directionColor[2]
|
||||
PLANE_X, // planeColor[0]
|
||||
PLANE_Y, // planeColor[1]
|
||||
PLANE_Z, // planeColor[2]
|
||||
SELECTION, // selectionColor
|
||||
INACTIVE, // inactiveColor
|
||||
TRANSLATION_LINE, // translationLineColor
|
||||
SCALE_LINE,
|
||||
ROTATION_USING_BORDER,
|
||||
ROTATION_USING_FILL,
|
||||
HATCHED_AXIS_LINES,
|
||||
TEXT,
|
||||
TEXT_SHADOW,
|
||||
COUNT
|
||||
};
|
||||
|
||||
enum Axis
|
||||
{
|
||||
Axis_X,
|
||||
Axis_Y,
|
||||
Axis_Z,
|
||||
Axis_COUNT,
|
||||
};
|
||||
|
||||
struct Style
|
||||
{
|
||||
IMGUI_API Style();
|
||||
|
||||
float TranslationLineThickness; // Thickness of lines for translation gizmo
|
||||
float TranslationLineArrowSize; // Size of arrow at the end of lines for translation gizmo
|
||||
float RotationLineThickness; // Thickness of lines for rotation gizmo
|
||||
float RotationOuterLineThickness; // Thickness of line surrounding the rotation gizmo
|
||||
float ScaleLineThickness; // Thickness of lines for scale gizmo
|
||||
float ScaleLineCircleSize; // Size of circle at the end of lines for scale gizmo
|
||||
float HatchedAxisLineThickness; // Thickness of hatched axis lines
|
||||
float CenterCircleSize; // Size of circle at the center of the translate/scale gizmo
|
||||
|
||||
ImVec4 Colors[COLOR::COUNT];
|
||||
|
||||
char AxisLabels[Axis::Axis_COUNT][32];
|
||||
};
|
||||
|
||||
IMGUI_API Style& GetStyle();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Cedric Guillemet
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,192 @@
|
||||
# ImGuizmo
|
||||
|
||||
Latest stable tagged version is 1.83. Current master version is 1.84 WIP.
|
||||
|
||||
What started with the gizmo is now a collection of dear imgui widgets and more advanced controls.
|
||||
|
||||
## Guizmos
|
||||
|
||||
### ImViewGizmo
|
||||
|
||||
Manipulate view orientation with 1 single line of code
|
||||
|
||||

|
||||
|
||||
### ImGuizmo
|
||||
|
||||
ImGizmo is a small (.h and .cpp) library built ontop of Dear ImGui that allow you to manipulate(Rotate & translate at the moment) 4x4 float matrices. No other dependancies. Coded with Immediate Mode (IM) philosophy in mind.
|
||||
|
||||
Built against DearImgui 1.53WIP
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
There is now a sample for Win32/OpenGL ! With a binary in bin directory.
|
||||

|
||||
|
||||
### ImSequencer
|
||||
|
||||
A WIP little sequencer used to edit frame start/end for different events in a timeline.
|
||||

|
||||
Check the sample for the documentation. More to come...
|
||||
|
||||
### Graph Editor
|
||||
|
||||
Nodes + connections. Custom draw inside nodes is possible with the delegate system in place.
|
||||

|
||||
|
||||
### API doc
|
||||
|
||||
Call BeginFrame right after ImGui_XXXX_NewFrame();
|
||||
|
||||
```C++
|
||||
void BeginFrame();
|
||||
```
|
||||
|
||||
return true if mouse cursor is over any gizmo control (axis, plan or screen component)
|
||||
|
||||
```C++
|
||||
bool IsOver();**
|
||||
```
|
||||
|
||||
return true if mouse IsOver or if the gizmo is in moving state
|
||||
|
||||
```C++
|
||||
bool IsUsing();**
|
||||
```
|
||||
|
||||
enable/disable the gizmo. Stay in the state until next call to Enable. gizmo is rendered with gray half transparent color when disabled
|
||||
|
||||
```C++
|
||||
void Enable(bool enable);**
|
||||
```
|
||||
|
||||
helper functions for manualy editing translation/rotation/scale with an input float
|
||||
translation, rotation and scale float points to 3 floats each
|
||||
Angles are in degrees (more suitable for human editing)
|
||||
example:
|
||||
|
||||
```C++
|
||||
float matrixTranslation[3], matrixRotation[3], matrixScale[3];
|
||||
ImGuizmo::DecomposeMatrixToComponents(gizmoMatrix.m16, matrixTranslation, matrixRotation, matrixScale);
|
||||
ImGui::InputFloat3("Tr", matrixTranslation, 3);
|
||||
ImGui::InputFloat3("Rt", matrixRotation, 3);
|
||||
ImGui::InputFloat3("Sc", matrixScale, 3);
|
||||
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, gizmoMatrix.m16);
|
||||
```
|
||||
|
||||
These functions have some numerical stability issues for now. Use with caution.
|
||||
|
||||
```C++
|
||||
void DecomposeMatrixToComponents(const float *matrix, float *translation, float *rotation, float *scale);
|
||||
void RecomposeMatrixFromComponents(const float *translation, const float *rotation, const float *scale, float *matrix);**
|
||||
```
|
||||
|
||||
Render a cube with face color corresponding to face normal. Usefull for debug/test
|
||||
|
||||
```C++
|
||||
void DrawCube(const float *view, const float *projection, float *matrix);**
|
||||
```
|
||||
|
||||
Call it when you want a gizmo
|
||||
Needs view and projection matrices.
|
||||
Matrix parameter is the source matrix (where will be gizmo be drawn) and might be transformed by the function. Return deltaMatrix is optional. snap points to a float[3] for translation and to a single float for scale or rotation. Snap angle is in Euler Degrees.
|
||||
|
||||
```C++
|
||||
enum OPERATION
|
||||
{
|
||||
TRANSLATE,
|
||||
ROTATE,
|
||||
SCALE
|
||||
};
|
||||
|
||||
enum MODE
|
||||
{
|
||||
LOCAL,
|
||||
WORLD
|
||||
};
|
||||
|
||||
void Manipulate(const float *view, const float *projection, OPERATION operation, MODE mode, float *matrix, float *deltaMatrix = 0, float *snap = 0);**
|
||||
```
|
||||
|
||||
### ImGui Example
|
||||
|
||||
Code for :
|
||||
|
||||

|
||||
|
||||
```C++
|
||||
void EditTransform(const Camera& camera, matrix_t& matrix)
|
||||
{
|
||||
static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::ROTATE);
|
||||
static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::WORLD);
|
||||
if (ImGui::IsKeyPressed(90))
|
||||
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
|
||||
if (ImGui::IsKeyPressed(69))
|
||||
mCurrentGizmoOperation = ImGuizmo::ROTATE;
|
||||
if (ImGui::IsKeyPressed(82)) // r Key
|
||||
mCurrentGizmoOperation = ImGuizmo::SCALE;
|
||||
if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
|
||||
mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE))
|
||||
mCurrentGizmoOperation = ImGuizmo::ROTATE;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE))
|
||||
mCurrentGizmoOperation = ImGuizmo::SCALE;
|
||||
float matrixTranslation[3], matrixRotation[3], matrixScale[3];
|
||||
ImGuizmo::DecomposeMatrixToComponents(matrix.m16, matrixTranslation, matrixRotation, matrixScale);
|
||||
ImGui::InputFloat3("Tr", matrixTranslation, 3);
|
||||
ImGui::InputFloat3("Rt", matrixRotation, 3);
|
||||
ImGui::InputFloat3("Sc", matrixScale, 3);
|
||||
ImGuizmo::RecomposeMatrixFromComponents(matrixTranslation, matrixRotation, matrixScale, matrix.m16);
|
||||
|
||||
if (mCurrentGizmoOperation != ImGuizmo::SCALE)
|
||||
{
|
||||
if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL))
|
||||
mCurrentGizmoMode = ImGuizmo::LOCAL;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("World", mCurrentGizmoMode == ImGuizmo::WORLD))
|
||||
mCurrentGizmoMode = ImGuizmo::WORLD;
|
||||
}
|
||||
static bool useSnap(false);
|
||||
if (ImGui::IsKeyPressed(83))
|
||||
useSnap = !useSnap;
|
||||
ImGui::Checkbox("", &useSnap);
|
||||
ImGui::SameLine();
|
||||
vec_t snap;
|
||||
switch (mCurrentGizmoOperation)
|
||||
{
|
||||
case ImGuizmo::TRANSLATE:
|
||||
snap = config.mSnapTranslation;
|
||||
ImGui::InputFloat3("Snap", &snap.x);
|
||||
break;
|
||||
case ImGuizmo::ROTATE:
|
||||
snap = config.mSnapRotation;
|
||||
ImGui::InputFloat("Angle Snap", &snap.x);
|
||||
break;
|
||||
case ImGuizmo::SCALE:
|
||||
snap = config.mSnapScale;
|
||||
ImGui::InputFloat("Scale Snap", &snap.x);
|
||||
break;
|
||||
}
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
|
||||
ImGuizmo::Manipulate(camera.mView.m16, camera.mProjection.m16, mCurrentGizmoOperation, mCurrentGizmoMode, matrix.m16, NULL, useSnap ? &snap.x : NULL);
|
||||
}
|
||||
```
|
||||
|
||||
## Install
|
||||
|
||||
ImGuizmo can be installed via [vcpkg](https://github.com/microsoft/vcpkg) and used cmake
|
||||
|
||||
```bash
|
||||
vcpkg install imguizmo
|
||||
```
|
||||
|
||||
See the [vcpkg example](/vcpkg-example) for more details
|
||||
|
||||
## License
|
||||
|
||||
ImGuizmo is licensed under the MIT License, see [LICENSE](/LICENSE) for more information.
|
||||
@@ -189,6 +189,9 @@ void AppConfig::set_defaults()
|
||||
if (get("show_gcode_window").empty())
|
||||
set_bool("show_gcode_window", true);
|
||||
|
||||
if (get("show_3d_navigator").empty())
|
||||
set_bool("show_3d_navigator", true);
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
|
||||
@@ -340,6 +340,115 @@ double ExtrusionLoop::min_mm3_per_mm() const
|
||||
return min_mm3_per_mm;
|
||||
}
|
||||
|
||||
ExtrusionLoopSloped::ExtrusionLoopSloped(ExtrusionPaths& original_paths,
|
||||
double seam_gap,
|
||||
double slope_min_length,
|
||||
double slope_max_segment_length,
|
||||
double start_slope_ratio,
|
||||
ExtrusionLoopRole role)
|
||||
: ExtrusionLoop(role)
|
||||
{
|
||||
// create slopes
|
||||
const auto add_slop = [this, slope_max_segment_length, seam_gap](const ExtrusionPath& path, const Polyline& poly,
|
||||
double ratio_begin, double ratio_end) {
|
||||
if (poly.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure `slope_max_segment_length`
|
||||
Polyline detailed_poly;
|
||||
{
|
||||
detailed_poly.append(poly.first_point());
|
||||
|
||||
// Recursively split the line into half until no longer than `slope_max_segment_length`
|
||||
const std::function<void(const Line&)> handle_line = [slope_max_segment_length, &detailed_poly, &handle_line](const Line& line) {
|
||||
if (line.length() <= slope_max_segment_length) {
|
||||
detailed_poly.append(line.b);
|
||||
} else {
|
||||
// Then process left half
|
||||
handle_line({line.a, line.midpoint()});
|
||||
// Then process right half
|
||||
handle_line({line.midpoint(), line.b});
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& l : poly.lines()) {
|
||||
handle_line(l);
|
||||
}
|
||||
}
|
||||
|
||||
starts.emplace_back(detailed_poly, path, ExtrusionPathSloped::Slope{ratio_begin, ratio_begin},
|
||||
ExtrusionPathSloped::Slope{ratio_end, ratio_end});
|
||||
|
||||
if (is_approx(ratio_end, 1.) && seam_gap > 0) {
|
||||
// Remove the segments that has no extrusion
|
||||
const auto seg_length = detailed_poly.length();
|
||||
if (seg_length > seam_gap) {
|
||||
// Split the segment and remove the last `seam_gap` bit
|
||||
const Polyline orig = detailed_poly;
|
||||
Polyline tmp;
|
||||
orig.split_at_length(seg_length - seam_gap, &detailed_poly, &tmp);
|
||||
|
||||
ratio_end = lerp(ratio_begin, ratio_end, (seg_length - seam_gap) / seg_length);
|
||||
assert(1. - ratio_end > EPSILON);
|
||||
} else {
|
||||
// Remove the entire segment
|
||||
detailed_poly.clear();
|
||||
}
|
||||
}
|
||||
if (!detailed_poly.empty()) {
|
||||
ends.emplace_back(detailed_poly, path, ExtrusionPathSloped::Slope{1., 1. - ratio_begin},
|
||||
ExtrusionPathSloped::Slope{1., 1. - ratio_end});
|
||||
}
|
||||
};
|
||||
|
||||
double remaining_length = slope_min_length;
|
||||
|
||||
ExtrusionPaths::iterator path = original_paths.begin();
|
||||
double start_ratio = start_slope_ratio;
|
||||
for (; path != original_paths.end() && remaining_length > 0; ++path) {
|
||||
const double path_len = unscale_(path->length());
|
||||
if (path_len > remaining_length) {
|
||||
// Split current path into slope and non-slope part
|
||||
Polyline slope_path;
|
||||
Polyline flat_path;
|
||||
path->polyline.split_at_length(scale_(remaining_length), &slope_path, &flat_path);
|
||||
|
||||
add_slop(*path, slope_path, start_ratio, 1);
|
||||
start_ratio = 1;
|
||||
|
||||
paths.emplace_back(std::move(flat_path), *path);
|
||||
remaining_length = 0;
|
||||
} else {
|
||||
remaining_length -= path_len;
|
||||
const double end_ratio = lerp(1.0, start_slope_ratio, remaining_length / slope_min_length);
|
||||
add_slop(*path, path->polyline, start_ratio, end_ratio);
|
||||
start_ratio = end_ratio;
|
||||
}
|
||||
}
|
||||
assert(remaining_length <= 0);
|
||||
assert(start_ratio == 1.);
|
||||
|
||||
// Put remaining flat paths
|
||||
paths.insert(paths.end(), path, original_paths.end());
|
||||
}
|
||||
|
||||
std::vector<const ExtrusionPath*> ExtrusionLoopSloped::get_all_paths() const {
|
||||
std::vector<const ExtrusionPath*> r;
|
||||
r.reserve(starts.size() + paths.size() + ends.size());
|
||||
for (const auto& p : starts) {
|
||||
r.push_back(&p);
|
||||
}
|
||||
for (const auto& p : paths) {
|
||||
r.push_back(&p);
|
||||
}
|
||||
for (const auto& p : ends) {
|
||||
r.push_back(&p);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
|
||||
{
|
||||
|
||||
@@ -301,6 +301,42 @@ private:
|
||||
bool m_no_extrusion = false;
|
||||
};
|
||||
|
||||
class ExtrusionPathSloped : public ExtrusionPath
|
||||
{
|
||||
public:
|
||||
struct Slope
|
||||
{
|
||||
double z_ratio{1.};
|
||||
double e_ratio{1.};
|
||||
};
|
||||
|
||||
Slope slope_begin;
|
||||
Slope slope_end;
|
||||
|
||||
ExtrusionPathSloped(const ExtrusionPath& rhs, const Slope& begin, const Slope& end)
|
||||
: ExtrusionPath(rhs), slope_begin(begin), slope_end(end)
|
||||
{}
|
||||
ExtrusionPathSloped(ExtrusionPath&& rhs, const Slope& begin, const Slope& end)
|
||||
: ExtrusionPath(std::move(rhs)), slope_begin(begin), slope_end(end)
|
||||
{}
|
||||
ExtrusionPathSloped(const Polyline& polyline, const ExtrusionPath& rhs, const Slope& begin, const Slope& end)
|
||||
: ExtrusionPath(polyline, rhs), slope_begin(begin), slope_end(end)
|
||||
{}
|
||||
ExtrusionPathSloped(Polyline&& polyline, const ExtrusionPath& rhs, const Slope& begin, const Slope& end)
|
||||
: ExtrusionPath(std::move(polyline), rhs), slope_begin(begin), slope_end(end)
|
||||
{}
|
||||
|
||||
Slope interpolate(const double ratio) const
|
||||
{
|
||||
return {
|
||||
lerp(slope_begin.z_ratio, slope_end.z_ratio, ratio),
|
||||
lerp(slope_begin.e_ratio, slope_end.e_ratio, ratio),
|
||||
};
|
||||
}
|
||||
|
||||
bool is_flat() const { return is_approx(slope_begin.z_ratio, slope_end.z_ratio); }
|
||||
};
|
||||
|
||||
class ExtrusionPathOriented : public ExtrusionPath
|
||||
{
|
||||
public:
|
||||
@@ -459,6 +495,22 @@ private:
|
||||
ExtrusionLoopRole m_loop_role;
|
||||
};
|
||||
|
||||
class ExtrusionLoopSloped : public ExtrusionLoop
|
||||
{
|
||||
public:
|
||||
std::vector<ExtrusionPathSloped> starts;
|
||||
std::vector<ExtrusionPathSloped> ends;
|
||||
|
||||
ExtrusionLoopSloped(ExtrusionPaths& original_paths,
|
||||
double seam_gap,
|
||||
double slope_min_length,
|
||||
double slope_max_segment_length,
|
||||
double start_slope_ratio,
|
||||
ExtrusionLoopRole role = elrDefault);
|
||||
|
||||
[[nodiscard]] std::vector<const ExtrusionPath*> get_all_paths() const;
|
||||
};
|
||||
|
||||
inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
|
||||
{
|
||||
dst.reserve(dst.size() + polylines.size());
|
||||
|
||||
@@ -210,6 +210,9 @@ static void getNamedSolids(const TopLoc_Location& location, const std::string& p
|
||||
case TopAbs_SOLID:
|
||||
namedSolids.emplace_back(TopoDS::Solid(transform.Shape()), fullName);
|
||||
break;
|
||||
case TopAbs_SHELL:
|
||||
namedSolids.emplace_back(TopoDS::Shell(transform.Shape()), fullName);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
+181
-40
@@ -1,4 +1,5 @@
|
||||
#include "BoundingBox.hpp"
|
||||
#include "Config.hpp"
|
||||
#include "Polygon.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "libslic3r.h"
|
||||
@@ -21,6 +22,7 @@
|
||||
#include "Time.hpp"
|
||||
#include "GCode/ExtrusionProcessor.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
@@ -2323,6 +2325,24 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
this->placeholder_parser().set("first_layer_print_max", new ConfigOptionFloats({bbox.max.x(), bbox.max.y()}));
|
||||
this->placeholder_parser().set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
|
||||
this->placeholder_parser().set("in_head_wrap_detect_zone",bbox_head_wrap_zone.overlap(bbox));
|
||||
|
||||
BoundingBoxf mesh_bbox(m_config.bed_mesh_min, m_config.bed_mesh_max);
|
||||
auto mesh_margin = m_config.adaptive_bed_mesh_margin.value;
|
||||
mesh_bbox.min = mesh_bbox.min.cwiseMax((bbox.min.array() - mesh_margin).matrix());
|
||||
mesh_bbox.max = mesh_bbox.max.cwiseMin((bbox.max.array() + mesh_margin).matrix());
|
||||
this->placeholder_parser().set("adaptive_bed_mesh_min", new ConfigOptionFloats({mesh_bbox.min.x(), mesh_bbox.min.y()}));
|
||||
this->placeholder_parser().set("adaptive_bed_mesh_max", new ConfigOptionFloats({mesh_bbox.max.x(), mesh_bbox.max.y()}));
|
||||
|
||||
auto probe_dist_x = std::max(1., m_config.bed_mesh_probe_distance.value.x());
|
||||
auto probe_dist_y = std::max(1., m_config.bed_mesh_probe_distance.value.y());
|
||||
int probe_count_x = std::max(3, (int) std::ceil(mesh_bbox.size().x() / probe_dist_x));
|
||||
int probe_count_y = std::max(3, (int) std::ceil(mesh_bbox.size().y() / probe_dist_y));
|
||||
this->placeholder_parser().set("bed_mesh_probe_count", new ConfigOptionInts({probe_count_x, probe_count_y}));
|
||||
auto bed_mesh_algo = "bicubic";
|
||||
if (probe_count_x < 4 || probe_count_y < 4) {
|
||||
bed_mesh_algo = "lagrange";
|
||||
}
|
||||
this->placeholder_parser().set("bed_mesh_algo", bed_mesh_algo);
|
||||
// get center without wipe tower
|
||||
BoundingBoxf bbox_wo_wt; // bounding box without wipe tower
|
||||
for (auto &objPtr : print.objects()) {
|
||||
@@ -2795,7 +2815,7 @@ void GCode::process_layers(
|
||||
const auto generator = tbb::make_filter<void, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print, &layer_to_print_idx](tbb::flow_control& fc) -> LayerResult {
|
||||
if (layer_to_print_idx >= layers_to_print.size()) {
|
||||
if ((!m_pressure_equalizer && layer_to_print_idx == layers_to_print.size()) || (m_pressure_equalizer && layer_to_print_idx == (layers_to_print.size() + 1))) {
|
||||
if (layer_to_print_idx == layers_to_print.size() + (m_pressure_equalizer ? 1 : 0)) {
|
||||
fc.stop();
|
||||
return {};
|
||||
} else {
|
||||
@@ -2891,9 +2911,16 @@ void GCode::process_layers(
|
||||
size_t layer_to_print_idx = 0;
|
||||
const auto generator = tbb::make_filter<void, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx, prime_extruder](tbb::flow_control& fc) -> LayerResult {
|
||||
if (layer_to_print_idx == layers_to_print.size()) {
|
||||
fc.stop();
|
||||
return {};
|
||||
if (layer_to_print_idx >= layers_to_print.size()) {
|
||||
if (layer_to_print_idx == layers_to_print.size() + (m_pressure_equalizer ? 1 : 0)) {
|
||||
fc.stop();
|
||||
return {};
|
||||
} else {
|
||||
// Pressure equalizer need insert empty input. Because it returns one layer back.
|
||||
// Insert NOP (no operation) layer;
|
||||
++layer_to_print_idx;
|
||||
return LayerResult::make_nop_layer_result();
|
||||
}
|
||||
} else {
|
||||
LayerToPrint &layer = layers_to_print[layer_to_print_idx ++];
|
||||
print.set_status(80, Slic3r::format(_(L("Generating G-code: layer %1%")), std::to_string(layer_to_print_idx)));
|
||||
@@ -2910,12 +2937,20 @@ void GCode::process_layers(
|
||||
}
|
||||
const auto spiral_mode = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&spiral_mode = *this->m_spiral_vase.get(), &layers_to_print](LayerResult in)->LayerResult {
|
||||
if (in.nop_layer_result)
|
||||
return in;
|
||||
spiral_mode.enable(in.spiral_vase_enable);
|
||||
bool last_layer = in.layer_id == layers_to_print.size() - 1;
|
||||
return { spiral_mode.process_layer(std::move(in.gcode), last_layer), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush };
|
||||
});
|
||||
const auto pressure_equalizer = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[pressure_equalizer = this->m_pressure_equalizer.get()](LayerResult in) -> LayerResult {
|
||||
return pressure_equalizer->process_layer(std::move(in));
|
||||
});
|
||||
const auto cooling = tbb::make_filter<LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||
[&cooling_buffer = *this->m_cooling_buffer.get()](LayerResult in)->std::string {
|
||||
if (in.nop_layer_result)
|
||||
return in.gcode;
|
||||
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
||||
});
|
||||
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
|
||||
@@ -2941,10 +2976,14 @@ void GCode::process_layers(
|
||||
});
|
||||
|
||||
// The pipeline elements are joined using const references, thus no copying is performed.
|
||||
if (m_spiral_vase)
|
||||
tbb::parallel_pipeline(12, generator & spiral_mode & cooling & fan_mover & output);
|
||||
if (m_spiral_vase && m_pressure_equalizer)
|
||||
tbb::parallel_pipeline(12, generator & spiral_mode & pressure_equalizer & cooling & fan_mover & output);
|
||||
else if (m_spiral_vase)
|
||||
tbb::parallel_pipeline(12, generator & spiral_mode & cooling & fan_mover & output);
|
||||
else if (m_pressure_equalizer)
|
||||
tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & fan_mover & output);
|
||||
else
|
||||
tbb::parallel_pipeline(12, generator & cooling & fan_mover & output);
|
||||
tbb::parallel_pipeline(12, generator & cooling & fan_mover & output);
|
||||
}
|
||||
|
||||
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)
|
||||
@@ -4495,7 +4534,6 @@ static std::unique_ptr<EdgeGrid::Grid> calculate_layer_edge_grid(const Layer& la
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, const ExtrusionEntitiesPtr& region_perimeters)
|
||||
{
|
||||
// get a copy; don't modify the orientation of the original loop object otherwise
|
||||
@@ -4518,11 +4556,17 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||
} else
|
||||
loop.split_at(last_pos, false);
|
||||
|
||||
const auto seam_scarf_type = m_config.seam_slope_type.value;
|
||||
const bool enable_seam_slope = ((seam_scarf_type == SeamScarfType::External && !is_hole) || seam_scarf_type == SeamScarfType::All) &&
|
||||
!m_config.spiral_mode &&
|
||||
(loop.role() == erExternalPerimeter || (loop.role() == erPerimeter && m_config.seam_slope_inner_walls)) &&
|
||||
layer_id() > 0;
|
||||
|
||||
// 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
|
||||
// we discard it in that case
|
||||
double clip_length = m_enable_loop_clipping ?
|
||||
scale_(m_config.seam_gap.get_abs_value(EXTRUDER_CONFIG(nozzle_diameter))) : 0;
|
||||
const double seam_gap = scale_(m_config.seam_gap.get_abs_value(EXTRUDER_CONFIG(nozzle_diameter)));
|
||||
const double clip_length = m_enable_loop_clipping && !enable_seam_slope ? seam_gap : 0;
|
||||
|
||||
// get paths
|
||||
ExtrusionPaths paths;
|
||||
@@ -4611,15 +4655,54 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool is_small_peri = false;
|
||||
for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) {
|
||||
// description += ExtrusionLoop::role_to_string(loop.loop_role());
|
||||
// description += ExtrusionEntity::role_to_string(path->role);
|
||||
|
||||
const auto speed_for_path = [&speed, &small_peri_speed](const ExtrusionPath& path) {
|
||||
// don't apply small perimeter setting for overhangs/bridges/non-perimeters
|
||||
is_small_peri = is_perimeter(path->role()) && !is_bridge(path->role()) && small_peri_speed > 0 && (path->get_overhang_degree() == 0 || path->get_overhang_degree() > 5);
|
||||
gcode += this->_extrude(*path, description, is_small_peri ? small_peri_speed : speed);
|
||||
const bool is_small_peri = is_perimeter(path.role()) && !is_bridge(path.role()) && small_peri_speed > 0 && (path.get_overhang_degree() == 0 || path.get_overhang_degree() > 5);
|
||||
return is_small_peri ? small_peri_speed : speed;
|
||||
};
|
||||
|
||||
if (!enable_seam_slope) {
|
||||
for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) {
|
||||
gcode += this->_extrude(*path, description, speed_for_path(*path));
|
||||
}
|
||||
} else {
|
||||
// Create seam slope
|
||||
double start_slope_ratio;
|
||||
if (m_config.seam_slope_start_height.percent) {
|
||||
start_slope_ratio = m_config.seam_slope_start_height.value / 100.;
|
||||
} else {
|
||||
// Get the ratio against current layer height
|
||||
double h = paths.front().height;
|
||||
start_slope_ratio = m_config.seam_slope_start_height.value / h;
|
||||
}
|
||||
|
||||
double loop_length = 0.;
|
||||
for (const auto & path : paths) {
|
||||
loop_length += unscale_(path.length());
|
||||
}
|
||||
|
||||
const bool slope_entire_loop = m_config.seam_slope_entire_loop;
|
||||
const double slope_min_length = slope_entire_loop ? loop_length : std::min(m_config.seam_slope_min_length.value, loop_length);
|
||||
const int slope_steps = m_config.seam_slope_steps;
|
||||
const double slope_max_segment_length = scale_(slope_min_length / slope_steps);
|
||||
|
||||
// Calculate the sloped loop
|
||||
ExtrusionLoopSloped new_loop(paths, seam_gap, slope_min_length, slope_max_segment_length, start_slope_ratio, loop.loop_role());
|
||||
|
||||
// Then extrude it
|
||||
for (const auto& p : new_loop.get_all_paths()) {
|
||||
gcode += this->_extrude(*p, description, speed_for_path(*p));
|
||||
}
|
||||
|
||||
// Fix path for wipe
|
||||
if (!new_loop.ends.empty()) {
|
||||
paths.clear();
|
||||
// The start slope part is ignored as it overlaps with the end part
|
||||
paths.reserve(new_loop.paths.size() + new_loop.ends.size());
|
||||
paths.insert(paths.end(), new_loop.paths.begin(), new_loop.paths.end());
|
||||
paths.insert(paths.end(), new_loop.ends.begin(), new_loop.ends.end());
|
||||
}
|
||||
}
|
||||
|
||||
// BBS
|
||||
@@ -4893,14 +4976,22 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
if (is_bridge(path.role()))
|
||||
description += " (bridge)";
|
||||
|
||||
const ExtrusionPathSloped* sloped = dynamic_cast<const ExtrusionPathSloped*>(&path);
|
||||
|
||||
const auto get_sloped_z = [&sloped, this](double z_ratio) {
|
||||
const auto height = sloped->height;
|
||||
return lerp(m_nominal_z - height, m_nominal_z, z_ratio);
|
||||
};
|
||||
|
||||
// go to first point of extrusion path
|
||||
//BBS: path.first_point is 2D point. But in lazy raise case, lift z is done in travel_to function.
|
||||
//Add m_need_change_layer_lift_z when change_layer in case of no lift if m_last_pos is equal to path.first_point() by chance
|
||||
if (!m_last_pos_defined || m_last_pos != path.first_point() || m_need_change_layer_lift_z) {
|
||||
if (!m_last_pos_defined || m_last_pos != path.first_point() || m_need_change_layer_lift_z || (sloped != nullptr && !sloped->is_flat())) {
|
||||
gcode += this->travel_to(
|
||||
path.first_point(),
|
||||
path.role(),
|
||||
"move to first " + description + " point"
|
||||
"move to first " + description + " point",
|
||||
sloped == nullptr ? DBL_MAX : get_sloped_z(sloped->slope_begin.z_ratio)
|
||||
);
|
||||
m_need_change_layer_lift_z = false;
|
||||
}
|
||||
@@ -5251,7 +5342,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
if (!variable_speed) {
|
||||
// F is mm per minute.
|
||||
gcode += m_writer.set_speed(F, "", comment);
|
||||
double path_length = 0.;
|
||||
{
|
||||
if (m_enable_cooling_markers) {
|
||||
if (enable_overhang_bridge_fan) {
|
||||
@@ -5284,9 +5374,11 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
}
|
||||
}
|
||||
}
|
||||
// BBS: use G1 if not enable arc fitting or has no arc fitting result or in spiral_mode mode
|
||||
// BBS: use G1 if not enable arc fitting or has no arc fitting result or in spiral_mode mode or we are doing sloped extrusion
|
||||
// Attention: G2 and G3 is not supported in spiral_mode mode
|
||||
if (!m_config.enable_arc_fitting || path.polyline.fitting_result.empty() || m_config.spiral_mode) {
|
||||
if (!m_config.enable_arc_fitting || path.polyline.fitting_result.empty() || m_config.spiral_mode || sloped != nullptr) {
|
||||
double path_length = 0.;
|
||||
double total_length = sloped == nullptr ? 0. : path.polyline.length() * SCALING_FACTOR;
|
||||
for (const Line& line : path.polyline.lines()) {
|
||||
std::string tempDescription = description;
|
||||
const double line_length = line.length() * SCALING_FACTOR;
|
||||
@@ -5300,10 +5392,22 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
|
||||
}
|
||||
}
|
||||
gcode += m_writer.extrude_to_xy(
|
||||
this->point_to_gcode(line.b),
|
||||
dE,
|
||||
GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion());
|
||||
if (sloped == nullptr) {
|
||||
// Normal extrusion
|
||||
gcode += m_writer.extrude_to_xy(
|
||||
this->point_to_gcode(line.b),
|
||||
dE,
|
||||
GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion());
|
||||
} else {
|
||||
// Sloped extrusion
|
||||
const auto [z_ratio, e_ratio] = sloped->interpolate(path_length / total_length);
|
||||
Vec2d dest2d = this->point_to_gcode(line.b);
|
||||
Vec3d dest3d(dest2d(0), dest2d(1), get_sloped_z(z_ratio));
|
||||
gcode += m_writer.extrude_to_xyz(
|
||||
dest3d,
|
||||
dE * e_ratio,
|
||||
GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// BBS: start to generate gcode from arc fitting data which includes line and arc
|
||||
@@ -5317,7 +5421,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
for (size_t point_index = start_index + 1; point_index < end_index + 1; point_index++) {
|
||||
const Line line = Line(path.polyline.points[point_index - 1], path.polyline.points[point_index]);
|
||||
const double line_length = line.length() * SCALING_FACTOR;
|
||||
path_length += line_length;
|
||||
auto dE = e_per_mm * line_length;
|
||||
if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) {
|
||||
auto oldE = dE;
|
||||
@@ -5339,7 +5442,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
const ArcSegment& arc = fitting_result[fitting_index].arc_data;
|
||||
const double arc_length = fitting_result[fitting_index].arc_data.length * SCALING_FACTOR;
|
||||
const Vec2d center_offset = this->point_to_gcode(arc.center) - this->point_to_gcode(arc.start_point);
|
||||
path_length += arc_length;
|
||||
auto dE = e_per_mm * arc_length;
|
||||
if (m_small_area_infill_flow_compensator && m_config.small_area_infill_flow_compensation.value) {
|
||||
auto oldE = dE;
|
||||
@@ -5368,6 +5470,15 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
} else {
|
||||
double last_set_speed = new_points[0].speed * 60.0;
|
||||
|
||||
double total_length = 0;
|
||||
if (sloped != nullptr) {
|
||||
// Calculate total extrusion length
|
||||
Points p;
|
||||
p.reserve(new_points.size());
|
||||
std::transform(new_points.begin(), new_points.end(), std::back_inserter(p), [](const ProcessedPoint& pp) { return pp.p; });
|
||||
Polyline l(p);
|
||||
total_length = l.length() * SCALING_FACTOR;
|
||||
}
|
||||
gcode += m_writer.set_speed(last_set_speed, "", comment);
|
||||
Vec2d prev = this->point_to_gcode_quantized(new_points[0].p);
|
||||
bool pre_fan_enabled = false;
|
||||
@@ -5375,6 +5486,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
if( m_enable_cooling_markers && enable_overhang_bridge_fan)
|
||||
pre_fan_enabled = check_overhang_fan(new_points[0].overlap, path.role());
|
||||
|
||||
double path_length = 0.;
|
||||
for (size_t i = 1; i < new_points.size(); i++) {
|
||||
std::string tempDescription = description;
|
||||
const ProcessedPoint &processed_point = new_points[i];
|
||||
@@ -5410,6 +5522,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
}
|
||||
|
||||
const double line_length = (p - prev).norm();
|
||||
path_length += line_length;
|
||||
double new_speed = pre_processed_point.speed * 60.0;
|
||||
if (last_set_speed != new_speed) {
|
||||
gcode += m_writer.set_speed(new_speed, "", comment);
|
||||
@@ -5424,8 +5537,15 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
|
||||
}
|
||||
}
|
||||
gcode +=
|
||||
m_writer.extrude_to_xy(p, dE, GCodeWriter::full_gcode_comment ? tempDescription : "");
|
||||
if (sloped == nullptr) {
|
||||
// Normal extrusion
|
||||
gcode += m_writer.extrude_to_xy(p, dE, GCodeWriter::full_gcode_comment ? tempDescription : "");
|
||||
} else {
|
||||
// Sloped extrusion
|
||||
const auto [z_ratio, e_ratio] = sloped->interpolate(path_length / total_length);
|
||||
Vec3d dest3d(p(0), p(1), get_sloped_z(z_ratio));
|
||||
gcode += m_writer.extrude_to_xyz(dest3d, dE * e_ratio, GCodeWriter::full_gcode_comment ? tempDescription : "");
|
||||
}
|
||||
|
||||
prev = p;
|
||||
|
||||
@@ -5504,7 +5624,7 @@ std::string GCode::_encode_label_ids_to_base64(std::vector<size_t> ids)
|
||||
}
|
||||
|
||||
// This method accepts &point in print coordinates.
|
||||
std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment)
|
||||
std::string GCode::travel_to(const Point& point, ExtrusionRole role, std::string comment, double z/* = DBL_MAX*/)
|
||||
{
|
||||
/* Define the travel move as a line between current position and the taget point.
|
||||
This is expressed in print coordinates, so it will need to be translated by
|
||||
@@ -5589,15 +5709,36 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
|
||||
|
||||
// use G1 because we rely on paths being straight (G0 may make round paths)
|
||||
if (travel.size() >= 2) {
|
||||
for (size_t i = 1; i < travel.size(); ++ i) {
|
||||
// BBS. Process lazy layer change, but don't do lazy layer change when enable spiral vase
|
||||
Vec3d curr_pos = m_writer.get_position();
|
||||
if (i == 1 && !m_spiral_vase) {
|
||||
Vec2d dest2d = this->point_to_gcode(travel.points[i]);
|
||||
Vec3d dest3d(dest2d(0), dest2d(1), m_nominal_z);
|
||||
gcode += m_writer.travel_to_xyz(dest3d, comment+" travel_to_xyz");
|
||||
if (m_spiral_vase) {
|
||||
// No lazy z lift for spiral vase mode
|
||||
for (size_t i = 1; i < travel.size(); ++i) {
|
||||
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment + " travel_to_xy");
|
||||
}
|
||||
} else {
|
||||
if (travel.size() == 2) {
|
||||
// No extra movements emitted by avoid_crossing_perimeters, simply move to the end point with z change
|
||||
const auto& dest2d = this->point_to_gcode(travel.points.back());
|
||||
Vec3d dest3d(dest2d(0), dest2d(1), z == DBL_MAX ? m_nominal_z : z);
|
||||
gcode += m_writer.travel_to_xyz(dest3d, comment + " travel_to_xyz");
|
||||
} else {
|
||||
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment+" travel_to_xy");
|
||||
// Extra movements emitted by avoid_crossing_perimeters, lift the z to normal height at the beginning, then apply the z
|
||||
// ratio at the last point
|
||||
for (size_t i = 1; i < travel.size(); ++i) {
|
||||
if (i == 1) {
|
||||
// Lift to normal z at beginning
|
||||
Vec2d dest2d = this->point_to_gcode(travel.points[i]);
|
||||
Vec3d dest3d(dest2d(0), dest2d(1), m_nominal_z);
|
||||
gcode += m_writer.travel_to_xyz(dest3d, comment + " travel_to_xyz");
|
||||
} else if (z != DBL_MAX && i == travel.size() - 1) {
|
||||
// Apply z_ratio for the very last point
|
||||
Vec2d dest2d = this->point_to_gcode(travel.points[i]);
|
||||
Vec3d dest3d(dest2d(0), dest2d(1), z);
|
||||
gcode += m_writer.travel_to_xyz(dest3d, comment + " travel_to_xyz");
|
||||
} else {
|
||||
// For all points in between, no z change
|
||||
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment + " travel_to_xy");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this->set_last_pos(travel.points.back());
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <cfloat>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@@ -219,7 +220,7 @@ public:
|
||||
void set_layer_count(unsigned int value) { m_layer_count = value; }
|
||||
void apply_print_config(const PrintConfig &print_config);
|
||||
|
||||
std::string travel_to(const Point& point, ExtrusionRole role, std::string comment);
|
||||
std::string travel_to(const Point& point, ExtrusionRole role, std::string comment, double z = DBL_MAX);
|
||||
bool needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type);
|
||||
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift);
|
||||
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
|
||||
|
||||
@@ -1080,7 +1080,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
||||
|
||||
const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_mode");
|
||||
if (spiral_vase != nullptr)
|
||||
m_spiral_vase_active = spiral_vase->value;
|
||||
m_detect_layer_based_on_tag = spiral_vase->value;
|
||||
|
||||
const ConfigOptionBool* has_scarf_joint_seam = config.option<ConfigOptionBool>("has_scarf_joint_seam");
|
||||
if (has_scarf_joint_seam != nullptr)
|
||||
m_detect_layer_based_on_tag = m_detect_layer_based_on_tag || has_scarf_joint_seam->value;
|
||||
|
||||
const ConfigOptionBool* manual_filament_change = config.option<ConfigOptionBool>("manual_filament_change");
|
||||
if (manual_filament_change != nullptr)
|
||||
@@ -1397,7 +1401,11 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
||||
|
||||
const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_mode");
|
||||
if (spiral_vase != nullptr)
|
||||
m_spiral_vase_active = spiral_vase->value;
|
||||
m_detect_layer_based_on_tag = spiral_vase->value;
|
||||
|
||||
const ConfigOptionBool* has_scarf_joint_seam = config.option<ConfigOptionBool>("has_scarf_joint_seam");
|
||||
if (has_scarf_joint_seam != nullptr)
|
||||
m_detect_layer_based_on_tag = m_detect_layer_based_on_tag || has_scarf_joint_seam->value;
|
||||
|
||||
const ConfigOptionEnumGeneric *bed_type = config.option<ConfigOptionEnumGeneric>("curr_bed_type");
|
||||
if (bed_type != nullptr)
|
||||
@@ -1479,7 +1487,9 @@ void GCodeProcessor::reset()
|
||||
|
||||
m_options_z_corrector.reset();
|
||||
|
||||
m_spiral_vase_active = false;
|
||||
m_detect_layer_based_on_tag = false;
|
||||
|
||||
m_seams_count = 0;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_mm3_per_mm_compare.reset();
|
||||
@@ -2344,12 +2354,12 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
|
||||
// layer change tag
|
||||
if (comment == reserved_tag(ETags::Layer_Change)) {
|
||||
++m_layer_id;
|
||||
if (m_spiral_vase_active) {
|
||||
if (m_detect_layer_based_on_tag) {
|
||||
if (m_result.moves.empty() || m_result.spiral_vase_layers.empty())
|
||||
// add a placeholder for layer height. the actual value will be set inside process_G1() method
|
||||
m_result.spiral_vase_layers.push_back({ FLT_MAX, { 0, 0 } });
|
||||
else {
|
||||
const size_t move_id = m_result.moves.size() - 1;
|
||||
const size_t move_id = m_result.moves.size() - 1 - m_seams_count;
|
||||
if (!m_result.spiral_vase_layers.empty())
|
||||
m_result.spiral_vase_layers.back().second.second = move_id;
|
||||
// add a placeholder for layer height. the actual value will be set inside process_G1() method
|
||||
@@ -3215,12 +3225,22 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||
machine.calculate_time(TimeProcessor::Planner::queue_size);
|
||||
}
|
||||
|
||||
const Vec3f plate_offset = {(float) m_x_offset, (float) m_y_offset, 0.0f};
|
||||
|
||||
if (m_seams_detector.is_active()) {
|
||||
// check for seam starting vertex
|
||||
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && !m_seams_detector.has_first_vertex()) {
|
||||
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
|
||||
//BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position
|
||||
const Vec3f real_first_pos = Vec3f(m_result.moves.back().position.x() - m_x_offset, m_result.moves.back().position.y() - m_y_offset, m_result.moves.back().position.z());
|
||||
m_seams_detector.set_first_vertex(real_first_pos - m_extruder_offsets[m_extruder_id]);
|
||||
const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
|
||||
if (!m_seams_detector.has_first_vertex()) {
|
||||
m_seams_detector.set_first_vertex(new_pos);
|
||||
} else if (m_detect_layer_based_on_tag) {
|
||||
// We may have sloped loop, drop any previous start pos if we have z increment
|
||||
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex();
|
||||
if (new_pos.z() > first_vertex->z()) {
|
||||
m_seams_detector.set_first_vertex(new_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
// check for seam ending vertex and store the resulting move
|
||||
else if ((type != EMoveType::Extrude || (m_extrusion_role != erExternalPerimeter && m_extrusion_role != erOverhangPerimeter)) && m_seams_detector.has_first_vertex()) {
|
||||
@@ -3230,8 +3250,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||
|
||||
const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]);
|
||||
//BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position
|
||||
const Vec3f real_last_pos = Vec3f(m_result.moves.back().position.x() - m_x_offset, m_result.moves.back().position.y() - m_y_offset, m_result.moves.back().position.z());
|
||||
const Vec3f new_pos = real_last_pos - m_extruder_offsets[m_extruder_id];
|
||||
const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
|
||||
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex();
|
||||
// the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later
|
||||
|
||||
@@ -3246,16 +3265,21 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||
}
|
||||
else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
|
||||
m_seams_detector.activate(true);
|
||||
Vec3f plate_offset = {(float) m_x_offset, (float) m_y_offset, 0.0f};
|
||||
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset);
|
||||
}
|
||||
|
||||
if (m_spiral_vase_active && !m_result.spiral_vase_layers.empty()) {
|
||||
if (m_result.spiral_vase_layers.back().first == FLT_MAX && delta_pos[Z] >= 0.0)
|
||||
if (m_detect_layer_based_on_tag && !m_result.spiral_vase_layers.empty()) {
|
||||
if (delta_pos[Z] >= 0.0 && type == EMoveType::Extrude) {
|
||||
const float current_z = static_cast<float>(m_end_position[Z]);
|
||||
// replace layer height placeholder with correct value
|
||||
m_result.spiral_vase_layers.back().first = static_cast<float>(m_end_position[Z]);
|
||||
if (m_result.spiral_vase_layers.back().first == FLT_MAX) {
|
||||
m_result.spiral_vase_layers.back().first = current_z;
|
||||
} else {
|
||||
m_result.spiral_vase_layers.back().first = std::max(m_result.spiral_vase_layers.back().first, current_z);
|
||||
}
|
||||
}
|
||||
if (!m_result.moves.empty())
|
||||
m_result.spiral_vase_layers.back().second.second = m_result.moves.size() - 1;
|
||||
m_result.spiral_vase_layers.back().second.second = m_result.moves.size() - 1 - m_seams_count;
|
||||
}
|
||||
|
||||
// store move
|
||||
@@ -3637,8 +3661,17 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
|
||||
|
||||
if (m_seams_detector.is_active()) {
|
||||
//BBS: check for seam starting vertex
|
||||
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && !m_seams_detector.has_first_vertex()) {
|
||||
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset);
|
||||
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
|
||||
const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
|
||||
if (!m_seams_detector.has_first_vertex()) {
|
||||
m_seams_detector.set_first_vertex(new_pos);
|
||||
} else if (m_detect_layer_based_on_tag) {
|
||||
// We may have sloped loop, drop any previous start pos if we have z increment
|
||||
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex();
|
||||
if (new_pos.z() > first_vertex->z()) {
|
||||
m_seams_detector.set_first_vertex(new_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
//BBS: check for seam ending vertex and store the resulting move
|
||||
else if ((type != EMoveType::Extrude || (m_extrusion_role != erExternalPerimeter && m_extrusion_role != erOverhangPerimeter)) && m_seams_detector.has_first_vertex()) {
|
||||
@@ -4267,6 +4300,10 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
|
||||
m_interpolation_points,
|
||||
});
|
||||
|
||||
if (type == EMoveType::Seam) {
|
||||
m_seams_count++;
|
||||
}
|
||||
|
||||
// stores stop time placeholders for later use
|
||||
if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
|
||||
@@ -700,7 +700,8 @@ namespace Slic3r {
|
||||
SeamsDetector m_seams_detector;
|
||||
OptionsZCorrector m_options_z_corrector;
|
||||
size_t m_last_default_color_id;
|
||||
bool m_spiral_vase_active;
|
||||
bool m_detect_layer_based_on_tag {false};
|
||||
int m_seams_count;
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
@@ -770,6 +771,10 @@ namespace Slic3r {
|
||||
//BBS: set offset for gcode writer
|
||||
void set_xy_offset(double x, double y) { m_x_offset = x; m_y_offset = y; }
|
||||
|
||||
// Orca: if true, only change new layer if ETags::Layer_Change occurs
|
||||
// otherwise when we got a lift of z during extrusion, a new layer will be added
|
||||
void detect_layer_based_on_tag(bool enabled) { m_detect_layer_based_on_tag = enabled; }
|
||||
|
||||
private:
|
||||
void apply_config(const DynamicPrintConfig& config);
|
||||
void apply_config_simplify3d(const std::string& filename);
|
||||
|
||||
@@ -633,7 +633,7 @@ void PressureEqualizer::adjust_volumetric_rate(const size_t fist_line_idx, const
|
||||
}
|
||||
|
||||
if (line.adjustable_flow) {
|
||||
float rate_start = rate_end + rate_slope * line.time_corrected();
|
||||
float rate_start = sqrt(rate_end * rate_end + 2 * line.volumetric_extrusion_rate * line.dist_xyz() * rate_slope / line.feedrate());
|
||||
if (rate_start < line.volumetric_extrusion_rate_start) {
|
||||
// Limit the volumetric extrusion rate at the start of this segment due to a segment
|
||||
// of ExtrusionType iRole, which will be extruded in the future.
|
||||
@@ -690,7 +690,7 @@ void PressureEqualizer::adjust_volumetric_rate(const size_t fist_line_idx, const
|
||||
}
|
||||
|
||||
if (line.adjustable_flow) {
|
||||
float rate_end = rate_start + rate_slope * line.time_corrected();
|
||||
float rate_end = sqrt(rate_start * rate_start + 2 * line.volumetric_extrusion_rate * line.dist_xyz() * rate_slope / line.feedrate());
|
||||
if (rate_end < line.volumetric_extrusion_rate_end) {
|
||||
// Limit the volumetric extrusion rate at the start of this segment due to a segment
|
||||
// of ExtrusionType iRole, which was extruded before.
|
||||
|
||||
@@ -176,6 +176,7 @@ void Layer::make_perimeters()
|
||||
&& config.detect_overhang_wall == other_config.detect_overhang_wall
|
||||
&& config.overhang_reverse == other_config.overhang_reverse
|
||||
&& config.overhang_reverse_threshold == other_config.overhang_reverse_threshold
|
||||
&& config.wall_direction == other_config.wall_direction
|
||||
&& config.opt_serialize("inner_wall_line_width") == other_config.opt_serialize("inner_wall_line_width")
|
||||
&& config.opt_serialize("outer_wall_line_width") == other_config.opt_serialize("outer_wall_line_width")
|
||||
&& config.detect_thin_wall == other_config.detect_thin_wall
|
||||
@@ -183,7 +184,13 @@ void Layer::make_perimeters()
|
||||
&& config.fuzzy_skin == other_config.fuzzy_skin
|
||||
&& config.fuzzy_skin_thickness == other_config.fuzzy_skin_thickness
|
||||
&& config.fuzzy_skin_point_distance == other_config.fuzzy_skin_point_distance
|
||||
&& config.fuzzy_skin_first_layer == other_config.fuzzy_skin_first_layer)
|
||||
&& config.fuzzy_skin_first_layer == other_config.fuzzy_skin_first_layer
|
||||
&& config.seam_slope_type == other_config.seam_slope_type
|
||||
&& config.seam_slope_start_height == other_config.seam_slope_start_height
|
||||
&& config.seam_slope_entire_loop == other_config.seam_slope_entire_loop
|
||||
&& config.seam_slope_min_length == other_config.seam_slope_min_length
|
||||
&& config.seam_slope_steps == other_config.seam_slope_steps
|
||||
&& config.seam_slope_inner_walls == other_config.seam_slope_inner_walls)
|
||||
{
|
||||
other_layerm->perimeters.clear();
|
||||
other_layerm->fills.clear();
|
||||
|
||||
@@ -40,7 +40,6 @@ public:
|
||||
// Polygon of this contour.
|
||||
Polygon polygon;
|
||||
// Is it a contour or a hole?
|
||||
// Contours are CCW oriented, holes are CW oriented.
|
||||
bool is_contour;
|
||||
// BBS: is perimeter using smaller width
|
||||
bool is_smaller_width_perimeter;
|
||||
@@ -899,18 +898,16 @@ void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExP
|
||||
else
|
||||
offset_top_surface = 0;
|
||||
// don't takes into account too thin areas
|
||||
// skip if the exposed area is smaller than "min_width_top_surface"
|
||||
double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), scale_(config->min_width_top_surface.get_abs_value(unscale_(perimeter_width))));
|
||||
|
||||
Polygons grown_upper_slices = offset(*this->upper_slices, min_width_top_surface);
|
||||
|
||||
// get boungding box of last
|
||||
BoundingBox last_box = get_extents(orig_polygons);
|
||||
last_box.offset(SCALED_EPSILON);
|
||||
|
||||
// skip if the exposed area is smaller than "min_width_top_surface"
|
||||
double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2. + 10), scale_(config->min_width_top_surface.get_abs_value(unscale_(perimeter_width))));
|
||||
|
||||
// get the Polygons upper the polygon this layer
|
||||
Polygons upper_polygons_series_clipped =
|
||||
ClipperUtils::clip_clipper_polygons_with_subject_bbox(grown_upper_slices, last_box);
|
||||
Polygons upper_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->upper_slices, last_box);
|
||||
upper_polygons_series_clipped = offset(upper_polygons_series_clipped, min_width_top_surface);
|
||||
|
||||
// set the clip to a virtual "second perimeter"
|
||||
fill_clip = offset_ex(orig_polygons, -double(ext_perimeter_spacing));
|
||||
@@ -928,7 +925,7 @@ void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExP
|
||||
const float bridge_margin =
|
||||
std::min(float(scale_(BRIDGE_INFILL_MARGIN)), float(scale_(nozzle_diameter * BRIDGE_INFILL_MARGIN / 0.4)));
|
||||
bridge_checker = offset_ex(diff_ex(orig_polygons, lower_polygons_series_clipped, ApplySafetyOffset::Yes),
|
||||
1.5 * bridge_offset + bridge_margin + perimeter_spacing / 2);
|
||||
1.5 * bridge_offset + bridge_margin + perimeter_spacing / 2.);
|
||||
}
|
||||
ExPolygons delete_bridge = diff_ex(orig_polygons, bridge_checker, ApplySafetyOffset::Yes);
|
||||
|
||||
@@ -938,7 +935,7 @@ void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExP
|
||||
ExPolygons temp_gap = diff_ex(top_polygons, fill_clip);
|
||||
ExPolygons inner_polygons =
|
||||
diff_ex(orig_polygons,
|
||||
offset_ex(top_polygons, offset_top_surface + min_width_top_surface - double(ext_perimeter_spacing / 2)),
|
||||
offset_ex(top_polygons, offset_top_surface + min_width_top_surface - double(ext_perimeter_spacing / 2.)),
|
||||
ApplySafetyOffset::Yes);
|
||||
// get the enlarged top surface, by using inner_polygons instead of upper_slices, and clip it for it to be exactly
|
||||
// the polygons to fill.
|
||||
@@ -948,7 +945,7 @@ void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExP
|
||||
//set the clip to the external wall but go back inside by infill_extrusion_width/2 to be sure the extrusion won't go outside even with a 100% overlap.
|
||||
double infill_spacing_unscaled = this->config->sparse_infill_line_width.get_abs_value(nozzle_diameter);
|
||||
if (infill_spacing_unscaled == 0) infill_spacing_unscaled = Flow::auto_extrusion_width(frInfill, nozzle_diameter);
|
||||
fill_clip = offset_ex(orig_polygons, double(ext_perimeter_spacing / 2) - scale_(infill_spacing_unscaled / 2));
|
||||
fill_clip = offset_ex(orig_polygons, double(ext_perimeter_spacing / 2.) - scale_(infill_spacing_unscaled / 2.));
|
||||
// ExPolygons oldLast = last;
|
||||
|
||||
non_top_polygons = intersection_ex(inner_polygons, orig_polygons);
|
||||
@@ -1445,7 +1442,8 @@ void PerimeterGenerator::process_classic()
|
||||
|
||||
coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing();
|
||||
coord_t ext_perimeter_spacing2;
|
||||
if(config->precise_outer_wall)
|
||||
// Orca: ignore precise_outer_wall if wall_sequence is not InnerOuter
|
||||
if(config->precise_outer_wall && this->config->wall_sequence == WallSequence::InnerOuter)
|
||||
ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (this->ext_perimeter_flow.width() + this->perimeter_flow.width()));
|
||||
else
|
||||
ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (this->ext_perimeter_flow.spacing() + this->perimeter_flow.spacing()));
|
||||
@@ -1732,8 +1730,19 @@ void PerimeterGenerator::process_classic()
|
||||
// at this point, all loops should be in contours[0]
|
||||
bool steep_overhang_contour = false;
|
||||
bool steep_overhang_hole = false;
|
||||
const WallDirection wall_direction = config->wall_direction;
|
||||
if (wall_direction != WallDirection::Auto) {
|
||||
// Skip steep overhang detection if wall direction is specified
|
||||
steep_overhang_contour = true;
|
||||
steep_overhang_hole = true;
|
||||
}
|
||||
ExtrusionEntityCollection entities = traverse_loops(*this, contours.front(), thin_walls, steep_overhang_contour, steep_overhang_hole);
|
||||
reorient_perimeters(entities, steep_overhang_contour, steep_overhang_hole, this->config->overhang_reverse_internal_only);
|
||||
// All walls are counter-clockwise initially, so we don't need to reorient it if that's what we want
|
||||
if (wall_direction != WallDirection::CounterClockwise) {
|
||||
reorient_perimeters(entities, steep_overhang_contour, steep_overhang_hole,
|
||||
// Reverse internal only if the wall direction is auto
|
||||
this->config->overhang_reverse_internal_only && wall_direction == WallDirection::Auto);
|
||||
}
|
||||
|
||||
// if brim will be printed, reverse the order of perimeters so that
|
||||
// we continue inwards after having finished the brim
|
||||
@@ -2173,9 +2182,11 @@ void PerimeterGenerator::process_arachne()
|
||||
const bool is_topmost_layer = (this->upper_slices == nullptr) ? true : false;
|
||||
if (is_topmost_layer && loop_number > 0 && config->only_one_wall_top)
|
||||
loop_number = 0;
|
||||
|
||||
auto apply_precise_outer_wall = config->precise_outer_wall && this->config->wall_sequence == WallSequence::InnerOuter;
|
||||
// Orca: properly adjust offset for the outer wall if precise_outer_wall is enabled.
|
||||
ExPolygons last = offset_ex(surface.expolygon.simplify_p(surface_simplify_resolution),
|
||||
config->precise_outer_wall ? -float(ext_perimeter_width - ext_perimeter_spacing )
|
||||
apply_precise_outer_wall? -float(ext_perimeter_width - ext_perimeter_spacing )
|
||||
: -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
|
||||
|
||||
Arachne::WallToolPathsParams input_params = Arachne::make_paths_params(this->layer_id, *object_config, *print_config);
|
||||
@@ -2183,7 +2194,7 @@ void PerimeterGenerator::process_arachne()
|
||||
input_params.is_top_or_bottom_layer = (is_bottom_layer || is_topmost_layer) ? true : false;
|
||||
|
||||
coord_t wall_0_inset = 0;
|
||||
if (config->precise_outer_wall)
|
||||
if (apply_precise_outer_wall)
|
||||
wall_0_inset = -coord_t(ext_perimeter_width / 2 - ext_perimeter_spacing / 2);
|
||||
|
||||
std::vector<Arachne::VariableWidthLines> out_shell;
|
||||
@@ -2521,8 +2532,19 @@ void PerimeterGenerator::process_arachne()
|
||||
|
||||
bool steep_overhang_contour = false;
|
||||
bool steep_overhang_hole = false;
|
||||
const WallDirection wall_direction = config->wall_direction;
|
||||
if (wall_direction != WallDirection::Auto) {
|
||||
// Skip steep overhang detection if wall direction is specified
|
||||
steep_overhang_contour = true;
|
||||
steep_overhang_hole = true;
|
||||
}
|
||||
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions, steep_overhang_contour, steep_overhang_hole); !extrusion_coll.empty()) {
|
||||
reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole, this->config->overhang_reverse_internal_only);
|
||||
// All walls are counter-clockwise initially, so we don't need to reorient it if that's what we want
|
||||
if (wall_direction != WallDirection::CounterClockwise) {
|
||||
reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole,
|
||||
// Reverse internal only if the wall direction is auto
|
||||
this->config->overhang_reverse_internal_only && wall_direction == WallDirection::Auto);
|
||||
}
|
||||
this->loops->append(extrusion_coll);
|
||||
}
|
||||
|
||||
|
||||
@@ -309,6 +309,53 @@ bool Polyline::split_at_index(const size_t index, Polyline* p1, Polyline* p2) co
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polyline::split_at_length(const double length, Polyline* p1, Polyline* p2) const
|
||||
{
|
||||
if (this->points.empty()) return false;
|
||||
if (length < 0 || length > this->length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (length < SCALED_EPSILON) {
|
||||
p1->clear();
|
||||
p1->append(this->first_point());
|
||||
*p2 = *this;
|
||||
} else if (is_approx(length, this->length(), SCALED_EPSILON)) {
|
||||
p2->clear();
|
||||
p2->append(this->last_point());
|
||||
*p1 = *this;
|
||||
} else {
|
||||
// 1 find the line to split at
|
||||
size_t line_idx = 0;
|
||||
double acc_length = 0;
|
||||
Point p = this->first_point();
|
||||
for (const auto& l : this->lines()) {
|
||||
p = l.b;
|
||||
|
||||
const double current_length = l.length();
|
||||
if (acc_length + current_length >= length) {
|
||||
p = lerp(l.a, l.b, (length - acc_length) / current_length);
|
||||
break;
|
||||
}
|
||||
acc_length += current_length;
|
||||
line_idx++;
|
||||
}
|
||||
|
||||
//2 judge whether the cloest point is one vertex of polyline.
|
||||
// and spilit the polyline at different index
|
||||
int index = this->find_point(p);
|
||||
if (index != -1) {
|
||||
this->split_at_index(index, p1, p2);
|
||||
} else {
|
||||
Polyline temp;
|
||||
this->split_at_index(line_idx, p1, &temp);
|
||||
p1->append(p);
|
||||
this->split_at_index(line_idx + 1, &temp, p2);
|
||||
p2->append_before(p);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Polyline::is_straight() const
|
||||
{
|
||||
|
||||
@@ -130,6 +130,7 @@ public:
|
||||
// template <class T> void simplify_by_visibility(const T &area);
|
||||
void split_at(Point &point, Polyline* p1, Polyline* p2) const;
|
||||
bool split_at_index(const size_t index, Polyline* p1, Polyline* p2) const;
|
||||
bool split_at_length(const double length, Polyline* p1, Polyline* p2) const;
|
||||
|
||||
bool is_straight() const;
|
||||
bool is_closed() const { return this->points.front() == this->points.back(); }
|
||||
|
||||
+32
-14
@@ -770,7 +770,7 @@ bool Preset::has_cali_lines(PresetBundle* preset_bundle)
|
||||
static std::vector<std::string> s_Preset_print_options {
|
||||
"layer_height", "initial_layer_print_height", "wall_loops", "alternate_extra_wall", "slice_closing_radius", "spiral_mode", "spiral_mode_smooth", "spiral_mode_max_xy_smoothing", "slicing_mode",
|
||||
"top_shell_layers", "top_shell_thickness", "bottom_shell_layers", "bottom_shell_thickness",
|
||||
"extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness","reduce_wall_solid_infill", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold","overhang_reverse_internal_only",
|
||||
"extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness","reduce_wall_solid_infill", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold","overhang_reverse_internal_only", "wall_direction",
|
||||
"seam_position", "staggered_inner_seams", "wall_sequence", "is_infill_first", "sparse_infill_density", "sparse_infill_pattern", "top_surface_pattern", "bottom_surface_pattern",
|
||||
"infill_direction", "counterbole_hole_bridging",
|
||||
"minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern","gap_fill_target",
|
||||
@@ -820,6 +820,7 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
"wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic",
|
||||
"hole_to_polyhole", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth",
|
||||
"small_area_infill_flow_compensation", "small_area_infill_flow_compensation_model",
|
||||
"seam_slope_type", "seam_slope_start_height", "seam_slope_entire_loop", "seam_slope_min_length", "seam_slope_steps", "seam_slope_inner_walls",
|
||||
};
|
||||
|
||||
static std::vector<std::string> s_Preset_filament_options {
|
||||
@@ -881,7 +882,7 @@ static std::vector<std::string> s_Preset_printer_options {
|
||||
"cooling_tube_retraction",
|
||||
"cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "purge_in_prime_tower", "enable_filament_ramming",
|
||||
"z_offset",
|
||||
"disable_m73", "preferred_orientation", "emit_machine_limits_to_gcode", "support_multi_bed_types"
|
||||
"disable_m73", "preferred_orientation", "emit_machine_limits_to_gcode", "support_multi_bed_types","bed_mesh_min","bed_mesh_max","bed_mesh_probe_distance", "adaptive_bed_mesh_margin"
|
||||
};
|
||||
|
||||
static std::vector<std::string> s_Preset_sla_print_options {
|
||||
@@ -1122,6 +1123,7 @@ void PresetCollection::load_presets(
|
||||
if (fs::exists(file_path))
|
||||
fs::remove(file_path);
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("parse config %1% failed")%preset.file;
|
||||
++m_errors;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1159,6 +1161,7 @@ void PresetCollection::load_presets(
|
||||
auto inherits_config2 = dynamic_cast<ConfigOptionString *>(inherits_config);
|
||||
if ((inherits_config2 && !inherits_config2->value.empty()) && !preset.is_custom_defined()) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("can not find parent for config %1%!")%preset.file;
|
||||
++m_errors;
|
||||
continue;
|
||||
}
|
||||
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
|
||||
@@ -1169,9 +1172,12 @@ void PresetCollection::load_presets(
|
||||
Preset::normalize(preset.config);
|
||||
// Report configuration fields, which are misplaced into a wrong group.
|
||||
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 (!incorrect_keys.empty()) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "Error in a preset file: The preset \"" << preset.file
|
||||
<< "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
|
||||
}
|
||||
preset.loaded = true;
|
||||
//BBS: add some workaround for previous incorrect settings
|
||||
if ((!preset.setting_id.empty())&&(preset.setting_id == preset.base_id))
|
||||
@@ -1179,6 +1185,7 @@ void PresetCollection::load_presets(
|
||||
//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;
|
||||
} catch (const std::ifstream::failure &err) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("The user-config cannot be loaded: %1%. Reason: %2%")%preset.file %err.what();
|
||||
fs::path file_path(preset.file);
|
||||
if (fs::exists(file_path))
|
||||
@@ -1188,6 +1195,7 @@ void PresetCollection::load_presets(
|
||||
fs::remove(file_path);
|
||||
//throw Slic3r::RuntimeError(std::string("The selected preset cannot be loaded: ") + preset.file + "\n\tReason: " + err.what());
|
||||
} catch (const std::runtime_error &err) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Failed loading the user-config file: %1%. Reason: %2%")%preset.file %err.what();
|
||||
//throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + preset.file + "\n\tReason: " + err.what());
|
||||
fs::path file_path(preset.file);
|
||||
@@ -1252,6 +1260,7 @@ int PresetCollection::get_differed_values_to_update(Preset& preset, std::map<std
|
||||
{
|
||||
if (preset.is_system || preset.is_default || preset.is_project_embedded) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" Error: not a user preset! Should not happen, name %1%") %preset.name;
|
||||
++m_errors;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1351,9 +1360,11 @@ void PresetCollection::load_project_embedded_presets(std::vector<Preset*>& proje
|
||||
Preset::normalize(preset->config);
|
||||
// Report configuration fields, which are misplaced into a wrong group.
|
||||
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->name << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
|
||||
if (!incorrect_keys.empty()) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << "Error in a preset file: The preset \"" << preset->name
|
||||
<< "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
|
||||
}
|
||||
preset->loaded = true;
|
||||
presets_loaded.emplace_back(*preset);
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(", %1% got preset, 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;
|
||||
@@ -1533,6 +1544,7 @@ void PresetCollection::save_user_presets(const std::string& dir_path, const std:
|
||||
}
|
||||
Preset* parent_preset = this->find_preset(inherits, false, true);
|
||||
if (!parent_preset) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" can not find parent preset for %1% , inherits %2%")%preset->name %inherits;
|
||||
continue;
|
||||
}
|
||||
@@ -1680,9 +1692,11 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
|
||||
Preset::normalize(new_config);
|
||||
// Report configuration fields, which are misplaced into a wrong group.
|
||||
std::string incorrect_keys = Preset::remove_invalid_keys(new_config, default_preset.config);
|
||||
if (! incorrect_keys.empty())
|
||||
BOOST_LOG_TRIVIAL(error) << "Error in a preset file: The preset \"" <<
|
||||
name << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
|
||||
if (!incorrect_keys.empty()) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << "Error in a preset file: The preset \"" << name
|
||||
<< "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
|
||||
}
|
||||
if (need_update) {
|
||||
if (iter->name == m_edited_preset.name && iter->is_dirty) {
|
||||
// Keep modifies when update from remote
|
||||
@@ -2800,9 +2814,13 @@ void PresetCollection::update_map_system_profile_renamed()
|
||||
for (Preset &preset : m_presets)
|
||||
for (const std::string &renamed_from : preset.renamed_from) {
|
||||
const auto [it, success] = m_map_system_profile_renamed.insert(std::pair<std::string, std::string>(renamed_from, preset.name));
|
||||
if (! success)
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Preset name \"%1%\" was marked as renamed from \"%2%\", though preset name \"%3%\" was marked as renamed from \"%2%\" as well.") % preset.name % renamed_from % it->second;
|
||||
}
|
||||
if (!success) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Preset name \"%1%\" was marked as renamed from \"%2%\", though preset name "
|
||||
"\"%3%\" was marked as renamed from \"%2%\" as well.") %
|
||||
preset.name % renamed_from % it->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string PresetCollection::name() const
|
||||
|
||||
@@ -770,6 +770,9 @@ private:
|
||||
|
||||
//BBS: mutex
|
||||
std::mutex m_mutex;
|
||||
|
||||
// Orca: used for validation only
|
||||
int m_errors = 0;
|
||||
};
|
||||
|
||||
// Printer supports the FFF and SLA technologies, with different set of configuration values,
|
||||
|
||||
@@ -831,17 +831,21 @@ bool PresetBundle::import_json_presets(PresetsConfigSubstitutions & s
|
||||
// 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 (!incorrect_keys.empty()) {
|
||||
++m_errors;
|
||||
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) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("The config cannot be loaded: %1%. Reason: %2%") % file % err.what();
|
||||
} catch (const std::runtime_error &err) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Failed importing config file: %1%. Reason: %2%") % file % err.what();
|
||||
}
|
||||
return true;
|
||||
@@ -945,6 +949,7 @@ void PresetBundle::update_system_preset_setting_ids(std::map<std::string, std::m
|
||||
Preset* preset = preset_collection->find_preset(name, false, true);
|
||||
if (preset) {
|
||||
if (!preset->setting_id.empty() && (preset->setting_id.compare(setting_id) != 0)) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("name %1%, local setting_id %2% is different with remote id %3%")
|
||||
%preset->name %preset->setting_id %setting_id;
|
||||
}
|
||||
@@ -1211,6 +1216,9 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
|
||||
// Here the vendor specific read only Config Bundles are stored.
|
||||
//BBS: change directory by design
|
||||
boost::filesystem::path dir = (boost::filesystem::path(data_dir()) / PRESET_SYSTEM_DIR).make_preferred();
|
||||
if (validation_mode)
|
||||
dir = (boost::filesystem::path(data_dir())).make_preferred();
|
||||
|
||||
PresetsConfigSubstitutions substitutions;
|
||||
std::string errors_cummulative;
|
||||
bool first = true;
|
||||
@@ -1221,6 +1229,10 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
|
||||
std::string vendor_name = dir_entry.path().filename().string();
|
||||
// Remove the .json suffix.
|
||||
vendor_name.erase(vendor_name.size() - 5);
|
||||
|
||||
if (validation_mode && !vendor_to_validate.empty() && vendor_name != vendor_to_validate)
|
||||
continue;
|
||||
|
||||
try {
|
||||
// Load the config bundle, flatten it.
|
||||
if (first) {
|
||||
@@ -1243,8 +1255,12 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
|
||||
}
|
||||
}
|
||||
} catch (const std::runtime_error &err) {
|
||||
errors_cummulative += err.what();
|
||||
errors_cummulative += "\n";
|
||||
if (validation_mode)
|
||||
throw err;
|
||||
else {
|
||||
errors_cummulative += err.what();
|
||||
errors_cummulative += "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3145,7 +3161,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
std::vector<std::pair<std::string, std::string>> process_subfiles;
|
||||
std::vector<std::pair<std::string, std::string>> filament_subfiles;
|
||||
std::vector<std::pair<std::string, std::string>> machine_subfiles;
|
||||
auto get_name_and_subpath = [](json::iterator& it, std::vector<std::pair<std::string, std::string>>& subfile_map) {
|
||||
auto get_name_and_subpath = [this](json::iterator& it, std::vector<std::pair<std::string, std::string>>& subfile_map) {
|
||||
if (it.value().is_array()) {
|
||||
for (auto iter1 = it.value().begin(); iter1 != it.value().end(); iter1++) {
|
||||
if (iter1.value().is_object()) {
|
||||
@@ -3159,18 +3175,21 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
}
|
||||
}
|
||||
else {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": invalid value type for " << iter2.key();
|
||||
}
|
||||
}
|
||||
if (!name.empty() && !subpath.empty())
|
||||
subfile_map.push_back(std::make_pair(name, subpath));
|
||||
} else {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": invalid type for " << iter1.key();
|
||||
}
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": invalid type for " << iter1.key();
|
||||
}
|
||||
} else {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": invalid type for " << it.key();
|
||||
}
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": invalid type for " << it.key();
|
||||
};
|
||||
try {
|
||||
boost::nowide::ifstream ifs(root_file);
|
||||
@@ -3268,6 +3287,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
model.variants.emplace_back(VendorProfile::PrinterVariant(variant_name));
|
||||
}
|
||||
} else {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error)<< __FUNCTION__ << boost::format(": invalid nozzle_diameters %1% for Vendor %1%") % nozzle_diameters % vendor_name;
|
||||
}
|
||||
}
|
||||
@@ -3302,6 +3322,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
// An empty material was inserted into the list of default materials. Remove it.
|
||||
model.default_materials.erase(model.default_materials.begin());
|
||||
} else {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": invalid default_materials %1% for Vendor %1%") % default_materials_field % vendor_name;
|
||||
}
|
||||
}
|
||||
@@ -3330,7 +3351,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
PresetCollection *presets = nullptr;
|
||||
size_t presets_loaded = 0;
|
||||
|
||||
auto parse_subfile = [path, vendor_name, presets_loaded, current_vendor_profile](\
|
||||
auto parse_subfile = [this, path, vendor_name, presets_loaded, current_vendor_profile](
|
||||
ConfigSubstitutionContext& substitution_context,
|
||||
PresetsConfigSubstitutions& substitutions,
|
||||
LoadConfigBundleAttributes& flags,
|
||||
@@ -3356,6 +3377,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
DynamicPrintConfig config_src;
|
||||
config_src.load_from_json(subfile, substitution_context, false, key_values, reason);
|
||||
if (!reason.empty()) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": load config file "<<subfile<<" Failed!";
|
||||
return reason;
|
||||
}
|
||||
@@ -3382,6 +3404,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
}
|
||||
}
|
||||
else {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": can not find inherits "<<inherits<<" for " << preset_name;
|
||||
//throw ConfigurationError(format("can not find inherits %1% for %2%", inherits, preset_name));
|
||||
reason = "Can not find inherits: " + inherits;
|
||||
@@ -3411,6 +3434,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
Preset::normalize(config);
|
||||
}
|
||||
catch(nlohmann::detail::parse_error &err) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "<< subfile <<" got a nlohmann::detail::parse_error, reason = " << err.what();
|
||||
reason = std::string("json parse error") + err.what();
|
||||
return reason;
|
||||
@@ -3418,15 +3442,18 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
|
||||
// Report configuration fields, which are misplaced into a wrong group.
|
||||
std::string incorrect_keys = Preset::remove_invalid_keys(config, *default_config);
|
||||
if (! incorrect_keys.empty())
|
||||
BOOST_LOG_TRIVIAL(error)<< __FUNCTION__ << ": The config " <<
|
||||
subfile << " contains incorrect keys: " << incorrect_keys << ", which were removed";
|
||||
if (!incorrect_keys.empty()) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": The config " << subfile << " contains incorrect keys: " << incorrect_keys
|
||||
<< ", which were removed";
|
||||
}
|
||||
|
||||
if (presets_collection->type() == Preset::TYPE_PRINTER) {
|
||||
// Filter out printer presets, which are not mentioned in the vendor profile.
|
||||
// These presets are considered not installed.
|
||||
auto printer_model = config.opt_string("printer_model");
|
||||
if (printer_model.empty()) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" <<
|
||||
preset_name << "\" defines no printer model, it will be ignored.";
|
||||
reason = std::string("can not find printer_model");
|
||||
@@ -3434,6 +3461,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
}
|
||||
auto printer_variant = config.opt_string("printer_variant");
|
||||
if (printer_variant.empty()) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" <<
|
||||
preset_name << "\" defines no printer variant, it will be ignored.";
|
||||
reason = std::string("can not find printer_variant");
|
||||
@@ -3443,6 +3471,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
[&](const VendorProfile::PrinterModel &m) { return m.id == printer_model; }
|
||||
);
|
||||
if (it_model == current_vendor_profile->models.end()) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" <<
|
||||
preset_name << "\" defines invalid printer model \"" << printer_model << "\", it will be ignored.";
|
||||
reason = std::string("can not find printer model in vendor profile");
|
||||
@@ -3450,6 +3479,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
}
|
||||
auto it_variant = it_model->variant(printer_variant);
|
||||
if (it_variant == nullptr) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" <<
|
||||
preset_name << "\" defines invalid printer variant \"" << printer_variant << "\", it will be ignored.";
|
||||
reason = std::string("can not find printer_variant in vendor profile");
|
||||
@@ -3458,6 +3488,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
}
|
||||
const Preset *preset_existing = presets_collection->find_preset(preset_name, false);
|
||||
if (preset_existing != nullptr) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" <<
|
||||
preset_name << "\" has already been loaded from another Config Bundle.";
|
||||
reason = std::string("duplicated defines");
|
||||
@@ -3465,6 +3496,9 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
}
|
||||
|
||||
auto file_path = (boost::filesystem::path(data_dir()) /PRESET_SYSTEM_DIR/ vendor_name / subfile_iter.second).make_preferred();
|
||||
if(validation_mode)
|
||||
file_path = (boost::filesystem::path(data_dir()) / vendor_name / subfile_iter.second).make_preferred();
|
||||
|
||||
// Load the preset into the list of presets, save it to disk.
|
||||
Preset &loaded = presets_collection->load_preset(file_path.string(), preset_name, std::move(config), false);
|
||||
if (flags.has(LoadConfigBundleAttribute::LoadSystem)) {
|
||||
@@ -3476,6 +3510,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << __LINE__ << loaded.name << " load filament_id: " << filament_id;
|
||||
if (presets_collection->type() == Preset::TYPE_FILAMENT) {
|
||||
if (filament_id.empty() && "Template" != vendor_name) {
|
||||
++m_errors;
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": can not find filament_id for " << preset_name;
|
||||
//throw ConfigurationError(format("can not find inherits %1% for %2%", inherits, preset_name));
|
||||
reason = "Can not find filament_id for " + preset_name;
|
||||
@@ -3524,6 +3559,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
{
|
||||
std::string reason = parse_subfile(substitution_context, substitutions, flags, subfile, configs, filament_id_maps, presets, presets_loaded);
|
||||
if (!reason.empty()) {
|
||||
++m_errors;
|
||||
//parse error
|
||||
std::string subfile_path = path + "/" + vendor_name + "/" + subfile.second;
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", got error when parse process setting from %1%") % subfile_path;
|
||||
@@ -3539,6 +3575,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
{
|
||||
std::string reason = parse_subfile(substitution_context, substitutions, flags, subfile, configs, filament_id_maps, presets, presets_loaded);
|
||||
if (!reason.empty()) {
|
||||
++m_errors;
|
||||
//parse error
|
||||
std::string subfile_path = path + "/" + vendor_name + "/" + subfile.second;
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", got error when parse filament setting from %1%") % subfile_path;
|
||||
@@ -3554,6 +3591,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
{
|
||||
std::string reason = parse_subfile(substitution_context, substitutions, flags, subfile, configs, filament_id_maps, presets, presets_loaded);
|
||||
if (!reason.empty()) {
|
||||
++m_errors;
|
||||
//parse error
|
||||
std::string subfile_path = path + "/" + vendor_name + "/" + subfile.second;
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", got error when parse printer setting from %1%") % subfile_path;
|
||||
|
||||
@@ -121,6 +121,9 @@ public:
|
||||
void update_selections(AppConfig &config);
|
||||
void set_calibrate_printer(std::string name);
|
||||
|
||||
void set_is_validation_mode(bool mode) { validation_mode = mode; }
|
||||
void set_vendor_to_validate(std::string vendor) { vendor_to_validate = vendor; }
|
||||
|
||||
PresetCollection prints;
|
||||
PresetCollection sla_prints;
|
||||
PresetCollection filaments;
|
||||
@@ -261,6 +264,14 @@ public:
|
||||
return { Preset::TYPE_PRINTER, Preset::TYPE_SLA_PRINT, Preset::TYPE_SLA_MATERIAL };
|
||||
}
|
||||
|
||||
// Orca: for validation only
|
||||
bool has_errors() const
|
||||
{
|
||||
if (m_errors != 0 || printers.m_errors != 0 || filaments.m_errors != 0 || prints.m_errors != 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
//std::pair<PresetsConfigSubstitutions, std::string> load_system_presets(ForwardCompatibilitySubstitutionRule compatibility_rule);
|
||||
//BBS: add json related logic
|
||||
@@ -284,6 +295,12 @@ private:
|
||||
|
||||
DynamicPrintConfig full_fff_config() const;
|
||||
DynamicPrintConfig full_sla_config() const;
|
||||
|
||||
// Orca: used for validation only
|
||||
bool validation_mode = false;
|
||||
std::string vendor_to_validate = "";
|
||||
int m_errors = 0;
|
||||
|
||||
};
|
||||
|
||||
ENABLE_ENUM_BITMASK_OPERATORS(PresetBundle::LoadConfigBundleAttribute)
|
||||
|
||||
@@ -1419,6 +1419,17 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
}
|
||||
}
|
||||
|
||||
if (warning && (m_default_object_config.default_jerk == 1 || m_default_object_config.outer_wall_jerk == 1 ||
|
||||
m_default_object_config.inner_wall_jerk == 1)) {
|
||||
warning->string = L("Setting the jerk speed too low could lead to artifacts on curved surfaces");
|
||||
if (m_default_object_config.outer_wall_jerk == 1)
|
||||
warning->opt_key = "outer_wall_jerk";
|
||||
else if (m_default_object_config.inner_wall_jerk == 1)
|
||||
warning->opt_key = "inner_wall_jerk";
|
||||
else
|
||||
warning->opt_key = "default_jerk";
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -1041,6 +1041,24 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
else
|
||||
m_support_used = false;
|
||||
|
||||
{
|
||||
const auto& o = model.objects;
|
||||
const auto opt_has_scarf_joint_seam = [](const DynamicConfig& c) {
|
||||
return c.has("seam_slope_type") && c.opt_enum<SeamScarfType>("seam_slope_type") != SeamScarfType::None;
|
||||
};
|
||||
const bool has_scarf_joint_seam = std::any_of(o.begin(), o.end(), [&new_full_config, &opt_has_scarf_joint_seam](ModelObject* obj) {
|
||||
return obj->get_config_value<ConfigOptionEnum<SeamScarfType>>(new_full_config, "seam_slope_type")->value != SeamScarfType::None ||
|
||||
std::any_of(obj->volumes.begin(), obj->volumes.end(), [&opt_has_scarf_joint_seam](const ModelVolume* v) { return opt_has_scarf_joint_seam(v->config.get());}) ||
|
||||
std::any_of(obj->layer_config_ranges.begin(), obj->layer_config_ranges.end(), [&opt_has_scarf_joint_seam](const auto& r) { return opt_has_scarf_joint_seam(r.second.get());});
|
||||
});
|
||||
|
||||
if (has_scarf_joint_seam) {
|
||||
new_full_config.set("has_scarf_joint_seam", true);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", has_scarf_joint_seam:" << has_scarf_joint_seam;
|
||||
}
|
||||
|
||||
// Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
|
||||
DynamicPrintConfig filament_overrides;
|
||||
//BBS: add plate index
|
||||
|
||||
+372
-244
File diff suppressed because it is too large
Load Diff
@@ -59,7 +59,7 @@ enum class FuzzySkinType {
|
||||
};
|
||||
|
||||
enum PrintHostType {
|
||||
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS
|
||||
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htObico
|
||||
};
|
||||
|
||||
enum AuthorizationType {
|
||||
@@ -98,6 +98,16 @@ enum class WallSequence {
|
||||
InnerOuterInner,
|
||||
Count,
|
||||
};
|
||||
|
||||
// Orca
|
||||
enum class WallDirection
|
||||
{
|
||||
Auto,
|
||||
CounterClockwise,
|
||||
Clockwise,
|
||||
Count,
|
||||
};
|
||||
|
||||
//BBS
|
||||
enum class PrintSequence {
|
||||
ByLayer,
|
||||
@@ -159,6 +169,13 @@ enum SeamPosition {
|
||||
spNearest, spAligned, spRear, spRandom
|
||||
};
|
||||
|
||||
// Orca
|
||||
enum class SeamScarfType {
|
||||
None,
|
||||
External,
|
||||
All,
|
||||
};
|
||||
|
||||
//Orca
|
||||
enum InternalBridgeFilter {
|
||||
ibfDisabled, ibfLimited, ibfNofilter
|
||||
@@ -372,6 +389,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SupportMaterialInterfacePattern)
|
||||
// BBS
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SupportType)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SeamPosition)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SeamScarfType)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLADisplayOrientation)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SLAPillarConnectionMode)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(BrimType)
|
||||
@@ -828,7 +846,6 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, top_surface_jerk))
|
||||
((ConfigOptionFloat, initial_layer_jerk))
|
||||
((ConfigOptionFloat, travel_jerk))
|
||||
|
||||
)
|
||||
|
||||
// This object is mapped to Perl as Slic3r::Config::PrintRegion.
|
||||
@@ -932,6 +949,15 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionEnum<WallSequence>, wall_sequence))
|
||||
((ConfigOptionBool, is_infill_first))
|
||||
((ConfigOptionBool, small_area_infill_flow_compensation))
|
||||
((ConfigOptionEnum<WallDirection>, wall_direction))
|
||||
|
||||
// Orca: seam slopes
|
||||
((ConfigOptionEnum<SeamScarfType>, seam_slope_type))
|
||||
((ConfigOptionFloatOrPercent, seam_slope_start_height))
|
||||
((ConfigOptionBool, seam_slope_entire_loop))
|
||||
((ConfigOptionFloat, seam_slope_min_length))
|
||||
((ConfigOptionInt, seam_slope_steps))
|
||||
((ConfigOptionBool, seam_slope_inner_walls))
|
||||
)
|
||||
|
||||
PRINT_CONFIG_CLASS_DEFINE(
|
||||
@@ -1081,6 +1107,8 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
|
||||
// Small Area Infill Flow Compensation
|
||||
((ConfigOptionStrings, small_area_infill_flow_compensation_model))
|
||||
|
||||
((ConfigOptionBool, has_scarf_joint_seam))
|
||||
)
|
||||
|
||||
// This object is mapped to Perl as Slic3r::Config::Print.
|
||||
@@ -1213,7 +1241,12 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
((ConfigOptionBools, activate_chamber_temp_control))
|
||||
((ConfigOptionInts , chamber_temperature))
|
||||
|
||||
// Orca: support adaptive bed mesh
|
||||
((ConfigOptionFloat, preferred_orientation))
|
||||
((ConfigOptionPoint, bed_mesh_min))
|
||||
((ConfigOptionPoint, bed_mesh_max))
|
||||
((ConfigOptionPoint, bed_mesh_probe_distance))
|
||||
((ConfigOptionFloat, adaptive_bed_mesh_margin))
|
||||
|
||||
|
||||
)
|
||||
|
||||
@@ -1114,6 +1114,7 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "overhang_reverse"
|
||||
|| opt_key == "overhang_reverse_internal_only"
|
||||
|| opt_key == "overhang_reverse_threshold"
|
||||
|| opt_key == "wall_direction"
|
||||
//BBS
|
||||
|| opt_key == "enable_overhang_speed"
|
||||
|| opt_key == "detect_thin_wall"
|
||||
@@ -1141,6 +1142,12 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
steps.emplace_back(posSlice);
|
||||
} else if (
|
||||
opt_key == "seam_position"
|
||||
|| opt_key == "seam_slope_type"
|
||||
|| opt_key == "seam_slope_start_height"
|
||||
|| opt_key == "seam_slope_entire_loop"
|
||||
|| opt_key == "seam_slope_min_length"
|
||||
|| opt_key == "seam_slope_steps"
|
||||
|| opt_key == "seam_slope_inner_walls"
|
||||
|| opt_key == "support_speed"
|
||||
|| opt_key == "support_interface_speed"
|
||||
|| opt_key == "overhang_1_4_speed"
|
||||
@@ -1155,7 +1162,11 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "sparse_infill_speed"
|
||||
|| opt_key == "inner_wall_speed"
|
||||
|| opt_key == "internal_solid_infill_speed"
|
||||
|| opt_key == "top_surface_speed") {
|
||||
|| opt_key == "top_surface_speed"
|
||||
|| opt_key == "bed_mesh_min"
|
||||
|| opt_key == "bed_mesh_max"
|
||||
|| opt_key == "adaptive_bed_mesh_margin"
|
||||
|| opt_key == "bed_mesh_probe_distance") {
|
||||
invalidated |= m_print->invalidate_step(psGCodeExport);
|
||||
} else if (
|
||||
opt_key == "flush_into_infill"
|
||||
|
||||
@@ -524,6 +524,10 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/calib_dlg.cpp
|
||||
Utils/CalibUtils.cpp
|
||||
Utils/CalibUtils.hpp
|
||||
GUI/PrinterCloudAuthDialog.cpp
|
||||
GUI/PrinterCloudAuthDialog.hpp
|
||||
Utils/Obico.cpp
|
||||
Utils/Obico.hpp
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
@@ -573,7 +577,7 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SLIC3R_GUI_SOURCES})
|
||||
|
||||
encoding_check(libslic3r_gui)
|
||||
|
||||
target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui minilzo GLEW::GLEW OpenGL::GL hidapi ${wxWidgets_LIBRARIES} glfw libcurl OpenSSL::SSL OpenSSL::Crypto)
|
||||
target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui imguizmo minilzo GLEW::GLEW OpenGL::GL hidapi ${wxWidgets_LIBRARIES} glfw libcurl OpenSSL::SSL OpenSSL::Crypto)
|
||||
#target_link_libraries(libslic3r_gui libslic3r cereal imgui minilzo GLEW::GLEW OpenGL::GL hidapi libcurl OpenSSL::SSL OpenSSL::Crypto ${wxWidgets_LIBRARIES} glfw)
|
||||
|
||||
if (MSVC)
|
||||
|
||||
@@ -100,6 +100,7 @@ void CopyrightsDialog::fill_entries()
|
||||
{ "GLFW", "", "https://www.glfw.org" },
|
||||
{ "GNU gettext", "", "https://www.gnu.org/software/gettext" },
|
||||
{ "ImGUI", "", "https://github.com/ocornut/imgui" },
|
||||
{ "ImGuizmo", "", "https://github.com/CedricGuillemet/ImGuizmo" },
|
||||
{ "Libigl", "", "https://libigl.github.io" },
|
||||
{ "libnest2d", "", "https://github.com/tamasmeszaros/libnest2d" },
|
||||
{ "lib_fts", "", "https://www.forrestthewoods.com" },
|
||||
|
||||
@@ -419,6 +419,15 @@ void Camera::rotate_local_around_target(const Vec3d& rotation_rad)
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::set_rotation(const Transform3d& rotation)
|
||||
{
|
||||
const Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target;
|
||||
m_view_rotation = Eigen::Quaterniond(rotation.matrix().template block<3, 3>(0, 0));
|
||||
m_view_rotation.normalize();
|
||||
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (-m_target) + translation, m_view_rotation, Vec3d(1., 1., 1.));
|
||||
update_zenit();
|
||||
}
|
||||
|
||||
std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBoxf3& box)
|
||||
{
|
||||
std::pair<double, double> ret;
|
||||
|
||||
@@ -146,6 +146,8 @@ public:
|
||||
// rotate the camera around three axes parallel to the camera local axes and passing through m_target
|
||||
void rotate_local_around_target(const Vec3d& rotation_rad);
|
||||
|
||||
void set_rotation(const Transform3d& rotation);
|
||||
|
||||
// returns true if the camera z axis (forward) is pointing in the negative direction of the world z axis
|
||||
bool is_looking_downward() const { return get_dir_forward().dot(Vec3d::UnitZ()) < 0.0; }
|
||||
|
||||
|
||||
@@ -279,6 +279,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||
config->opt_int("enforce_support_layers") == 0 &&
|
||||
! config->opt_bool("detect_thin_wall") &&
|
||||
! config->opt_bool("overhang_reverse") &&
|
||||
config->opt_enum<WallDirection>("wall_direction") == WallDirection::Auto &&
|
||||
config->opt_enum<TimelapseType>("timelapse_type") == TimelapseType::tlTraditional))
|
||||
{
|
||||
wxString msg_text = _(L("Spiral mode only works when wall loops is 1, support is disabled, top shell layers is 0, sparse infill density is 0 and timelapse type is traditional."));
|
||||
@@ -306,6 +307,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||
new_conf.set_key_value("enforce_support_layers", new ConfigOptionInt(0));
|
||||
new_conf.set_key_value("detect_thin_wall", new ConfigOptionBool(false));
|
||||
new_conf.set_key_value("overhang_reverse", new ConfigOptionBool(false));
|
||||
new_conf.set_key_value("wall_direction", new ConfigOptionEnum<WallDirection>(WallDirection::Auto));
|
||||
new_conf.set_key_value("timelapse_type", new ConfigOptionEnum<TimelapseType>(tlTraditional));
|
||||
sparse_infill_density = 0;
|
||||
timelapse_type = TimelapseType::tlTraditional;
|
||||
@@ -554,6 +556,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||
|
||||
toggle_field("top_shell_thickness", ! has_spiral_vase && has_top_solid_infill);
|
||||
toggle_field("bottom_shell_thickness", ! has_spiral_vase && has_bottom_solid_infill);
|
||||
|
||||
toggle_field("wall_direction", !has_spiral_vase);
|
||||
|
||||
// Gap fill is newly allowed in between perimeter lines even for empty infill (see GH #1476).
|
||||
toggle_field("gap_infill_speed", have_perimeters);
|
||||
@@ -732,7 +736,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||
|
||||
bool has_detect_overhang_wall = config->opt_bool("detect_overhang_wall");
|
||||
bool has_overhang_reverse = config->opt_bool("overhang_reverse");
|
||||
bool allow_overhang_reverse = has_detect_overhang_wall && !has_spiral_vase;
|
||||
bool force_wall_direction = config->opt_enum<WallDirection>("wall_direction") != WallDirection::Auto;
|
||||
bool allow_overhang_reverse = has_detect_overhang_wall && !has_spiral_vase && !force_wall_direction;
|
||||
toggle_field("overhang_reverse", allow_overhang_reverse);
|
||||
toggle_line("overhang_reverse_threshold", allow_overhang_reverse && has_overhang_reverse);
|
||||
toggle_line("overhang_reverse_internal_only", allow_overhang_reverse && has_overhang_reverse);
|
||||
@@ -747,6 +752,16 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||
|
||||
bool have_small_area_infill_flow_compensation = config->opt_bool("small_area_infill_flow_compensation");
|
||||
toggle_line("small_area_infill_flow_compensation_model", have_small_area_infill_flow_compensation);
|
||||
|
||||
|
||||
toggle_field("seam_slope_type", !has_spiral_vase);
|
||||
bool has_seam_slope = !has_spiral_vase && config->opt_enum<SeamScarfType>("seam_slope_type") != SeamScarfType::None;
|
||||
toggle_line("seam_slope_start_height", has_seam_slope);
|
||||
toggle_line("seam_slope_entire_loop", has_seam_slope);
|
||||
toggle_line("seam_slope_min_length", has_seam_slope);
|
||||
toggle_line("seam_slope_steps", has_seam_slope);
|
||||
toggle_line("seam_slope_inner_walls", has_seam_slope);
|
||||
toggle_field("seam_slope_min_length", !config->opt_bool("seam_slope_entire_loop"));
|
||||
}
|
||||
|
||||
void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/)
|
||||
|
||||
@@ -3394,19 +3394,6 @@ bool ExportConfigsDialog::has_check_box_selected()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExportConfigsDialog::preset_is_not_compatible_bbl_printer(Preset *preset)
|
||||
{
|
||||
if (preset->type != Preset::Type::TYPE_PRINT && preset->type != Preset::Type::TYPE_FILAMENT) return true;
|
||||
PresetBundle * preset_bundle = wxGetApp().preset_bundle;
|
||||
vector<std::string> printers;
|
||||
get_filament_compatible_printer(preset, printers);
|
||||
if (printers.empty()) return true;
|
||||
Preset *printer_preset = preset_bundle->printers.find_preset(printers[0], false);
|
||||
if (!printer_preset) return true;
|
||||
if (!preset_bundle->is_bbl_vendor()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string ExportConfigsDialog::initial_file_path(const wxString &path, const std::string &sub_file_path)
|
||||
{
|
||||
std::string export_path = into_u8(path);
|
||||
@@ -3600,12 +3587,6 @@ void ExportConfigsDialog::select_curr_radiobox(std::vector<std::pair<RadioBox *,
|
||||
}else if (export_type == m_exprot_type.filament_bundle) {
|
||||
for (std::pair<std::string, std::vector<std::pair<std::string, Preset*>>> filament_name_to_preset : m_filament_name_to_presets) {
|
||||
if (filament_name_to_preset.second.empty()) continue;
|
||||
bool all_preset_is_compatible_third_printer = true;
|
||||
for (std::pair<std::string, Preset *> filament_preset : filament_name_to_preset.second) {
|
||||
if (!preset_is_not_compatible_bbl_printer(filament_preset.second))
|
||||
all_preset_is_compatible_third_printer = false;
|
||||
}
|
||||
if (all_preset_is_compatible_third_printer) continue;
|
||||
wxString filament_name = wxString::FromUTF8(filament_name_to_preset.first);
|
||||
m_preset_sizer->Add(create_checkbox(m_presets_window, filament_name, m_printer_name), 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, FromDIP(5));
|
||||
}
|
||||
@@ -3621,12 +3602,6 @@ void ExportConfigsDialog::select_curr_radiobox(std::vector<std::pair<RadioBox *,
|
||||
} else if (export_type == m_exprot_type.filament_preset) {
|
||||
for (std::pair<std::string, std::vector<std::pair<std::string, Preset *>>> filament_name_to_preset : m_filament_name_to_presets) {
|
||||
if (filament_name_to_preset.second.empty()) continue;
|
||||
bool all_preset_is_compatible_third_printer = true;
|
||||
for (std::pair<std::string, Preset*> filament_preset : filament_name_to_preset.second) {
|
||||
if (!preset_is_not_compatible_bbl_printer(filament_preset.second))
|
||||
all_preset_is_compatible_third_printer = false;
|
||||
}
|
||||
if (all_preset_is_compatible_third_printer) continue;
|
||||
wxString filament_name = wxString::FromUTF8(filament_name_to_preset.first);
|
||||
m_preset_sizer->Add(create_checkbox(m_presets_window, filament_name, m_printer_name), 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, FromDIP(5));
|
||||
}
|
||||
@@ -3821,7 +3796,6 @@ ExportConfigsDialog::ExportCase ExportConfigsDialog::archive_filament_bundle_to_
|
||||
std::string printer_vendor = printer_name_to_preset.first;
|
||||
if (printer_vendor.empty()) continue;
|
||||
Preset * filament_preset = printer_name_to_preset.second;
|
||||
if (preset_is_not_compatible_bbl_printer(filament_preset)) continue;
|
||||
if (vendor_to_filament_name.find(std::make_pair(printer_vendor, filament_preset->name)) != vendor_to_filament_name.end()) continue;
|
||||
vendor_to_filament_name.insert(std::make_pair(printer_vendor, filament_preset->name));
|
||||
std::string preset_path = boost::filesystem::path(filament_preset->file).make_preferred().string();
|
||||
@@ -3928,7 +3902,6 @@ ExportConfigsDialog::ExportCase ExportConfigsDialog::archive_filament_preset_to_
|
||||
}
|
||||
for (std::pair<std::string, Preset*> printer_name_preset : iter->second) {
|
||||
Preset * filament_preset = printer_name_preset.second;
|
||||
if (preset_is_not_compatible_bbl_printer(filament_preset)) continue;
|
||||
if (filament_presets.find(filament_preset->name) != filament_presets.end()) continue;
|
||||
filament_presets.insert(filament_preset->name);
|
||||
std::string preset_path = boost::filesystem::path(filament_preset->file).make_preferred().string();
|
||||
@@ -3967,7 +3940,6 @@ ExportConfigsDialog::ExportCase ExportConfigsDialog::archive_process_preset_to_f
|
||||
std::unordered_map<std::string, std::vector<Preset *>>::iterator iter = m_process_presets.find(printer_name);
|
||||
if (m_process_presets.end() != iter) {
|
||||
for (Preset *process_preset : iter->second) {
|
||||
if (preset_is_not_compatible_bbl_printer(process_preset)) continue;
|
||||
if (process_presets.find(process_preset->name) != process_presets.end()) continue;
|
||||
process_presets.insert(process_preset->name);
|
||||
std::string preset_path = boost::filesystem::path(process_preset->file).make_preferred().string();
|
||||
|
||||
@@ -262,7 +262,6 @@ private:
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
void show_export_result(const ExportCase &export_case);
|
||||
bool has_check_box_selected();
|
||||
bool preset_is_not_compatible_bbl_printer(Preset *preset);
|
||||
std::string initial_file_path(const wxString &path, const std::string &sub_file_path);
|
||||
std::string initial_file_name(const wxString &path, const std::string file_name);
|
||||
wxBoxSizer *create_export_config_item(wxWindow *parent);
|
||||
|
||||
@@ -588,6 +588,14 @@ void TextCtrl::BUILD() {
|
||||
EnterPressed enter(this);
|
||||
propagate_value();
|
||||
}), text_ctrl->GetId());
|
||||
} else {
|
||||
// Orca: adds logic that scrolls the parent if the text control doesn't have focus
|
||||
text_ctrl->Bind(wxEVT_MOUSEWHEEL, [text_ctrl](wxMouseEvent& event) {
|
||||
if (text_ctrl->HasFocus() && text_ctrl->GetScrollRange(wxVERTICAL) != 1)
|
||||
event.Skip(); // don't consume the event so that the text control will scroll
|
||||
else
|
||||
text_ctrl->GetParent()->ScrollLines((event.GetWheelRotation() > 0 ? -1 : 1) * event.GetLinesPerAction());
|
||||
});
|
||||
}
|
||||
|
||||
text_ctrl->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent &event)
|
||||
|
||||
@@ -85,6 +85,8 @@
|
||||
#endif
|
||||
#include <imgui/imgui_internal.h>
|
||||
|
||||
#include <imguizmo/ImGuizmo.h>
|
||||
|
||||
static constexpr const float TRACKBALLSIZE = 0.8f;
|
||||
|
||||
static Slic3r::ColorRGBA DEFAULT_BG_LIGHT_COLOR = { 0.906f, 0.906f, 0.906f, 1.0f };
|
||||
@@ -5535,6 +5537,70 @@ bool GLCanvas3D::_render_arrange_menu(float left, float right, float bottom, flo
|
||||
return settings_changed;
|
||||
}
|
||||
|
||||
static float identityMatrix[16] = {1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f};
|
||||
static const float cameraProjection[16] = {1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f};
|
||||
|
||||
void GLCanvas3D::_render_3d_navigator()
|
||||
{
|
||||
if (!wxGetApp().show_3d_navigator()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGuizmo::BeginFrame();
|
||||
ImGuizmo::AllowAxisFlip(false);
|
||||
|
||||
auto& style = ImGuizmo::GetStyle();
|
||||
style.Colors[ImGuizmo::COLOR::DIRECTION_X] = ImGuiWrapper::to_ImVec4(ColorRGBA::Y());
|
||||
style.Colors[ImGuizmo::COLOR::DIRECTION_Y] = ImGuiWrapper::to_ImVec4(ColorRGBA::Z());
|
||||
style.Colors[ImGuizmo::COLOR::DIRECTION_Z] = ImGuiWrapper::to_ImVec4(ColorRGBA::X());
|
||||
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_X], "y");
|
||||
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_Y], "z");
|
||||
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_Z], "x");
|
||||
|
||||
float sc = get_scale();
|
||||
#ifdef WIN32
|
||||
const int dpi = get_dpi_for_window(wxGetApp().GetTopWindow());
|
||||
sc *= (float) dpi / (float) DPI_DEFAULT;
|
||||
#endif // WIN32
|
||||
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
const float viewManipulateLeft = 0;
|
||||
const float viewManipulateTop = io.DisplaySize.y;
|
||||
const float camDistance = 8.f;
|
||||
ImGuizmo::SetID(0);
|
||||
|
||||
Camera& camera = wxGetApp().plater()->get_camera();
|
||||
Transform3d m = Transform3d::Identity();
|
||||
m.matrix().block(0, 0, 3, 3) = camera.get_view_rotation().toRotationMatrix();
|
||||
// Rotate along X and Z axis for 90 degrees to have Y-up
|
||||
const auto coord_mapping_transform = Geometry::rotation_transform(Vec3d(0.5 * PI, 0, 0.5 * PI));
|
||||
m = m * coord_mapping_transform;
|
||||
float cameraView[16];
|
||||
for (unsigned int c = 0; c < 4; ++c) {
|
||||
for (unsigned int r = 0; r < 4; ++r) {
|
||||
cameraView[c * 4 + r] = m(r, c);
|
||||
}
|
||||
}
|
||||
|
||||
const float size = 128 * sc;
|
||||
const bool dirty = ImGuizmo::ViewManipulate(cameraView, cameraProjection, ImGuizmo::OPERATION::ROTATE, ImGuizmo::MODE::WORLD,
|
||||
identityMatrix, camDistance, ImVec2(viewManipulateLeft, viewManipulateTop - size),
|
||||
ImVec2(size, size), 0x10101010);
|
||||
|
||||
if (dirty) {
|
||||
for (unsigned int c = 0; c < 4; ++c) {
|
||||
for (unsigned int r = 0; r < 4; ++r) {
|
||||
m(r, c) = cameraView[c * 4 + r];
|
||||
}
|
||||
}
|
||||
// Rotate back
|
||||
m = m * (coord_mapping_transform.inverse());
|
||||
camera.set_rotation(m);
|
||||
|
||||
request_extra_frame();
|
||||
}
|
||||
}
|
||||
|
||||
#define ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT 0
|
||||
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT
|
||||
static void debug_output_thumbnail(const ThumbnailData& thumbnail_data)
|
||||
@@ -7324,6 +7390,8 @@ void GLCanvas3D::_render_overlays()
|
||||
}*/
|
||||
}
|
||||
m_labels.render(sorted_instances);
|
||||
|
||||
_render_3d_navigator();
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_style_editor()
|
||||
|
||||
@@ -1181,6 +1181,7 @@ private:
|
||||
//BBS: GUI refactor: adjust main toolbar position
|
||||
bool _render_orient_menu(float left, float right, float bottom, float top);
|
||||
bool _render_arrange_menu(float left, float right, float bottom, float top);
|
||||
void _render_3d_navigator();
|
||||
// render thumbnail using the default framebuffer
|
||||
void render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector<ColorRGBA>& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type);
|
||||
|
||||
|
||||
@@ -329,6 +329,9 @@ private:
|
||||
bool show_gcode_window() const { return m_show_gcode_window; }
|
||||
void toggle_show_gcode_window();
|
||||
|
||||
bool show_3d_navigator() const { return app_config->get_bool("show_3d_navigator"); }
|
||||
void toggle_show_3d_navigator() const { app_config->set_bool("show_3d_navigator", !show_3d_navigator()); }
|
||||
|
||||
wxString get_inf_dialog_contect () {return m_info_dialog_content;};
|
||||
|
||||
std::vector<std::string> split_str(std::string src, std::string separator);
|
||||
|
||||
@@ -1815,7 +1815,7 @@ void ObjectGridTable::init_cols(ObjectGrid *object_grid)
|
||||
m_col_data.push_back(col);
|
||||
|
||||
//first column for plate_index
|
||||
col = new ObjectGridCol(coString, "plate_index", L(" "), true, false, false, false, wxALIGN_CENTRE); //bool only_object, bool icon, bool edit, bool config
|
||||
col = new ObjectGridCol(coString, "plate_index", " ", true, false, false, false, wxALIGN_CENTRE); //bool only_object, bool icon, bool edit, bool config
|
||||
m_col_data.push_back(col);
|
||||
|
||||
//second column for module name
|
||||
|
||||
@@ -2567,6 +2567,15 @@ void MainFrame::init_menubar_as_editor()
|
||||
this, [this]() { return m_tabpanel->GetSelection() == tpPreview; },
|
||||
[this]() { return wxGetApp().show_gcode_window(); }, this);
|
||||
|
||||
append_menu_check_item(
|
||||
viewMenu, wxID_ANY, _L("Show 3D Navigator"), _L("Show 3D navigator in Prepare and Preview scene"),
|
||||
[this](wxCommandEvent&) {
|
||||
wxGetApp().toggle_show_3d_navigator();
|
||||
m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT));
|
||||
},
|
||||
this, [this]() { return m_tabpanel->GetSelection() == TabPosition::tp3DEditor || m_tabpanel->GetSelection() == TabPosition::tpPreview; },
|
||||
[this]() { return wxGetApp().show_3d_navigator(); }, this);
|
||||
|
||||
append_menu_item(
|
||||
viewMenu, wxID_ANY, _L("Reset Window Layout"), _L("Reset to default window layout"),
|
||||
[this](wxCommandEvent&) { m_plater->reset_window_layout(); }, "", this,
|
||||
|
||||
@@ -2356,13 +2356,18 @@ void PartPlate::generate_logo_polygon(ExPolygon &logo_polygon)
|
||||
{
|
||||
if (m_shape.size() == 4)
|
||||
{
|
||||
//rectangle case
|
||||
auto preset_bundle = wxGetApp().preset_bundle;
|
||||
|
||||
bool is_bbl_vendor = false;
|
||||
if (preset_bundle)
|
||||
is_bbl_vendor = preset_bundle->is_bbl_vendor();
|
||||
//rectangle case
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
const Vec2d& p = m_shape[i];
|
||||
if ((i == 0) || (i == 1)) {
|
||||
logo_polygon.contour.append({ scale_(p(0)), scale_(p(1) - 12.f) });
|
||||
}
|
||||
logo_polygon.contour.append({scale_(p(0)), scale_(is_bbl_vendor ? p(1) - 12.f : p(1))});
|
||||
}
|
||||
else {
|
||||
logo_polygon.contour.append({ scale_(p(0)), scale_(p(1)) });
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#include "PhysicalPrinterDialog.hpp"
|
||||
#include "PresetComboBoxes.hpp"
|
||||
#include "PrinterCloudAuthDialog.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
@@ -124,6 +126,8 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
|
||||
this->update();
|
||||
if (opt_key == "print_host")
|
||||
this->update_printhost_buttons();
|
||||
if (opt_key == "printhost_port")
|
||||
this->update_ports();
|
||||
};
|
||||
|
||||
m_optgroup->append_single_option_line("host_type");
|
||||
@@ -161,12 +165,31 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
|
||||
show_error(this, text);
|
||||
return;
|
||||
}
|
||||
|
||||
wxString msg;
|
||||
bool result;
|
||||
{
|
||||
// Show a wait cursor during the connection test, as it is blocking UI.
|
||||
wxBusyCursor wait;
|
||||
result = host->test(msg);
|
||||
|
||||
if (!result && host->is_cloud()) {
|
||||
PrinterCloudAuthDialog dlg(this->GetParent(), host.get());
|
||||
dlg.ShowModal();
|
||||
|
||||
auto api_key = dlg.GetApiKey();
|
||||
m_config->opt_string("printhost_apikey") = api_key;
|
||||
result = !api_key.empty();
|
||||
if (result) {
|
||||
if (Field* print_host_webui_field = this->m_optgroup->get_field("printhost_apikey"); print_host_webui_field) {
|
||||
if (TextInput* temp_input = dynamic_cast<TextInput*>(print_host_webui_field->getWindow()); temp_input) {
|
||||
if (wxTextCtrl* temp = temp_input->GetTextCtrl()) {
|
||||
temp->SetValue(wxString(api_key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
show_info(this, host->get_test_ok_msg(), _L("Success!"));
|
||||
@@ -311,6 +334,42 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
|
||||
update();
|
||||
}
|
||||
|
||||
void PhysicalPrinterDialog::update_ports() {
|
||||
const PrinterTechnology tech = Preset::printer_technology(*m_config);
|
||||
if (tech == ptFFF) {
|
||||
const auto opt = m_config->option<ConfigOptionEnum<PrintHostType>>("host_type");
|
||||
if (opt->value == htObico) {
|
||||
auto build_web_ui = [](DynamicPrintConfig* config) {
|
||||
auto host = config->opt_string("print_host");
|
||||
auto port = config->opt_string("printhost_port");
|
||||
auto api_key = config->opt_string("printhost_apikey");
|
||||
if (host.empty() || port.empty()) {
|
||||
return std::string();
|
||||
}
|
||||
boost::regex re("\\[(\\d+)\\]");
|
||||
boost::smatch match;
|
||||
if (!boost::regex_search(port, match, re))
|
||||
return std::string();
|
||||
if (match.size() <= 1) {
|
||||
return std::string();
|
||||
}
|
||||
boost::format urlFormat("%1%/printers/%2%/control");
|
||||
urlFormat % host % match[1];
|
||||
return urlFormat.str();
|
||||
};
|
||||
auto url = build_web_ui(m_config);
|
||||
if (Field* print_host_webui_field = m_optgroup->get_field("print_host_webui"); print_host_webui_field) {
|
||||
if (TextInput* temp_input = dynamic_cast<TextInput*>(print_host_webui_field->getWindow()); temp_input) {
|
||||
if (wxTextCtrl* temp = temp_input->GetTextCtrl()) {
|
||||
temp->SetValue(wxString(url));
|
||||
m_config->opt_string("print_host_webui") = url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicalPrinterDialog::update_printhost_buttons()
|
||||
{
|
||||
std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config));
|
||||
@@ -415,6 +474,12 @@ void PhysicalPrinterDialog::update(bool printer_change)
|
||||
if (wxTextCtrl* temp = dynamic_cast<wxTextCtrl*>(printhost_field->getWindow()); temp && temp->GetValue() == L"https://connect.prusa3d.com") {
|
||||
temp->SetValue(wxString());
|
||||
}
|
||||
|
||||
if (TextInput* temp_input = dynamic_cast<TextInput*>(printhost_field->getWindow()); temp_input) {
|
||||
if (wxTextCtrl* temp = temp_input->GetTextCtrl(); temp &&temp->GetValue() == L"https://app.obico.io") {
|
||||
temp->SetValue(wxString());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opt->value == htPrusaLink) { // PrusaConnect does NOT allow http digest
|
||||
m_optgroup->show_field("printhost_authorization_type");
|
||||
@@ -436,6 +501,18 @@ void PhysicalPrinterDialog::update(bool printer_change)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opt->value == htObico) {
|
||||
supports_multiple_printers = true;
|
||||
if (Field* printhost_field = m_optgroup->get_field("print_host"); printhost_field) {
|
||||
if (TextInput* temp_input = dynamic_cast<TextInput*>(printhost_field->getWindow()); temp_input) {
|
||||
if (wxTextCtrl* temp = temp_input->GetTextCtrl(); temp && temp->GetValue().IsEmpty()) {
|
||||
temp->SetValue(L"https://app.obico.io");
|
||||
m_config->opt_string("print_host") = "https://app.obico.io";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_optgroup->set_value("host_type", int(PrintHostType::htOctoPrint), false);
|
||||
|
||||
@@ -61,6 +61,7 @@ public:
|
||||
void update_preset_input();
|
||||
void update_printhost_buttons();
|
||||
void update_printers();
|
||||
void update_ports();
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "PrinterCloudAuthDialog.hpp"
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/toolbar.h>
|
||||
#include <wx/textdlg.h>
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/fileconf.h>
|
||||
#include <wx/file.h>
|
||||
#include <wx/wfstream.h>
|
||||
|
||||
#include <boost/cast.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "MainFrame.hpp"
|
||||
#include <boost/dll.hpp>
|
||||
|
||||
#include <sstream>
|
||||
#include <slic3r/GUI/Widgets/WebView.hpp>
|
||||
//------------------------------------------
|
||||
// PrinterCloundAuthDialog
|
||||
//------------------------------------------
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
PrinterCloudAuthDialog::PrinterCloudAuthDialog(wxWindow* parent, PrintHost* host)
|
||||
: wxDialog((wxWindow*) (wxGetApp().mainframe), wxID_ANY, "Login"), m_host(host)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
// Url
|
||||
host->get_login_url(m_TargetUrl);
|
||||
BOOST_LOG_TRIVIAL(info) << "login url = " << m_TargetUrl.ToStdString();
|
||||
|
||||
// Create the webview
|
||||
m_browser = WebView::CreateWebView(this, m_TargetUrl);
|
||||
if (m_browser == nullptr) {
|
||||
wxLogError("Could not init m_browser");
|
||||
return;
|
||||
}
|
||||
m_browser->Hide();
|
||||
m_browser->SetSize(0, 0);
|
||||
|
||||
// Connect the webview events
|
||||
Bind(wxEVT_WEBVIEW_NAVIGATING, &PrinterCloudAuthDialog::OnNavigationRequest, this, m_browser->GetId());
|
||||
Bind(wxEVT_WEBVIEW_NAVIGATED, &PrinterCloudAuthDialog::OnNavigationComplete, this, m_browser->GetId());
|
||||
Bind(wxEVT_WEBVIEW_LOADED, &PrinterCloudAuthDialog::OnDocumentLoaded, this, m_browser->GetId());
|
||||
Bind(wxEVT_WEBVIEW_NEWWINDOW, &PrinterCloudAuthDialog::OnNewWindow, this, m_browser->GetId());
|
||||
Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &PrinterCloudAuthDialog::OnScriptMessage, this, m_browser->GetId());
|
||||
|
||||
// UI
|
||||
SetTitle(_L("Login"));
|
||||
// Set a more sensible size for web browsing
|
||||
wxSize pSize = FromDIP(wxSize(650, 840));
|
||||
SetSize(pSize);
|
||||
|
||||
int screenheight = wxSystemSettings::GetMetric(wxSYS_SCREEN_Y, NULL);
|
||||
int screenwidth = wxSystemSettings::GetMetric(wxSYS_SCREEN_X, NULL);
|
||||
int MaxY = (screenheight - pSize.y) > 0 ? (screenheight - pSize.y) / 2 : 0;
|
||||
wxPoint tmpPT((screenwidth - pSize.x) / 2, MaxY);
|
||||
Move(tmpPT);
|
||||
}
|
||||
|
||||
PrinterCloudAuthDialog::~PrinterCloudAuthDialog() {}
|
||||
|
||||
void PrinterCloudAuthDialog::OnNavigationRequest(wxWebViewEvent& evt)
|
||||
{
|
||||
//todo
|
||||
}
|
||||
|
||||
void PrinterCloudAuthDialog::OnNavigationComplete(wxWebViewEvent& evt)
|
||||
{
|
||||
m_browser->Show();
|
||||
Layout();
|
||||
//fortest
|
||||
//WebView::RunScript(m_browser, "window.wx.postMessage('This is a web message')");
|
||||
}
|
||||
|
||||
void PrinterCloudAuthDialog::OnDocumentLoaded(wxWebViewEvent& evt)
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
void PrinterCloudAuthDialog::OnNewWindow(wxWebViewEvent& evt) {
|
||||
|
||||
}
|
||||
|
||||
void PrinterCloudAuthDialog::OnScriptMessage(wxWebViewEvent& evt)
|
||||
{
|
||||
wxString str_input = evt.GetString();
|
||||
try {
|
||||
json j = json::parse(into_u8(str_input));
|
||||
wxString strCmd = j["command"];
|
||||
if (strCmd == "login_token") {
|
||||
auto token = j["data"]["token"];
|
||||
m_host->set_api_key(token);
|
||||
m_apikey = token;
|
||||
}
|
||||
Close();
|
||||
} catch (std::exception& e) {
|
||||
wxMessageBox(e.what(), "parse json failed", wxICON_WARNING);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef slic3r_GUI_PrinterCloudAuthDialog_hpp_
|
||||
#define slic3r_GUI_PrinterCloudAuthDialog_hpp_
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/dialog.h>
|
||||
#include "wx/webview.h"
|
||||
|
||||
#if wxUSE_WEBVIEW_IE
|
||||
#include "wx/msw/webview_ie.h"
|
||||
#endif
|
||||
#if wxUSE_WEBVIEW_EDGE
|
||||
#include "wx/msw/webview_edge.h"
|
||||
#endif
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "PrintHost.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class PrinterCloudAuthDialog : public wxDialog
|
||||
{
|
||||
protected:
|
||||
wxWebView* m_browser;
|
||||
wxString m_TargetUrl;
|
||||
|
||||
wxString m_javascript;
|
||||
wxString m_response_js;
|
||||
PrintHost* m_host;
|
||||
std::string m_apikey;
|
||||
|
||||
public:
|
||||
PrinterCloudAuthDialog(wxWindow* parent, PrintHost* host);
|
||||
~PrinterCloudAuthDialog();
|
||||
|
||||
std::string GetApiKey() { return m_apikey; };
|
||||
|
||||
void OnNavigationRequest(wxWebViewEvent& evt);
|
||||
void OnNavigationComplete(wxWebViewEvent& evt);
|
||||
void OnDocumentLoaded(wxWebViewEvent& evt);
|
||||
void OnNewWindow(wxWebViewEvent& evt);
|
||||
void OnScriptMessage(wxWebViewEvent& evt);
|
||||
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif
|
||||
+33
-3
@@ -1551,6 +1551,24 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
||||
}
|
||||
}
|
||||
|
||||
if(opt_key == "make_overhang_printable"){
|
||||
if(m_config->opt_bool("make_overhang_printable")){
|
||||
wxString msg_text = _(
|
||||
L("Enabling this option will modify the model's shape. If your print requires precise dimensions or is part of an "
|
||||
"assembly, it's important to double-check whether this change in geometry impacts the functionality of your print."));
|
||||
msg_text += "\n\n" + _(L("Are you sure you want to enable this option?"));
|
||||
MessageDialog dialog(wxGetApp().plater(), msg_text, "", wxICON_WARNING | wxYES | wxNO);
|
||||
dialog.SetButtonLabel(wxID_YES, _L("Enable"));
|
||||
dialog.SetButtonLabel(wxID_NO, _L("Cancel"));
|
||||
if (dialog.ShowModal() == wxID_NO) {
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
new_conf.set_key_value("make_overhang_printable", new ConfigOptionBool(false));
|
||||
m_config_manipulation.apply(m_config, &new_conf);
|
||||
wxGetApp().plater()->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(opt_key=="layer_height"){
|
||||
auto min_layer_height_from_nozzle=wxGetApp().preset_bundle->full_config().option<ConfigOptionFloats>("min_layer_height")->values;
|
||||
auto max_layer_height_from_nozzle=wxGetApp().preset_bundle->full_config().option<ConfigOptionFloats>("max_layer_height")->values;
|
||||
@@ -1958,6 +1976,12 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("seam_position", "seam");
|
||||
optgroup->append_single_option_line("staggered_inner_seams", "seam");
|
||||
optgroup->append_single_option_line("seam_gap","seam");
|
||||
optgroup->append_single_option_line("seam_slope_type", "seam#scarf-joint-seam");
|
||||
optgroup->append_single_option_line("seam_slope_start_height", "seam#scarf-joint-seam");
|
||||
optgroup->append_single_option_line("seam_slope_entire_loop", "seam#scarf-joint-seam");
|
||||
optgroup->append_single_option_line("seam_slope_min_length", "seam#scarf-joint-seam");
|
||||
optgroup->append_single_option_line("seam_slope_steps", "seam#scarf-joint-seam");
|
||||
optgroup->append_single_option_line("seam_slope_inner_walls", "seam#scarf-joint-seam");
|
||||
optgroup->append_single_option_line("role_based_wipe_speed","seam");
|
||||
optgroup->append_single_option_line("wipe_speed", "seam");
|
||||
optgroup->append_single_option_line("wipe_on_loops","seam");
|
||||
@@ -1999,6 +2023,7 @@ void TabPrint::build()
|
||||
optgroup = page->new_optgroup(L("Walls and surfaces"), L"param_advanced");
|
||||
optgroup->append_single_option_line("wall_sequence");
|
||||
optgroup->append_single_option_line("is_infill_first");
|
||||
optgroup->append_single_option_line("wall_direction");
|
||||
optgroup->append_single_option_line("print_flow_ratio");
|
||||
optgroup->append_single_option_line("top_solid_infill_flow_ratio");
|
||||
optgroup->append_single_option_line("bottom_solid_infill_flow_ratio");
|
||||
@@ -2008,13 +2033,12 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("reduce_crossing_wall");
|
||||
optgroup->append_single_option_line("max_travel_detour_distance");
|
||||
|
||||
optgroup = page->new_optgroup(L("Small Area Infill Flow Compensation (experimental)"), L"param_advanced");
|
||||
optgroup->append_single_option_line("small_area_infill_flow_compensation");
|
||||
optgroup->append_single_option_line("small_area_infill_flow_compensation", "small-area-infill-flow-compensation");
|
||||
Option option = optgroup->get_option("small_area_infill_flow_compensation_model");
|
||||
option.opt.full_width = true;
|
||||
option.opt.is_code = true;
|
||||
option.opt.height = 15;
|
||||
optgroup->append_single_option_line(option);
|
||||
optgroup->append_single_option_line(option, "small-area-infill-flow-compensation");
|
||||
|
||||
optgroup = page->new_optgroup(L("Bridging"), L"param_advanced");
|
||||
optgroup->append_single_option_line("bridge_flow");
|
||||
@@ -3543,6 +3567,12 @@ void TabPrinter::build_fff()
|
||||
optgroup->append_single_option_line("extruder_clearance_height_to_rod");
|
||||
optgroup->append_single_option_line("extruder_clearance_height_to_lid");
|
||||
|
||||
optgroup = page->new_optgroup(L("Adaptive bed mesh"));
|
||||
optgroup->append_single_option_line("bed_mesh_min", "adaptive_bed_mesh");
|
||||
optgroup->append_single_option_line("bed_mesh_max", "adaptive_bed_mesh");
|
||||
optgroup->append_single_option_line("bed_mesh_probe_distance", "adaptive_bed_mesh");
|
||||
optgroup->append_single_option_line("adaptive_bed_mesh_margin", "adaptive_bed_mesh");
|
||||
|
||||
optgroup = page->new_optgroup(L("Accessory") /*, L"param_accessory"*/);
|
||||
optgroup->append_single_option_line("nozzle_type");
|
||||
optgroup->append_single_option_line("nozzle_hrc");
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
#include "Obico.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <wx/progdlg.h>
|
||||
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "Http.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "Bonjour.hpp"
|
||||
#include "slic3r/GUI/BonjourDialog.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace pt = boost::property_tree;
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
Obico::Obico(DynamicPrintConfig* config) :
|
||||
m_host(config->opt_string("print_host")),
|
||||
m_web_ui(config->opt_string("print_host_webui")),
|
||||
m_cafile(config->opt_string("printhost_cafile")),
|
||||
m_port(config->opt_string("printhost_port")),
|
||||
m_apikey(config->opt_string("printhost_apikey")),
|
||||
m_ssl_revoke_best_effort(config->opt_bool("printhost_ssl_ignore_revoke"))
|
||||
{}
|
||||
|
||||
const char* Obico::get_name() const { return "Obico"; }
|
||||
|
||||
void Obico::set_api_key(const std::string auth_api_key) { m_apikey = auth_api_key; }
|
||||
|
||||
std::string Obico::get_host() const {
|
||||
return m_host;
|
||||
}
|
||||
void Obico::set_auth(Http& http) const
|
||||
{
|
||||
http.header("Authorization", "Bearer " + m_apikey);
|
||||
if (!m_cafile.empty()) {
|
||||
http.ca_file(m_cafile);
|
||||
}
|
||||
}
|
||||
|
||||
bool Obico::get_login_url(wxString& auth_url) const
|
||||
{
|
||||
auth_url = make_url("o/authorize?response_type=token&client_id=OrcaSlicer&hide_navbar=true");
|
||||
return true;
|
||||
}
|
||||
|
||||
wxString Obico::get_test_ok_msg() const { return _(L("Connected to Obico successfully!")); }
|
||||
|
||||
wxString Obico::get_test_failed_msg(wxString& msg) const
|
||||
{
|
||||
return GUI::format_wxstr("%s: %s", _L("Could not connect to Obico"), msg.Truncate(256));
|
||||
}
|
||||
|
||||
bool Obico::test(wxString& msg) const
|
||||
{
|
||||
if (m_apikey.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = true;
|
||||
const char* name = get_name();
|
||||
auto url = make_url("api/v1/version/");
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
||||
// Here we do not have to add custom "Host" header - the url contains host filled by user and libCurl will set the header by itself.
|
||||
auto http = Http::get(std::move(url));
|
||||
set_auth(http);
|
||||
http.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status %
|
||||
body;
|
||||
res = false;
|
||||
msg = format_error(body, error, status);
|
||||
})
|
||||
.on_complete([&, this](std::string body, unsigned) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body;
|
||||
})
|
||||
#ifdef WIN32
|
||||
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
|
||||
.on_ip_resolve([&](std::string address) {
|
||||
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
||||
// Remember resolved address to be reused at successive REST API call.
|
||||
msg = GUI::from_u8(address);
|
||||
})
|
||||
#endif // WIN32
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Obico::get_printers(wxArrayString& printers) const
|
||||
{
|
||||
const char* name = get_name();
|
||||
bool res = false;
|
||||
auto url = make_url("api/v1/printers/");
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: List printers at: %2%") % name % url;
|
||||
|
||||
auto http = Http::get(std::move(url));
|
||||
set_auth(http);
|
||||
|
||||
http.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error listing printers: %2%, HTTP %3%, body: `%4%`") % name % error % status %
|
||||
body;
|
||||
})
|
||||
.on_complete([&](std::string body, unsigned http_status) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got printers: %2%, HTTP status: %3%") % name % body % http_status;
|
||||
if (http_status != 200)
|
||||
throw HostNetworkError(GUI::format(_L("HTTP status: %1%\nMessage body: \"%2%\""), http_status, body));
|
||||
|
||||
std::stringstream ss(body);
|
||||
pt::ptree ptree;
|
||||
try {
|
||||
pt::read_json(ss, ptree);
|
||||
} catch (const pt::ptree_error& err) {
|
||||
throw HostNetworkError(
|
||||
GUI::format(_L("Parsing of host response failed.\nMessage body: \"%1%\"\nError: \"%2%\""), body, err.what()));
|
||||
}
|
||||
|
||||
const auto error = ptree.get_optional<std::string>("error");
|
||||
if (error)
|
||||
throw HostNetworkError(*error);
|
||||
|
||||
try {
|
||||
BOOST_FOREACH (boost::property_tree::ptree::value_type& v, ptree) {
|
||||
const auto name = v.second.get<std::string>("name");
|
||||
const auto port = v.second.get<std::string>("id");
|
||||
printers.push_back(Slic3r::GUI::from_u8(name + " [" + port + "]"));
|
||||
}
|
||||
} catch (const std::exception& err) {
|
||||
throw HostNetworkError(
|
||||
GUI::format(_L("Enumeration of host printers failed.\nMessage body: \"%1%\"\nError: \"%2%\""), body, err.what()));
|
||||
}
|
||||
res = true;
|
||||
})
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
PrintHostPostUploadActions Obico::get_post_upload_actions() const {
|
||||
return PrintHostPostUploadAction::StartPrint;
|
||||
}
|
||||
|
||||
bool Obico::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
||||
{
|
||||
const char* name = get_name();
|
||||
const auto upload_filename = upload_data.upload_path.filename();
|
||||
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||
wxString test_msg;
|
||||
if (!test(test_msg)) {
|
||||
error_fn(std::move(test_msg));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = true;
|
||||
auto url = make_url("api/v1/g_code_files/");
|
||||
|
||||
auto http = Http::post(url); // std::move(url));
|
||||
set_auth(http);
|
||||
http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
|
||||
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
||||
.form_add("printer_id", m_port)
|
||||
.form_add("filename", upload_filename.string())
|
||||
.form_add_file("file", upload_data.source_path.string(), upload_filename.string())
|
||||
|
||||
.on_complete([&](std::string body, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body;
|
||||
})
|
||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file to %2%: %3%, HTTP %4%, body: `%5%`") % name % url % error %
|
||||
status % body;
|
||||
error_fn(format_error(body, error, status));
|
||||
res = false;
|
||||
})
|
||||
.on_progress([&](Http::Progress progress, bool& cancel) {
|
||||
prorgess_fn(std::move(progress), cancel);
|
||||
if (cancel) {
|
||||
// Upload was canceled
|
||||
BOOST_LOG_TRIVIAL(info) << name << ": Upload canceled";
|
||||
res = false;
|
||||
}
|
||||
})
|
||||
#ifdef WIN32
|
||||
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
|
||||
#endif
|
||||
.perform_sync();
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string Obico::make_url(const std::string& path) const
|
||||
{
|
||||
if (m_host.find("http://") == 0 || m_host.find("https://") == 0) {
|
||||
if (m_host.back() == '/') {
|
||||
return (boost::format("%1%%2%") % m_host % path).str();
|
||||
} else {
|
||||
return (boost::format("%1%/%2%") % m_host % path).str();
|
||||
}
|
||||
} else {
|
||||
return (boost::format("http://%1%/%2%") % m_host % path).str();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef slic3r_Obico_hpp_
|
||||
#define slic3r_Obico_hpp_
|
||||
|
||||
#include <string>
|
||||
#include <wx/string.h>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
|
||||
#include "PrintHost.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class DynamicPrintConfig;
|
||||
class Http;
|
||||
class Obico : public PrintHost
|
||||
{
|
||||
public:
|
||||
Obico(DynamicPrintConfig* config);
|
||||
~Obico() override = default;
|
||||
|
||||
const char* get_name() const override;
|
||||
virtual bool can_test() const { return true; };
|
||||
bool has_auto_discovery() const override { return false; }
|
||||
bool is_cloud() const override { return true; }
|
||||
bool get_login_url(wxString& auth_url) const override;
|
||||
void set_api_key(const std::string auth_api_key) override;
|
||||
std::string get_host() const override;
|
||||
|
||||
wxString get_test_ok_msg() const override;
|
||||
wxString get_test_failed_msg(wxString& msg) const override;
|
||||
virtual bool test(wxString& curl_msg) const override;
|
||||
bool get_printers(wxArrayString& printers) const override;
|
||||
PrintHostPostUploadActions get_post_upload_actions() const;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override;
|
||||
|
||||
protected:
|
||||
virtual void set_auth(Http& http) const;
|
||||
private:
|
||||
std::string m_host;
|
||||
std::string m_port;
|
||||
std::string m_apikey;
|
||||
std::string m_cafile;
|
||||
std::string m_web_ui;
|
||||
bool m_ssl_revoke_best_effort;
|
||||
|
||||
std::string make_url(const std::string& path) const;
|
||||
};
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "Repetier.hpp"
|
||||
#include "MKS.hpp"
|
||||
#include "../GUI/PrintHostDialogs.hpp"
|
||||
#include "Obico.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
using boost::optional;
|
||||
@@ -54,6 +55,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
|
||||
case htPrusaLink: return new PrusaLink(config);
|
||||
case htPrusaConnect: return new PrusaConnect(config);
|
||||
case htMKS: return new MKS(config);
|
||||
case htObico: return new Obico(config);
|
||||
default: return nullptr;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -70,6 +70,11 @@ public:
|
||||
|
||||
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
||||
|
||||
//Support for cloud webui login
|
||||
virtual bool is_cloud() const { return false; }
|
||||
virtual bool get_login_url(wxString& auth_url) const { return false; }
|
||||
virtual void set_api_key(const std::string auth_api_key) {}
|
||||
|
||||
protected:
|
||||
virtual wxString format_error(const std::string &body, const std::string &error, unsigned status) const;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user