Merge remote-tracking branch 'remote/main' into local-fp

This commit is contained in:
Aidan Case
2024-03-04 14:19:13 -06:00
578 changed files with 72112 additions and 23889 deletions
+10
View File
@@ -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.
+163
View File
@@ -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;
}
+9
View File
@@ -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
+282
View File
@@ -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();
}
+21
View File
@@ -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.
+192
View File
@@ -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
![Image of ImViewGizmo](http://i.imgur.com/7UVcyDd.gif)
### 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
![Image of Rotation](http://i.imgur.com/y4mcVoT.gif)
![Image of Translation](http://i.imgur.com/o8q8iHq.gif)
![Image of Bounds](http://i.imgur.com/3Ez5LBr.gif)
There is now a sample for Win32/OpenGL ! With a binary in bin directory.
![Image of Sample](https://i.imgur.com/nXlzyqD.png)
### ImSequencer
A WIP little sequencer used to edit frame start/end for different events in a timeline.
![Image of Rotation](http://i.imgur.com/BeyNwCn.png)
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.
![Image of GraphEditor](Images/nodeeditor.jpg)
### 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 :
![Image of dialog](http://i.imgur.com/GL5flN1.png)
```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.
+3
View File
@@ -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
+109
View File
@@ -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)
{
+52
View File
@@ -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());
+3
View File
@@ -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
View File
@@ -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());
+2 -1
View File
@@ -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(); }
+54 -17
View File
@@ -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) {
+6 -1
View File
@@ -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);
+2 -2
View File
@@ -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.
+8 -1
View File
@@ -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();
+38 -16
View File
@@ -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);
}
+47
View File
@@ -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
{
+1
View File
@@ -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
View File
@@ -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
+3
View File
@@ -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,
+52 -14
View File
@@ -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;
+17
View File
@@ -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)
+11
View File
@@ -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 {};
}
+18
View File
@@ -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
File diff suppressed because it is too large Load Diff
+35 -2
View File
@@ -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))
)
+12 -1
View File
@@ -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"
+5 -1
View File
@@ -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)
+1
View File
@@ -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" },
+9
View File
@@ -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;
+2
View File
@@ -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; }
+16 -1
View File
@@ -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*/)
-28
View File
@@ -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();
-1
View File
@@ -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);
+8
View File
@@ -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)
+68
View File
@@ -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()
+1
View File
@@ -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);
+3
View File
@@ -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);
+1 -1
View File
@@ -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
+9
View File
@@ -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,
+8 -3
View File
@@ -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)) });
}
+77
View File
@@ -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);
+1
View File
@@ -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;
+105
View File
@@ -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
+51
View File
@@ -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
View File
@@ -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");
+218
View File
@@ -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();
}
}
}
+51
View File
@@ -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
+2
View File
@@ -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 {
+5
View File
@@ -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;
};