mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-30 18:21:58 +08:00
Merge branch 'lm_med'
This commit is contained in:
commit
07032d971d
@ -149,6 +149,8 @@ set(SLIC3R_SOURCES
|
||||
Format/SVG.cpp
|
||||
Format/SLAArchiveFormatRegistry.hpp
|
||||
Format/SLAArchiveFormatRegistry.cpp
|
||||
Format/PrintRequest.cpp
|
||||
Format/PrintRequest.cpp
|
||||
GCode/ThumbnailData.cpp
|
||||
GCode/ThumbnailData.hpp
|
||||
GCode/Thumbnails.cpp
|
||||
@ -481,6 +483,8 @@ set(SLIC3R_SOURCES
|
||||
SLA/DefaultSupportTree.cpp
|
||||
SLA/BranchingTreeSLA.hpp
|
||||
SLA/BranchingTreeSLA.cpp
|
||||
SLA/ZCorrection.hpp
|
||||
SLA/ZCorrection.cpp
|
||||
BranchingTree/BranchingTree.cpp
|
||||
BranchingTree/BranchingTree.hpp
|
||||
BranchingTree/PointCloud.cpp
|
||||
|
161
src/libslic3r/Format/PrintRequest.cpp
Normal file
161
src/libslic3r/Format/PrintRequest.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
#include "PrintRequest.hpp"
|
||||
|
||||
#include <boost/property_tree/xml_parser.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <fast_float/fast_float.h>
|
||||
|
||||
#include "libslic3r/Exception.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Format/STL.hpp"
|
||||
|
||||
//#include "slic3r/GUI/format.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace pt = boost::property_tree;
|
||||
namespace {
|
||||
void read_file(const char* input_file, pt::ptree& tree)
|
||||
{
|
||||
boost::filesystem::path path(input_file);
|
||||
|
||||
boost::nowide::ifstream ifs(path.string());
|
||||
try {
|
||||
pt::read_xml(ifs, tree);
|
||||
}
|
||||
catch (const boost::property_tree::xml_parser::xml_parser_error& err) {
|
||||
throw Slic3r::RuntimeError("Failed reading PrintRequest file. File format is corrupt.");
|
||||
}
|
||||
}
|
||||
|
||||
void read_tree(const boost::property_tree::ptree::value_type& section, boost::filesystem::path& model_path, std::string& material, std::string& material_color, std::vector<std::string>& transformation_matrix)
|
||||
{
|
||||
for (const auto& data : section.second) {
|
||||
if (data.first == "Path") {
|
||||
model_path = boost::filesystem::path(data.second.data());
|
||||
}
|
||||
else if (data.first == "Material") {
|
||||
material = data.second.data();
|
||||
}
|
||||
else if (data.first == "MaterialColor") {
|
||||
material_color = data.second.data();
|
||||
}
|
||||
else if (data.first == "TransformationMatrix") {
|
||||
transformation_matrix.reserve(16);
|
||||
for (const auto& element : data.second) {
|
||||
transformation_matrix.emplace_back(element.second.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool fill_model(Model* model, const boost::filesystem::path& model_path, const std::string& material, const std::vector<std::string>& transformation_matrix)
|
||||
{
|
||||
if (!boost::filesystem::exists(model_path))
|
||||
throw Slic3r::RuntimeError("Failed reading PrintRequest file. Path doesn't exists. " + model_path.string());
|
||||
if (!boost::algorithm::iends_with(model_path.string(), ".stl"))
|
||||
throw Slic3r::RuntimeError("Failed reading PrintRequest file. Path is not stl file. " + model_path.string());
|
||||
bool result = load_stl(model_path.string().c_str(), model);
|
||||
if (!material.empty()) {
|
||||
model->objects.back()->volumes.front()->set_material_id(material);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void add_instance(Model* model, const boost::filesystem::path& model_path, const std::vector<std::string>& transformation_matrix)
|
||||
{
|
||||
if (transformation_matrix.size() >= 16) {
|
||||
|
||||
auto string_to_double = [model_path](const std::string& from) -> double {
|
||||
double ret_val;
|
||||
auto answer = fast_float::from_chars(from.data(), from.data() + from.size(), ret_val);
|
||||
if (answer.ec != std::errc())
|
||||
throw Slic3r::RuntimeError("Failed reading PrintRequest file. Couldn't parse transformation matrix. " + model_path.string());
|
||||
return ret_val;
|
||||
};
|
||||
|
||||
Vec3d offset_vector;
|
||||
Slic3r::Transform3d matrix;
|
||||
try
|
||||
{
|
||||
offset_vector = Slic3r::Vec3d(string_to_double(transformation_matrix[3]), string_to_double(transformation_matrix[7]), string_to_double(transformation_matrix[11]));
|
||||
// PrintRequest is row-major 4x4, Slic3r::Transform3d (Eigen) is column-major by default 3x3
|
||||
matrix(0, 0) = string_to_double(transformation_matrix[0]);
|
||||
matrix(1, 0) = string_to_double(transformation_matrix[1]);
|
||||
matrix(2, 0) = string_to_double(transformation_matrix[2]);
|
||||
matrix(0, 1) = string_to_double(transformation_matrix[4]);
|
||||
matrix(1, 1) = string_to_double(transformation_matrix[5]);
|
||||
matrix(2, 1) = string_to_double(transformation_matrix[6]);
|
||||
matrix(0, 2) = string_to_double(transformation_matrix[8]);
|
||||
matrix(1, 2) = string_to_double(transformation_matrix[9]);
|
||||
matrix(2, 2) = string_to_double(transformation_matrix[10]);
|
||||
}
|
||||
catch (const Slic3r::RuntimeError& e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
||||
ModelObject* object = model->objects.back();
|
||||
Slic3r::Geometry::Transformation transformation(matrix);
|
||||
transformation.set_offset(offset_vector);
|
||||
object->add_instance(transformation);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool load_printRequest(const char* input_file, Model* model)
|
||||
{
|
||||
pt::ptree tree;
|
||||
try
|
||||
{
|
||||
read_file(input_file, tree);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
|
||||
for (const auto& section0 : tree) {
|
||||
if (section0.first != "PrintRequest")
|
||||
continue;
|
||||
if (section0.second.empty())
|
||||
continue;
|
||||
for (const auto& section1 : section0.second) {
|
||||
if (section1.first != "Files")
|
||||
continue;
|
||||
if (section1.second.empty())
|
||||
continue;
|
||||
for (const auto& section2 : section1.second) {
|
||||
if (section2.first != "File")
|
||||
continue;
|
||||
if (section2.second.empty())
|
||||
continue;
|
||||
boost::filesystem::path model_path;
|
||||
std::string material;
|
||||
std::string material_color;
|
||||
std::vector<std::string> transformation_matrix;
|
||||
|
||||
try
|
||||
{
|
||||
read_tree(section2, model_path, material, material_color, transformation_matrix);
|
||||
result = result && fill_model(model, model_path, material, transformation_matrix);
|
||||
if (!result)
|
||||
return false;
|
||||
add_instance(model, model_path, transformation_matrix);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
12
src/libslic3r/Format/PrintRequest.hpp
Normal file
12
src/libslic3r/Format/PrintRequest.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef slic3r_Format_PrintRequest_hpp_
|
||||
#define slic3r_Format_PrintRequest_hpp_
|
||||
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
class Model;
|
||||
bool load_printRequest(const char* input_file, Model* model);
|
||||
|
||||
} //namespace Slic3r
|
||||
|
||||
#endif
|
@ -28,6 +28,7 @@
|
||||
#include "Format/3mf.hpp"
|
||||
#include "Format/STEP.hpp"
|
||||
#include "Format/SVG.hpp"
|
||||
#include "Format/PrintRequest.hpp"
|
||||
|
||||
#include <float.h>
|
||||
|
||||
@ -140,6 +141,8 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
|
||||
result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, false);
|
||||
else if (boost::algorithm::iends_with(input_file, ".svg"))
|
||||
result = load_svg(input_file, model);
|
||||
else if (boost::ends_with(input_file, ".printRequest"))
|
||||
result = load_printRequest(input_file.c_str(), &model);
|
||||
else
|
||||
throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .step/.stp, .svg, .amf(.xml) or extension .3mf(.zip).");
|
||||
|
||||
@ -148,9 +151,10 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
|
||||
|
||||
if (model.objects.empty())
|
||||
throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty");
|
||||
|
||||
for (ModelObject *o : model.objects)
|
||||
o->input_file = input_file;
|
||||
|
||||
if (!boost::ends_with(input_file, ".printRequest"))
|
||||
for (ModelObject *o : model.objects)
|
||||
o->input_file = input_file;
|
||||
|
||||
if (options & LoadAttribute::AddDefaultInstances)
|
||||
model.add_default_instances();
|
||||
|
@ -609,6 +609,7 @@ static std::vector<std::string> s_Preset_sla_material_options {
|
||||
"material_print_speed",
|
||||
"area_fill",
|
||||
"default_sla_material_profile",
|
||||
"zcorrection_layers",
|
||||
"compatible_prints", "compatible_prints_condition",
|
||||
"compatible_printers", "compatible_printers_condition", "inherits",
|
||||
|
||||
@ -1213,6 +1214,20 @@ const std::string& PresetCollection::get_preset_name_by_alias(const std::string&
|
||||
return alias;
|
||||
}
|
||||
|
||||
const std::string& PresetCollection::get_preset_name_by_alias_invisible(const std::string& alias) const
|
||||
{
|
||||
for (
|
||||
// Find the 1st profile name with the alias.
|
||||
auto it = Slic3r::lower_bound_by_predicate(m_map_alias_to_profile_name.begin(), m_map_alias_to_profile_name.end(), [&alias](auto& l) { return l.first < alias; });
|
||||
// Continue over all profile names with the same alias.
|
||||
it != m_map_alias_to_profile_name.end() && it->first == alias; ++it)
|
||||
if (auto it_preset = this->find_preset_internal(it->second);
|
||||
it_preset != m_presets.end() && it_preset->name == it->second &&
|
||||
it_preset->is_compatible)
|
||||
return it_preset->name;
|
||||
return alias;
|
||||
}
|
||||
|
||||
const std::string* PresetCollection::get_preset_name_renamed(const std::string &old_name) const
|
||||
{
|
||||
auto it_renamed = m_map_system_profile_renamed.find(old_name);
|
||||
@ -1478,13 +1493,13 @@ Preset& PresetCollection::select_preset(size_t idx)
|
||||
return m_presets[idx];
|
||||
}
|
||||
|
||||
bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, bool force)
|
||||
bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, bool force, bool force_invisible /*= false*/)
|
||||
{
|
||||
std::string name = Preset::remove_suffix_modified(name_w_suffix);
|
||||
// 1) Try to find the preset by its name.
|
||||
auto it = this->find_preset_internal(name);
|
||||
size_t idx = 0;
|
||||
if (it != m_presets.end() && it->name == name && it->is_visible)
|
||||
if (it != m_presets.end() && it->name == name && (force_invisible || it->is_visible))
|
||||
// Preset found by its name and it is visible.
|
||||
idx = it - m_presets.begin();
|
||||
else {
|
||||
|
@ -410,6 +410,7 @@ public:
|
||||
PresetWithVendorProfile get_edited_preset_with_vendor_profile() const { return this->get_preset_with_vendor_profile(this->get_edited_preset()); }
|
||||
|
||||
const std::string& get_preset_name_by_alias(const std::string& alias) const;
|
||||
const std::string& get_preset_name_by_alias_invisible(const std::string& alias) const;
|
||||
const std::string* get_preset_name_renamed(const std::string &old_name) const;
|
||||
|
||||
// used to update preset_choice from Tab
|
||||
@ -528,7 +529,8 @@ public:
|
||||
// Select a profile by its name. Return true if the selection changed.
|
||||
// Without force, the selection is only updated if the index changes.
|
||||
// With force, the changes are reverted if the new index is the same as the old index.
|
||||
bool select_preset_by_name(const std::string &name, bool force);
|
||||
// With force_invisible, force preset selection even it's invisible.
|
||||
bool select_preset_by_name(const std::string &name, bool force, bool force_invisible = false);
|
||||
|
||||
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
|
||||
std::string path_from_name(const std::string &new_name) const;
|
||||
|
@ -496,6 +496,20 @@ const std::string& PresetBundle::get_preset_name_by_alias( const Preset::Type& p
|
||||
return presets.get_preset_name_by_alias(alias);
|
||||
}
|
||||
|
||||
const std::string& PresetBundle::get_preset_name_by_alias_invisible(const Preset::Type& preset_type, const std::string& alias) const
|
||||
{
|
||||
// there are not aliases for Printers profiles
|
||||
if (preset_type == Preset::TYPE_PRINTER || preset_type == Preset::TYPE_INVALID)
|
||||
return alias;
|
||||
|
||||
const PresetCollection& presets = preset_type == Preset::TYPE_PRINT ? prints :
|
||||
preset_type == Preset::TYPE_SLA_PRINT ? sla_prints :
|
||||
preset_type == Preset::TYPE_FILAMENT ? filaments :
|
||||
sla_materials;
|
||||
|
||||
return presets.get_preset_name_by_alias_invisible(alias);
|
||||
}
|
||||
|
||||
void PresetBundle::save_changes_for_preset(const std::string& new_name, Preset::Type type,
|
||||
const std::vector<std::string>& unselected_options)
|
||||
{
|
||||
|
@ -182,6 +182,7 @@ public:
|
||||
void load_installed_printers(const AppConfig &config);
|
||||
|
||||
const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias, int extruder_id = -1);
|
||||
const std::string& get_preset_name_by_alias_invisible(const Preset::Type& preset_type, const std::string& alias) const;
|
||||
|
||||
// Save current preset of a provided type under a new name. If the name is different from the old one,
|
||||
// Unselected option would be reverted to the beginning values
|
||||
|
@ -4042,6 +4042,14 @@ void PrintConfigDef::init_sla_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.2));
|
||||
|
||||
def = this->add("zcorrection_layers", coInt);
|
||||
def->label = L("Z compensation");
|
||||
def->category = L("Advanced");
|
||||
def->tooltip = L("Number of layers to Z correct to avoid cross layer bleed");
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(0));
|
||||
|
||||
def = this->add("gamma_correction", coFloat);
|
||||
def->label = L("Printer gamma correction");
|
||||
def->full_label = L("Printer gamma correction");
|
||||
|
@ -1174,6 +1174,8 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, material_correction_y))
|
||||
((ConfigOptionFloat, material_correction_z))
|
||||
((ConfigOptionEnum<SLAMaterialSpeed>, material_print_speed))
|
||||
((ConfigOptionInt, zcorrection_layers))
|
||||
|
||||
((ConfigOptionFloatNullable, material_ow_support_pillar_diameter))
|
||||
((ConfigOptionFloatNullable, material_ow_branchingsupport_pillar_diameter))
|
||||
((ConfigOptionFloatNullable, material_ow_support_head_front_diameter))
|
||||
|
130
src/libslic3r/SLA/ZCorrection.cpp
Normal file
130
src/libslic3r/SLA/ZCorrection.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
#include "ZCorrection.hpp"
|
||||
|
||||
#include "Execution/ExecutionTBB.hpp"
|
||||
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
|
||||
namespace Slic3r { namespace sla {
|
||||
|
||||
std::vector<ExPolygons> apply_zcorrection(
|
||||
const std::vector<ExPolygons> &slices, size_t layers)
|
||||
{
|
||||
return zcorr_detail::apply_zcorrection(ex_tbb, slices, layers);
|
||||
}
|
||||
|
||||
std::vector<ExPolygons> apply_zcorrection(const std::vector<ExPolygons> &slices,
|
||||
const std::vector<float> &grid,
|
||||
float depth)
|
||||
{
|
||||
return zcorr_detail::apply_zcorrection(ex_tbb, slices, grid, depth);
|
||||
}
|
||||
|
||||
namespace zcorr_detail {
|
||||
|
||||
DepthMap create_depthmap(const std::vector<ExPolygons> &slices,
|
||||
const std::vector<float> &grid,
|
||||
size_t max_depth)
|
||||
{
|
||||
struct DepthPoly {
|
||||
size_t depth = 0;
|
||||
ExPolygons contour;
|
||||
};
|
||||
|
||||
DepthMap ret;
|
||||
|
||||
if (slices.empty() || slices.size() != grid.size())
|
||||
return ret;
|
||||
|
||||
size_t depth_limit = max_depth > 0 ? max_depth : slices.size();
|
||||
ret.resize(slices.size());
|
||||
|
||||
ret.front() = DepthMapLayer{ {size_t{0}, slices.front()} };
|
||||
|
||||
for (size_t i = 0; i < slices.size() - 1; ++i) {
|
||||
DepthMapLayer &depths_current = ret[i];
|
||||
DepthMapLayer &depths_nxt = ret[i + 1];
|
||||
|
||||
for (const auto &[depth, cntrs] : depths_current) {
|
||||
DepthPoly common;
|
||||
|
||||
common.contour = intersection_ex(slices[i + 1], cntrs);
|
||||
common.depth = std::min(depth_limit, depth + 1);
|
||||
|
||||
DepthPoly overhangs;
|
||||
overhangs.contour = diff_ex(slices[i + 1], cntrs);
|
||||
|
||||
if (!common.contour.empty()) {
|
||||
std::copy(common.contour.begin(), common.contour.end(),
|
||||
std::back_inserter(depths_nxt[common.depth]));
|
||||
}
|
||||
|
||||
if (!overhangs.contour.empty()) {
|
||||
std::copy(overhangs.contour.begin(), overhangs.contour.end(),
|
||||
std::back_inserter(depths_nxt[overhangs.depth]));
|
||||
}
|
||||
}
|
||||
|
||||
for(auto &[i, cntrs] : depths_nxt) {
|
||||
depths_nxt[i] = union_ex(cntrs);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void apply_zcorrection(DepthMap &dmap, size_t layers)
|
||||
{
|
||||
for (size_t lyr = 0; lyr < dmap.size(); ++lyr) {
|
||||
size_t threshold = std::min(lyr, layers);
|
||||
|
||||
auto &dlayer = dmap[lyr];
|
||||
|
||||
for (auto it = dlayer.begin(); it != dlayer.end();)
|
||||
if (it->first < threshold)
|
||||
it = dlayer.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
ExPolygons merged_layer(const DepthMapLayer &dlayer)
|
||||
{
|
||||
using namespace Slic3r;
|
||||
|
||||
ExPolygons out;
|
||||
for (auto &[i, cntrs] : dlayer) {
|
||||
std::copy(cntrs.begin(), cntrs.end(), std::back_inserter(out));
|
||||
}
|
||||
|
||||
out = union_ex(out);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<ExPolygons> depthmap_to_slices(const DepthMap &dm)
|
||||
{
|
||||
auto out = reserve_vector<ExPolygons>(dm.size());
|
||||
for (const auto &dlayer : dm) {
|
||||
out.emplace_back(merged_layer(dlayer));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
ExPolygons intersect_layers(const std::vector<ExPolygons> &slices,
|
||||
size_t layer_from, size_t layers_down)
|
||||
{
|
||||
size_t drill_to = std::min(layer_from, layers_down);
|
||||
auto drill_to_layer = static_cast<int>(layer_from - drill_to);
|
||||
|
||||
ExPolygons merged_lyr = slices[layer_from];
|
||||
for (int i = layer_from; i >= drill_to_layer; --i)
|
||||
merged_lyr = intersection_ex(merged_lyr, slices[i]);
|
||||
|
||||
return merged_lyr;
|
||||
}
|
||||
|
||||
} // namespace zcorr_detail
|
||||
|
||||
} // namespace sla
|
||||
} // namespace Slic3r
|
84
src/libslic3r/SLA/ZCorrection.hpp
Normal file
84
src/libslic3r/SLA/ZCorrection.hpp
Normal file
@ -0,0 +1,84 @@
|
||||
#ifndef ZCORRECTION_HPP
|
||||
#define ZCORRECTION_HPP
|
||||
|
||||
#include "libslic3r/ExPolygon.hpp"
|
||||
#include "libslic3r/Execution/Execution.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace sla {
|
||||
|
||||
std::vector<ExPolygons> apply_zcorrection(const std::vector<ExPolygons> &slices,
|
||||
size_t layers);
|
||||
|
||||
std::vector<ExPolygons> apply_zcorrection(const std::vector<ExPolygons> &slices,
|
||||
const std::vector<float> &grid,
|
||||
float depth);
|
||||
|
||||
namespace zcorr_detail {
|
||||
|
||||
ExPolygons intersect_layers(const std::vector<ExPolygons> &slices,
|
||||
size_t layer_from,
|
||||
size_t layers_down);
|
||||
|
||||
template<class Ex>
|
||||
std::vector<ExPolygons> apply_zcorrection(Ex ep,
|
||||
const std::vector<ExPolygons> &slices,
|
||||
size_t layers)
|
||||
{
|
||||
std::vector<ExPolygons> output(slices.size());
|
||||
|
||||
execution::for_each(ep, size_t{0}, slices.size(),
|
||||
[&output, &slices, layers] (size_t lyr) {
|
||||
output[lyr] = intersect_layers(slices, lyr, layers);
|
||||
}, execution::max_concurrency(ep));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
inline size_t depth_to_layers(const std::vector<float> &grid,
|
||||
size_t from_layer,
|
||||
float depth)
|
||||
{
|
||||
size_t depth_layers = 0;
|
||||
while (from_layer > depth_layers &&
|
||||
grid[from_layer - depth_layers] > grid[from_layer] - depth)
|
||||
depth_layers++;
|
||||
|
||||
return depth_layers;
|
||||
}
|
||||
|
||||
template<class Ex>
|
||||
std::vector<ExPolygons> apply_zcorrection(Ex ep,
|
||||
const std::vector<ExPolygons> &slices,
|
||||
const std::vector<float> &grid,
|
||||
float depth)
|
||||
{
|
||||
std::vector<ExPolygons> output(slices.size());
|
||||
|
||||
execution::for_each(ep, size_t{0}, slices.size(),
|
||||
[&output, &slices, &grid, depth] (size_t lyr) {
|
||||
output[lyr] = intersect_layers(slices, lyr,
|
||||
depth_to_layers(grid, lyr, depth));
|
||||
}, execution::max_concurrency(ep));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
using DepthMapLayer = std::map<size_t, ExPolygons>;
|
||||
using DepthMap = std::vector<DepthMapLayer>;
|
||||
|
||||
DepthMap create_depthmap(const std::vector<ExPolygons> &slices,
|
||||
const std::vector<float> &grid, size_t max_depth = 0);
|
||||
|
||||
void apply_zcorrection(DepthMap &dmap, size_t layers);
|
||||
|
||||
ExPolygons merged_layer(const DepthMapLayer &dlayer);
|
||||
|
||||
std::vector<ExPolygons> depthmap_to_slices(const DepthMap &dm);
|
||||
|
||||
} // namespace zcorr_detail
|
||||
|
||||
} // namespace sla
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // ZCORRECTION_HPP
|
@ -842,6 +842,7 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
|
||||
"absolute_correction"sv,
|
||||
"elefant_foot_compensation"sv,
|
||||
"elefant_foot_min_width"sv,
|
||||
"zcorrection_layers"sv,
|
||||
"gamma_correction"sv,
|
||||
};
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <libslic3r/Execution/ExecutionTBB.hpp>
|
||||
#include <libslic3r/SLA/Pad.hpp>
|
||||
#include <libslic3r/SLA/SupportPointGenerator.hpp>
|
||||
#include <libslic3r/SLA/ZCorrection.hpp>
|
||||
|
||||
#include <libslic3r/ElephantFootCompensation.hpp>
|
||||
#include <libslic3r/AABBTreeIndirect.hpp>
|
||||
@ -129,6 +130,11 @@ void SLAPrint::Steps::apply_printer_corrections(SLAPrintObject &po, SliceOrigin
|
||||
if (idx < slices.size())
|
||||
slices[idx] = elephant_foot_compensation(slices[idx], min_w, efc(i));
|
||||
}
|
||||
|
||||
if (o == soModel) { // Z correction applies only to the model slices
|
||||
slices = sla::apply_zcorrection(slices,
|
||||
m_print->m_material_config.zcorrection_layers.getInt());
|
||||
}
|
||||
}
|
||||
|
||||
indexed_triangle_set SLAPrint::Steps::generate_preview_vdb(
|
||||
|
@ -766,6 +766,10 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin
|
||||
|
||||
html_window = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition,
|
||||
wxSize(60 * em, 20 * em), wxHW_SCROLLBAR_AUTO);
|
||||
html_window->Bind(wxEVT_HTML_LINK_CLICKED, [](wxHtmlLinkEvent& event) {
|
||||
wxGetApp().open_browser_with_warning_dialog(event.GetLinkInfo().GetHref());
|
||||
event.Skip(false);
|
||||
});
|
||||
append(html_window, 0, wxEXPAND);
|
||||
|
||||
list_printer->Bind(wxEVT_LISTBOX, [this](wxCommandEvent& evt) {
|
||||
@ -855,9 +859,19 @@ void PageMaterials::set_compatible_printers_html_window(const std::vector<std::s
|
||||
// TRN ConfigWizard: Materials : "%1%" = "Filaments"/"SLA materials"
|
||||
text = format_wxstr(_L("%1% visible for <b>(\"Template\")</b> printer are universal profiles available for all printers. These might not be compatible with your printer."), materials->technology == T_FFF ? _L("Filaments") : _L("SLA materials"));
|
||||
} else {
|
||||
bool has_medical = false;
|
||||
for (const Preset *printer : materials->printers) {
|
||||
if (printer->vendor && printer->vendor->id == "PrusaProMedical") {
|
||||
has_medical = true;
|
||||
}
|
||||
}
|
||||
// TRN PrusaSlicer-Medical ConfigWizard: Materials"
|
||||
wxString zero_line = _L("The list of validated workflows for Medical One can be found in this <a href=\"prusa.io/m1-validation\">article</a>. Profiles for other materials are not verified by the material manufacturer and therefore may not correspond to the current version of the material.");
|
||||
if (!has_medical) {
|
||||
zero_line.Clear();
|
||||
}
|
||||
// TRN ConfigWizard: Materials : "%1%" = "Filaments"/"SLA materials"
|
||||
wxString first_line = format_wxstr(_L("%1% marked with <b>*</b> are <b>not</b> compatible with some installed printers."), materials->technology == T_FFF ? _L("Filaments") : _L("SLA materials"));
|
||||
|
||||
if (all_printers) {
|
||||
// TRN ConfigWizard: Materials : "%1%" = "filament"/"SLA material"
|
||||
wxString second_line = format_wxstr(_L("All installed printers are compatible with the selected %1%."), materials->technology == T_FFF ? _L("filament") : _L("SLA material"));
|
||||
@ -868,12 +882,14 @@ void PageMaterials::set_compatible_printers_html_window(const std::vector<std::s
|
||||
"</style>"
|
||||
"<body bgcolor= %s>"
|
||||
"<font color=%s>"
|
||||
"%s<br /><br />%s"
|
||||
"<font size=\"3\">"
|
||||
"%s<br /><br />%s<br /><br />%s"
|
||||
"</font>"
|
||||
"</body>"
|
||||
"</html>"
|
||||
, bgr_clr_str
|
||||
, text_clr_str
|
||||
, zero_line
|
||||
, first_line
|
||||
, second_line
|
||||
);
|
||||
@ -891,13 +907,16 @@ void PageMaterials::set_compatible_printers_html_window(const std::vector<std::s
|
||||
"</style>"
|
||||
"<body bgcolor= %s>"
|
||||
"<font color=%s>"
|
||||
"%s<br /><br />%s"
|
||||
"<font size=\"3\">"
|
||||
"%s<br /><br />%s<br /><br />%s"
|
||||
"<table>"
|
||||
"<tr>"
|
||||
, bgr_clr_str
|
||||
, text_clr_str
|
||||
, zero_line
|
||||
, first_line
|
||||
, second_line);
|
||||
, second_line
|
||||
);
|
||||
for (size_t i = 0; i < printer_names.size(); ++i)
|
||||
{
|
||||
text += wxString::Format("<td>%s</td>", boost::nowide::widen(printer_names[i]));
|
||||
|
@ -105,8 +105,8 @@ void SLAImportJob::prepare()
|
||||
{
|
||||
reset();
|
||||
|
||||
auto path = p->import_dlg->get_path();
|
||||
auto nm = wxFileName(path);
|
||||
const std::string path = p->import_dlg->get_path();
|
||||
auto nm = wxFileName(from_u8(path));
|
||||
p->path = !nm.Exists(wxFILE_EXISTS_REGULAR) ? "" : nm.GetFullPath();
|
||||
if (p->path.empty()) {
|
||||
p->err = _u8L("The file does not exist.");
|
||||
|
@ -300,6 +300,7 @@ struct Plater::priv
|
||||
static const std::regex pattern_any_amf;
|
||||
static const std::regex pattern_prusa;
|
||||
static const std::regex pattern_zip;
|
||||
static const std::regex pattern_printRequest;
|
||||
|
||||
priv(Plater *q, MainFrame *main_frame);
|
||||
~priv();
|
||||
@ -597,6 +598,7 @@ const std::regex Plater::priv::pattern_zip_amf(".*[.]zip[.]amf", std::regex::ica
|
||||
const std::regex Plater::priv::pattern_any_amf(".*[.](amf|amf[.]xml|zip[.]amf)", std::regex::icase);
|
||||
const std::regex Plater::priv::pattern_prusa(".*prusa", std::regex::icase);
|
||||
const std::regex Plater::priv::pattern_zip(".*zip", std::regex::icase);
|
||||
const std::regex Plater::priv::pattern_printRequest(".*printRequest", std::regex::icase);
|
||||
|
||||
Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
: q(q)
|
||||
@ -1198,6 +1200,14 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
const bool type_zip_amf = !type_3mf && std::regex_match(path.string(), pattern_zip_amf);
|
||||
const bool type_any_amf = !type_3mf && std::regex_match(path.string(), pattern_any_amf);
|
||||
const bool type_prusa = std::regex_match(path.string(), pattern_prusa);
|
||||
const bool type_printRequest = std::regex_match(path.string(), pattern_printRequest);
|
||||
|
||||
if (type_printRequest && printer_technology != ptSLA) {
|
||||
Slic3r::GUI::show_info(nullptr,
|
||||
_L("PrintRequest can only be loaded if an SLA printer is selected."),
|
||||
_L("Error!"));
|
||||
continue;
|
||||
}
|
||||
|
||||
Slic3r::Model model;
|
||||
bool is_project_file = type_prusa;
|
||||
@ -1370,7 +1380,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
convert_model_if(model, answer_convert_from_imperial_units == wxID_YES);
|
||||
}
|
||||
|
||||
if (model.looks_like_multipart_object()) {
|
||||
if (!type_printRequest && model.looks_like_multipart_object()) {
|
||||
if (answer_consider_as_multi_part_objects == wxOK_DEFAULT) {
|
||||
RichMessageDialog dlg(q, _L(
|
||||
"This file contains several objects positioned at multiple heights.\n"
|
||||
@ -1409,6 +1419,58 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
}
|
||||
if (!model_object->instances.empty())
|
||||
model_object->ensure_on_bed(is_project_file);
|
||||
if (type_printRequest) {
|
||||
for (ModelInstance* obj_instance : model_object->instances) {
|
||||
obj_instance->set_offset(obj_instance->get_offset() + Slic3r::to_3d(this->bed.build_volume().bed_center(), -model_object->origin_translation(2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type_printRequest) {
|
||||
assert(model.materials.size());
|
||||
|
||||
for (const auto& material : model.materials) {
|
||||
std::string preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias_invisible(Preset::Type::TYPE_SLA_MATERIAL,
|
||||
Preset::remove_suffix_modified(material.first));
|
||||
Preset* prst = wxGetApp().preset_bundle->sla_materials.find_preset(preset_name, false);
|
||||
if (!prst) { //did not find compatible profile
|
||||
// try find alias of material comaptible with another print profile - if exists, use the print profile
|
||||
auto& prints = wxGetApp().preset_bundle->sla_prints;
|
||||
std::string edited_print_name = prints.get_edited_preset().name;
|
||||
bool found = false;
|
||||
for (auto it = prints.begin(); it != prints.end(); ++it)
|
||||
{
|
||||
if (it->name != edited_print_name) {
|
||||
BOOST_LOG_TRIVIAL(error) << it->name;
|
||||
wxGetApp().get_tab(Preset::Type::TYPE_SLA_PRINT)->select_preset(it->name, false);
|
||||
preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias_invisible(Preset::Type::TYPE_SLA_MATERIAL,
|
||||
Preset::remove_suffix_modified(material.first));
|
||||
prst = wxGetApp().preset_bundle->sla_materials.find_preset(preset_name, false);
|
||||
if (prst) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// return to original print profile
|
||||
wxGetApp().get_tab(Preset::Type::TYPE_SLA_PRINT)->select_preset(edited_print_name, false);
|
||||
std::string notif_text = into_u8(_L("Material preset was not loaded:"));
|
||||
notif_text += "\n - " + preset_name;
|
||||
q->get_notification_manager()->push_notification(NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::PrintInfoNotificationLevel, notif_text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
|
||||
if (preset_bundle->sla_materials.get_selected_preset_name() != preset_name) {
|
||||
preset_bundle->sla_materials.select_preset_by_name(preset_name, false, true);
|
||||
preset_bundle->tmp_installed_presets = { preset_name };
|
||||
q->notify_about_installed_presets();
|
||||
wxGetApp().load_current_presets(false);// For this case we shouldn't check printer_presets
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (one_by_one) {
|
||||
@ -4814,7 +4876,7 @@ void ProjectDropDialog::on_dpi_changed(const wxRect& suggested_rect)
|
||||
|
||||
bool Plater::load_files(const wxArrayString& filenames, bool delete_after_load/*=false*/)
|
||||
{
|
||||
const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa|step|stp|zip)", std::regex::icase);
|
||||
const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa|step|stp|zip|printRequest)", std::regex::icase);
|
||||
const std::regex pattern_gcode_drop(".*[.](gcode|g|bgcode|bgc)", std::regex::icase);
|
||||
|
||||
std::vector<fs::path> paths;
|
||||
|
@ -5341,7 +5341,16 @@ void TabSLAMaterial::build()
|
||||
opt.opt.label = axis;
|
||||
line.append_option(opt);
|
||||
}
|
||||
optgroup->append_line(line);
|
||||
|
||||
optgroup->append_single_option_line("zcorrection_layers");
|
||||
|
||||
line = Line{ "", "" };
|
||||
line.full_width = 1;
|
||||
// line.label_path = category_path + "recommended-thin-wall-thickness";
|
||||
line.widget = [this](wxWindow* parent) {
|
||||
return description_line_widget(parent, &m_z_correction_to_mm_description);
|
||||
};
|
||||
optgroup->append_line(line);
|
||||
|
||||
add_material_overrides_page();
|
||||
@ -5491,7 +5500,19 @@ void TabSLAMaterial::update()
|
||||
// m_update_cnt--;
|
||||
//
|
||||
// if (m_update_cnt == 0)
|
||||
wxGetApp().mainframe->on_config_changed(m_config);
|
||||
wxGetApp().mainframe->on_config_changed(m_config);
|
||||
}
|
||||
|
||||
void TabSLAMaterial::update_description_lines()
|
||||
{
|
||||
if (m_active_page && m_active_page->title() == "Material" && m_z_correction_to_mm_description) {
|
||||
auto cfg = m_preset_bundle->full_config();
|
||||
double lh = cfg.opt_float("layer_height");
|
||||
int zlayers = cfg.opt_int("zcorrection_layers");
|
||||
m_z_correction_to_mm_description->SetText(GUI::format_wxstr(_L("Current Z correction depth is: %1% mm"), zlayers * lh));
|
||||
}
|
||||
|
||||
Tab::update_description_lines();
|
||||
}
|
||||
|
||||
void TabSLAMaterial::update_sla_prusa_specific_visibility()
|
||||
@ -5519,6 +5540,8 @@ void TabSLAMaterial::clear_pages()
|
||||
|
||||
for (auto& over_opt : m_overrides_options)
|
||||
over_opt.second = nullptr;
|
||||
|
||||
m_z_correction_to_mm_description = nullptr;
|
||||
}
|
||||
|
||||
void TabSLAMaterial::msw_rescale()
|
||||
|
@ -571,6 +571,8 @@ class TabSLAMaterial : public Tab
|
||||
void update_material_overrides_page();
|
||||
|
||||
std::map<std::string, wxWindow*> m_overrides_options;
|
||||
ogStaticText* m_z_correction_to_mm_description = nullptr;
|
||||
|
||||
public:
|
||||
TabSLAMaterial(wxBookCtrlBase* parent) :
|
||||
Tab(parent, _L("Materials"), Slic3r::Preset::TYPE_SLA_MATERIAL) {}
|
||||
@ -586,6 +588,7 @@ public:
|
||||
void sys_color_changed() override;
|
||||
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptSLA; }
|
||||
void update_sla_prusa_specific_visibility() override;
|
||||
void update_description_lines() override;
|
||||
};
|
||||
|
||||
class TabSLAPrint : public Tab
|
||||
|
@ -5,7 +5,8 @@ add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp
|
||||
sla_supptgen_tests.cpp
|
||||
sla_raycast_tests.cpp
|
||||
sla_supptreeutils_tests.cpp
|
||||
sla_archive_readwrite_tests.cpp)
|
||||
sla_archive_readwrite_tests.cpp
|
||||
sla_zcorrection_tests.cpp)
|
||||
|
||||
# mold linker for successful linking needs also to link TBB library and link it before libslic3r.
|
||||
target_link_libraries(${_TEST_NAME}_tests test_common TBB::tbb TBB::tbbmalloc libslic3r)
|
||||
|
123
tests/sla_print/sla_zcorrection_tests.cpp
Normal file
123
tests/sla_print/sla_zcorrection_tests.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <test_utils.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "libslic3r/TriangleMeshSlicer.hpp"
|
||||
#include "libslic3r/SLA/ZCorrection.hpp"
|
||||
#include "libslic3r/MTUtils.hpp"
|
||||
#include "libslic3r/SVG.hpp"
|
||||
|
||||
void print_depthmap(std::string_view prefix,
|
||||
const Slic3r::BoundingBox &bb,
|
||||
const Slic3r::sla::zcorr_detail::DepthMap &dm)
|
||||
{
|
||||
using namespace Slic3r;
|
||||
|
||||
size_t cnt = 0;
|
||||
for (const sla::zcorr_detail::DepthMapLayer &layer : dm) {
|
||||
SVG svg(std::string(prefix) + std::to_string(cnt++) + ".svg", bb);
|
||||
for (const auto &[depth, dpolys] : layer) {
|
||||
svg.draw_outline(dpolys);
|
||||
svg.draw(dpolys, "green", 1. + depth / 10.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Number of layers should be equal after z correction", "[ZCorr]")
|
||||
{
|
||||
using namespace Slic3r;
|
||||
|
||||
const size_t Layers = random_value(size_t{1}, size_t{100});
|
||||
INFO("Layers = " << Layers);
|
||||
|
||||
float zcorr_depth = GENERATE(0.f, random_value(0.01f, 10.f));
|
||||
|
||||
std::vector<ExPolygons> slices(Layers);
|
||||
std::vector<float> hgrid = grid(0.f, Layers * 1.f, 1.f);
|
||||
|
||||
std::vector<ExPolygons> output = sla::apply_zcorrection(slices, hgrid, zcorr_depth);
|
||||
|
||||
REQUIRE(slices.size() == output.size());
|
||||
}
|
||||
|
||||
TEST_CASE("Testing DepthMap for a cube", "[ZCorr]")
|
||||
{
|
||||
using namespace Slic3r;
|
||||
|
||||
TriangleMesh mesh = load_model("20mm_cube.obj");
|
||||
auto bb = bounding_box(mesh);
|
||||
bb.offset(-0.1);
|
||||
|
||||
std::vector<float> hgrid = grid<float>(bb.min.z(), bb.max.z(), 1.f);
|
||||
|
||||
std::vector<ExPolygons> slices = slice_mesh_ex(mesh.its, hgrid, {});
|
||||
|
||||
sla::zcorr_detail::DepthMap dmap = sla::zcorr_detail::create_depthmap(slices, hgrid);
|
||||
|
||||
REQUIRE(dmap.size() == slices.size());
|
||||
|
||||
for (size_t i = 0; i < slices.size(); ++i) {
|
||||
const auto &dlayer = dmap[i];
|
||||
const ExPolygons &slayer = slices[i];
|
||||
REQUIRE(dlayer.size() == 1);
|
||||
REQUIRE(dlayer.begin()->first == i);
|
||||
double ad = area(dlayer.begin()->second);
|
||||
double as = area(slayer);
|
||||
REQUIRE(ad == Approx(as).margin(EPSILON));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Testing DepthMap for arbitrary shapes", "[ZCorr]")
|
||||
{
|
||||
using namespace Slic3r;
|
||||
|
||||
auto modelname = GENERATE("V_standing.obj", "A_upsidedown.obj");
|
||||
|
||||
TriangleMesh mesh = load_model(modelname);
|
||||
auto bb = bounding_box(mesh);
|
||||
bb.offset(-0.1);
|
||||
|
||||
std::vector<float> hgrid = grid<float>(bb.min.z(), bb.max.z(), 0.5f);
|
||||
|
||||
std::vector<ExPolygons> slices = slice_mesh_ex(mesh.its, hgrid, {});
|
||||
|
||||
size_t zcorr_layers = GENERATE(size_t{0}, random_value(size_t{1}, size_t{10}));
|
||||
|
||||
sla::zcorr_detail::DepthMap dmap =
|
||||
sla::zcorr_detail::create_depthmap(slices, hgrid, zcorr_layers);
|
||||
|
||||
#ifndef NDEBUG
|
||||
print_depthmap("debug_dmap", scaled(to_2d(bb)), dmap);
|
||||
#endif
|
||||
|
||||
REQUIRE(dmap.size() == slices.size());
|
||||
|
||||
auto corrslices_fast = sla::apply_zcorrection(slices, zcorr_layers);
|
||||
sla::zcorr_detail::apply_zcorrection(dmap, zcorr_layers);
|
||||
|
||||
for (size_t i = 0; i < corrslices_fast.size(); ++i) {
|
||||
ExPolygons dlayer = sla::zcorr_detail::merged_layer(dmap[i]);
|
||||
const ExPolygons &slayer = corrslices_fast[i];
|
||||
double ad = area(dlayer);
|
||||
double as = area(slayer);
|
||||
REQUIRE(ad == Approx(as).margin(EPSILON));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Test depth to layers calculation", "[ZCorr]") {
|
||||
using namespace Slic3r;
|
||||
|
||||
float layer_h = 0.5f;
|
||||
std::vector<float> hgrid = grid(0.f, 100.f, layer_h);
|
||||
|
||||
float depth = GENERATE(0.f,
|
||||
random_value(0.01f, 0.499f),
|
||||
0.5f,
|
||||
random_value(0.501f, 10.f));
|
||||
|
||||
for (size_t i = 0; i < hgrid.size(); ++i) {
|
||||
auto expected_lyrs = std::min(i, static_cast<size_t>(std::ceil(depth/layer_h)));
|
||||
REQUIRE(sla::zcorr_detail::depth_to_layers(hgrid, i, depth) == expected_lyrs);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user