Merge branch 'master' into fs_simplify_multipart_object
# Conflicts: # src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
This commit is contained in:
commit
74eb4ec042
@ -288,7 +288,7 @@ set(CGAL_DO_NOT_WARN_ABOUT_CMAKE_BUILD_TYPE ON CACHE BOOL "" FORCE)
|
||||
|
||||
cmake_policy(PUSH)
|
||||
cmake_policy(SET CMP0011 NEW)
|
||||
find_package(CGAL REQUIRED)
|
||||
find_package(CGAL 4.13.2 REQUIRED)
|
||||
cmake_policy(POP)
|
||||
|
||||
add_library(libslic3r_cgal STATIC MeshBoolean.cpp MeshBoolean.hpp TryCatchSignal.hpp
|
||||
|
@ -382,6 +382,7 @@ void fill_iniconf(ConfMap &m, const SLAPrint &print)
|
||||
m["layerHeight"] = get_cfg_value(cfg, "layer_height");
|
||||
m["expTime"] = get_cfg_value(cfg, "exposure_time");
|
||||
m["expTimeFirst"] = get_cfg_value(cfg, "initial_exposure_time");
|
||||
m["expUserProfile"] = get_cfg_value(cfg, "material_print_speed") == "slow" ? "1" : "0";
|
||||
m["materialName"] = get_cfg_value(cfg, "sla_material_settings_id");
|
||||
m["printerModel"] = get_cfg_value(cfg, "printer_model");
|
||||
m["printerVariant"] = get_cfg_value(cfg, "printer_variant");
|
||||
|
@ -475,6 +475,7 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
|
||||
std::vector<GCode::LayerToPrint> layers_to_print;
|
||||
layers_to_print.reserve(object.layers().size() + object.support_layers().size());
|
||||
|
||||
/*
|
||||
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
|
||||
// This is the same logic as in support generator.
|
||||
//FIXME should we use the printing extruders instead?
|
||||
@ -488,7 +489,7 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
|
||||
for (auto lh : object.print()->config().min_layer_height.values)
|
||||
support_layer_height_min = std::min(support_layer_height_min, std::max(0.01, lh));
|
||||
gap_over_supports += support_layer_height_min;
|
||||
}
|
||||
}*/
|
||||
|
||||
std::vector<std::pair<double, double>> warning_ranges;
|
||||
|
||||
@ -528,22 +529,23 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
|
||||
if ((layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
|
||||
// Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions.
|
||||
|| (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) {
|
||||
double support_contact_z = (last_extrusion_layer && last_extrusion_layer->support_layer)
|
||||
? gap_over_supports
|
||||
: 0.;
|
||||
double top_cd = object.config().support_material_contact_distance;
|
||||
double bottom_cd = object.config().support_material_bottom_contact_distance == 0. ? top_cd : object.config().support_material_bottom_contact_distance;
|
||||
|
||||
double extra_gap = (layer_to_print.support_layer ? bottom_cd : top_cd);
|
||||
|
||||
double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.)
|
||||
+ layer_to_print.layer()->height
|
||||
+ support_contact_z;
|
||||
+ std::max(0., extra_gap);
|
||||
// Negative support_contact_z is not taken into account, it can result in false positives in cases
|
||||
// where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752)
|
||||
|
||||
if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON)
|
||||
warning_ranges.emplace_back(std::make_pair((last_extrusion_layer ? last_extrusion_layer->print_z() : 0.), layers_to_print.back().print_z()));
|
||||
|
||||
// Remember last layer with extrusions.
|
||||
if (has_extrusions)
|
||||
last_extrusion_layer = &layers_to_print.back();
|
||||
}
|
||||
// Remember last layer with extrusions.
|
||||
if (has_extrusions)
|
||||
last_extrusion_layer = &layers_to_print.back();
|
||||
}
|
||||
|
||||
if (! warning_ranges.empty()) {
|
||||
|
@ -551,6 +551,7 @@ static std::vector<std::string> s_Preset_sla_material_options {
|
||||
"material_correction_z",
|
||||
"material_notes",
|
||||
"material_vendor",
|
||||
"material_print_speed",
|
||||
"default_sla_material_profile",
|
||||
"compatible_prints", "compatible_prints_condition",
|
||||
"compatible_printers", "compatible_printers_condition", "inherits"
|
||||
|
@ -604,12 +604,24 @@ void print_objects_regions_invalidate_keep_some_volumes(PrintObjectRegions &prin
|
||||
print_object_regions.cached_volume_ids.erase(print_object_regions.cached_volume_ids.begin() + last_cached_volume, print_object_regions.cached_volume_ids.end());
|
||||
}
|
||||
|
||||
// Find a bounding box of a volume's part intersecting layer_range. Such a bounding box will likely be smaller in XY than the full bounding box,
|
||||
// thus it will intersect with lower number of other volumes.
|
||||
const PrintObjectRegions::BoundingBox* find_volume_extents(const PrintObjectRegions::LayerRangeRegions &layer_range, const ModelVolume &volume)
|
||||
{
|
||||
auto it = lower_bound_by_predicate(layer_range.volumes.begin(), layer_range.volumes.end(), [&volume](const PrintObjectRegions::VolumeExtents &l){ return l.volume_id < volume.id(); });
|
||||
return it != layer_range.volumes.end() && it->volume_id == volume.id() ? &it->bbox : nullptr;
|
||||
}
|
||||
|
||||
// Find a bounding box of a topmost printable volume referenced by this modifier given this_region_id.
|
||||
const PrintObjectRegions::BoundingBox* find_modifier_volume_extents(const PrintObjectRegions::LayerRangeRegions &layer_range, const int this_region_id)
|
||||
{
|
||||
// Find the top-most printable volume of this modifier, or the printable volume itself.
|
||||
int parent_region_id = this_region_id;
|
||||
for (; ! layer_range.volume_regions[parent_region_id].model_volume->is_model_part(); parent_region_id = layer_range.volume_regions[parent_region_id].parent)
|
||||
assert(parent_region_id >= 0);
|
||||
return find_volume_extents(layer_range, *layer_range.volume_regions[parent_region_id].model_volume);
|
||||
}
|
||||
|
||||
PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_or_parent_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders);
|
||||
|
||||
void print_region_ref_inc(PrintRegion &r) { ++ r.m_ref_cnt; }
|
||||
@ -634,11 +646,54 @@ bool verify_update_print_object_regions(
|
||||
print_region_ref_reset(*region);
|
||||
|
||||
// Verify and / or update PrintRegions produced by ModelVolumes, layer range modifiers, modifier volumes.
|
||||
for (PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges)
|
||||
for (PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges) {
|
||||
// Each modifier ModelVolume intersecting this layer_range shall be referenced here at least once if it intersects some
|
||||
// printable ModelVolume at this layer_range even if it does not modify its overlapping printable ModelVolume configuration yet.
|
||||
// VolumeRegions reference ModelVolumes in layer_range.volume_regions the order they are stored in ModelObject volumes.
|
||||
// Remember whether a given modifier ModelVolume was visited already.
|
||||
auto it_model_volume_modifier_last = model_volumes.end();
|
||||
for (PrintObjectRegions::VolumeRegion ®ion : layer_range.volume_regions)
|
||||
if (region.model_volume->is_model_part() || region.model_volume->is_modifier()) {
|
||||
auto it_model_volume = lower_bound_by_predicate(model_volumes.begin(), model_volumes.end(), [®ion](const ModelVolume *l){ return l->id() < region.model_volume->id(); });
|
||||
assert(it_model_volume != model_volumes.end() && (*it_model_volume)->id() == region.model_volume->id());
|
||||
if (region.model_volume->is_modifier() && it_model_volume != it_model_volume_modifier_last) {
|
||||
// A modifier ModelVolume is visited for the first time.
|
||||
// A visited modifier may not have had parent volume_regions created overlapping with some model parts or modifiers,
|
||||
// if the visited modifier did not modify their properties. Now the visited modifier's configuration may have changed,
|
||||
// which may require new regions to be created.
|
||||
it_model_volume_modifier_last = it_model_volume;
|
||||
int this_region_id = int(®ion - layer_range.volume_regions.data());
|
||||
int next_region_id = this_region_id + 1;
|
||||
const PrintObjectRegions::BoundingBox *bbox = find_volume_extents(layer_range, *region.model_volume);
|
||||
assert(bbox);
|
||||
for (int parent_region_id = this_region_id - 1; parent_region_id >= 0; -- parent_region_id) {
|
||||
const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id];
|
||||
assert(parent_region.model_volume != region.model_volume);
|
||||
if (parent_region.model_volume->is_model_part() || parent_region.model_volume->is_modifier()) {
|
||||
// volume_regions are produced in decreasing order of parent volume_regions ids.
|
||||
// Some regions may not have been generated the last time by generate_print_object_regions().
|
||||
assert(next_region_id == int(layer_range.volume_regions.size()) ||
|
||||
layer_range.volume_regions[next_region_id].model_volume != region.model_volume ||
|
||||
layer_range.volume_regions[next_region_id].parent <= parent_region_id);
|
||||
if (next_region_id < int(layer_range.volume_regions.size()) &&
|
||||
layer_range.volume_regions[next_region_id].model_volume == region.model_volume &&
|
||||
layer_range.volume_regions[next_region_id].parent == parent_region_id) {
|
||||
// A parent region is already overridden.
|
||||
++ next_region_id;
|
||||
} else {
|
||||
// Such parent region does not exist. If it is needed, then we need to reslice.
|
||||
const PrintObjectRegions::BoundingBox *parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id);
|
||||
assert(parent_bbox != nullptr);
|
||||
if (parent_bbox->intersects(*bbox))
|
||||
// Only create new region for a modifier, which actually modifies config of it's parent.
|
||||
if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, **it_model_volume, num_extruders);
|
||||
config != parent_region.region->config())
|
||||
// This modifier newly overrides a region, which it did not before. We need to reslice.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PrintRegionConfig cfg = region.parent == -1 ?
|
||||
region_config_from_model_volume(default_region_config, layer_range.config, **it_model_volume, num_extruders) :
|
||||
region_config_from_model_volume(layer_range.volume_regions[region.parent].region->config(), nullptr, **it_model_volume, num_extruders);
|
||||
@ -657,6 +712,7 @@ bool verify_update_print_object_regions(
|
||||
}
|
||||
print_region_ref_inc(*region.region);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify and / or update PrintRegions produced by color painting.
|
||||
for (const PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges)
|
||||
@ -851,17 +907,28 @@ static PrintObjectRegions* generate_print_object_regions(
|
||||
} else {
|
||||
assert(volume.is_modifier());
|
||||
// Modifiers may be chained one over the other. Check for overlap, merge DynamicPrintConfigs.
|
||||
for (int parent_region_id = int(layer_range.volume_regions.size()) - 1; parent_region_id >= 0; -- parent_region_id)
|
||||
if (const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id];
|
||||
parent_region.model_volume->is_model_part() || parent_region.model_volume->is_modifier()) {
|
||||
const PrintObjectRegions::BoundingBox *parent_bbox = find_volume_extents(layer_range, *parent_region.model_volume);
|
||||
bool added = false;
|
||||
int parent_model_part_id = -1;
|
||||
for (int parent_region_id = int(layer_range.volume_regions.size()) - 1; parent_region_id >= 0; -- parent_region_id) {
|
||||
const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id];
|
||||
const ModelVolume &parent_volume = *parent_region.model_volume;
|
||||
if (parent_volume.is_model_part() || parent_volume.is_modifier()) {
|
||||
const PrintObjectRegions::BoundingBox *parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id);
|
||||
assert(parent_bbox != nullptr);
|
||||
if (parent_bbox->intersects(*bbox))
|
||||
// Only create new region for a modifier, which actually modifies config of it's parent.
|
||||
if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, volume, num_extruders);
|
||||
config != parent_region.region->config())
|
||||
config != parent_region.region->config()) {
|
||||
added = true;
|
||||
layer_range.volume_regions.push_back({ &volume, parent_region_id, get_create_region(std::move(config)), bbox });
|
||||
} else if (parent_model_part_id == -1 && parent_volume.is_model_part())
|
||||
parent_model_part_id = parent_region_id;
|
||||
}
|
||||
}
|
||||
if (! added && parent_model_part_id >= 0)
|
||||
// This modifier does not override any printable volume's configuration, however it may in the future.
|
||||
// Store it so that verify_update_print_object_regions() will handle this modifier correctly if its configuration changes.
|
||||
layer_range.volume_regions.push_back({ &volume, parent_model_part_id, layer_range.volume_regions[parent_model_part_id].region, bbox });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,6 +165,12 @@ static const t_config_enum_values s_keys_map_SLAPillarConnectionMode = {
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SLAPillarConnectionMode)
|
||||
|
||||
static const t_config_enum_values s_keys_map_SLAMaterialSpeed = {
|
||||
{"slow", slamsSlow},
|
||||
{"fast", slamsFast}
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SLAMaterialSpeed);
|
||||
|
||||
static const t_config_enum_values s_keys_map_BrimType = {
|
||||
{"no_brim", btNoBrim},
|
||||
{"outer_only", btOuterOnly},
|
||||
@ -3730,6 +3736,19 @@ void PrintConfigDef::init_sla_params()
|
||||
def->max = 10;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(2.0));
|
||||
|
||||
def = this->add("material_print_speed", coEnum);
|
||||
def->label = L("Print speed");
|
||||
def->tooltip = L(
|
||||
"A slower printing profile might be necessary when using materials with higher viscosity "
|
||||
"or with some hollowed parts. It slows down the tilt movement and adds a delay before exposure.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<SLAMaterialSpeed>::get_enum_values();
|
||||
def->enum_values.push_back("slow");
|
||||
def->enum_values.push_back("fast");
|
||||
def->enum_labels.push_back(L("Slow"));
|
||||
def->enum_labels.push_back(L("Fast"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnum<SLAMaterialSpeed>(slamsSlow));
|
||||
}
|
||||
|
||||
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
|
||||
|
@ -915,6 +915,8 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, hollowing_closing_distance))
|
||||
)
|
||||
|
||||
enum SLAMaterialSpeed { slamsSlow, slamsFast };
|
||||
|
||||
PRINT_CONFIG_CLASS_DEFINE(
|
||||
SLAMaterialConfig,
|
||||
|
||||
@ -929,6 +931,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, material_correction_x))
|
||||
((ConfigOptionFloat, material_correction_y))
|
||||
((ConfigOptionFloat, material_correction_z))
|
||||
((ConfigOptionEnum<SLAMaterialSpeed>, material_print_speed))
|
||||
)
|
||||
|
||||
PRINT_CONFIG_CLASS_DEFINE(
|
||||
|
@ -165,11 +165,6 @@ typedef struct NSVGimage
|
||||
// Parses SVG file from a file, returns SVG image as paths.
|
||||
NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi);
|
||||
|
||||
// Parses SVG file from a file, returns SVG image as paths.
|
||||
// And makes replases befor parsing
|
||||
// replace_map containes old_value->new_value
|
||||
NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replace_map);
|
||||
|
||||
// Parses SVG file from a null terminated string, returns SVG image as paths.
|
||||
// Important note: changes the string.
|
||||
NSVGimage* nsvgParse(char* input, const char* units, float dpi);
|
||||
@ -2908,12 +2903,6 @@ NSVGimage* nsvgParse(char* input, const char* units, float dpi)
|
||||
|
||||
NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
|
||||
{
|
||||
return nsvgParseFromFileWithReplace(filename, units, dpi, { {} });
|
||||
}
|
||||
|
||||
NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replaces)
|
||||
{
|
||||
std::string str;
|
||||
FILE* fp = NULL;
|
||||
size_t size;
|
||||
char* data = NULL;
|
||||
@ -2930,14 +2919,7 @@ NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units,
|
||||
data[size] = '\0'; // Must be null terminated.
|
||||
fclose(fp);
|
||||
|
||||
if(replaces.empty())
|
||||
image = nsvgParse(data, units, dpi);
|
||||
else {
|
||||
str.assign(data);
|
||||
for (auto val : replaces)
|
||||
boost::replace_all(str, val.first, val.second);
|
||||
image = nsvgParse(str.data(), units, dpi);
|
||||
}
|
||||
image = nsvgParse(data, units, dpi);
|
||||
free(data);
|
||||
return image;
|
||||
|
||||
|
@ -252,12 +252,16 @@ void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
|
||||
BoundingBoxf3 Bed3D::calc_extended_bounding_box() const
|
||||
{
|
||||
BoundingBoxf3 out { m_build_volume.bounding_volume() };
|
||||
const Vec3d size = out.size();
|
||||
// ensures that the bounding box is set as defined or the following calls to merge() will not work as intented
|
||||
if (size.x() > 0.0 && size.y() > 0.0 && !out.defined)
|
||||
out.defined = true;
|
||||
// Reset the build volume Z, we don't want to zoom to the top of the build volume if it is empty.
|
||||
out.min.z() = 0;
|
||||
out.max.z() = 0;
|
||||
out.min.z() = 0.0;
|
||||
out.max.z() = 0.0;
|
||||
// extend to contain axes
|
||||
out.merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones());
|
||||
out.merge(out.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, out.max(2)));
|
||||
out.merge(out.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, out.max.z()));
|
||||
// extend to contain model, if any
|
||||
BoundingBoxf3 model_bb = m_model.get_bounding_box();
|
||||
if (model_bb.defined) {
|
||||
|
@ -260,6 +260,43 @@ wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width,
|
||||
return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)));
|
||||
}
|
||||
|
||||
NSVGimage* BitmapCache::nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replaces)
|
||||
{
|
||||
std::string str;
|
||||
FILE* fp = NULL;
|
||||
size_t size;
|
||||
char* data = NULL;
|
||||
NSVGimage* image = NULL;
|
||||
|
||||
fp = boost::nowide::fopen(filename, "rb");
|
||||
if (!fp) goto error;
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
data = (char*)malloc(size + 1);
|
||||
if (data == NULL) goto error;
|
||||
if (fread(data, 1, size, fp) != size) goto error;
|
||||
data[size] = '\0'; // Must be null terminated.
|
||||
fclose(fp);
|
||||
|
||||
if (replaces.empty())
|
||||
image = nsvgParse(data, units, dpi);
|
||||
else {
|
||||
str.assign(data);
|
||||
for (auto val : replaces)
|
||||
boost::replace_all(str, val.first, val.second);
|
||||
image = nsvgParse(str.data(), units, dpi);
|
||||
}
|
||||
free(data);
|
||||
return image;
|
||||
|
||||
error:
|
||||
if (fp) fclose(fp);
|
||||
if (data) free(data);
|
||||
if (image) nsvgDelete(image);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height,
|
||||
const bool grayscale/* = false*/, const bool dark_mode/* = false*/, const std::string& new_color /*= ""*/)
|
||||
{
|
||||
@ -278,11 +315,11 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_
|
||||
// map of color replaces
|
||||
std::map<std::string, std::string> replaces;
|
||||
if (dark_mode)
|
||||
replaces["#808080"] = "#FFFFFF";
|
||||
replaces["\"#808080\""] = "\"#FFFFFF\"";
|
||||
if (!new_color.empty())
|
||||
replaces["#ED6B21"] = new_color;
|
||||
replaces["\"#ED6B21\""] = "\"" + new_color + "\"";
|
||||
|
||||
NSVGimage *image = ::nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, replaces);
|
||||
NSVGimage *image = nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, replaces);
|
||||
if (image == nullptr)
|
||||
return nullptr;
|
||||
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include <wx/wx.h>
|
||||
#endif
|
||||
|
||||
struct NSVGimage;
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class BitmapCache
|
||||
@ -32,6 +34,11 @@ public:
|
||||
|
||||
// Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero.
|
||||
wxBitmap* load_png(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false);
|
||||
|
||||
// Parses SVG file from a file, returns SVG image as paths.
|
||||
// And makes replases befor parsing
|
||||
// replace_map containes old_value->new_value
|
||||
static NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map<std::string, std::string>& replaces);
|
||||
// Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width.
|
||||
wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false, const std::string& new_color = "");
|
||||
|
||||
|
@ -271,8 +271,10 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
|
||||
ImGui::SetCursorPosX(widget_align);
|
||||
ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f);
|
||||
int radius = (int)m_smooth_params.radius;
|
||||
if (ImGui::SliderInt("##1", &radius, 1, 10))
|
||||
if (ImGui::SliderInt("##1", &radius, 1, 10)) {
|
||||
radius = std::clamp(radius, 1, 10);
|
||||
m_smooth_params.radius = (unsigned int)radius;
|
||||
}
|
||||
|
||||
ImGui::SetCursorPosX(text_align);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
@ -1119,6 +1121,8 @@ void GLCanvas3D::reset_volumes()
|
||||
|
||||
ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const
|
||||
{
|
||||
assert(m_initialized);
|
||||
|
||||
ModelInstanceEPrintVolumeState state;
|
||||
m_volumes.check_outside_state(m_bed.build_volume(), &state);
|
||||
return state;
|
||||
|
@ -812,6 +812,7 @@ public:
|
||||
|
||||
void schedule_extra_frame(int miliseconds);
|
||||
|
||||
float get_main_toolbar_height() { return m_main_toolbar.get_height(); }
|
||||
int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); }
|
||||
void force_main_toolbar_left_action(int item_id) { m_main_toolbar.force_left_action(item_id, *this); }
|
||||
void force_main_toolbar_right_action(int item_id) { m_main_toolbar.force_right_action(item_id, *this); }
|
||||
|
@ -431,52 +431,46 @@ bool static check_old_linux_datadir(const wxString& app_name) {
|
||||
static bool run_updater_win()
|
||||
{
|
||||
// find updater exe
|
||||
boost::filesystem::path path_to_binary = boost::dll::program_location();
|
||||
for (const auto& dir_entry : boost::filesystem::directory_iterator(path_to_binary.parent_path())) {
|
||||
if (dir_entry.path().filename() == "prusaslicer-updater.exe") {
|
||||
// run updater. Original args: /silent -restartapp prusa-slicer.exe -startappfirst
|
||||
boost::filesystem::path path_updater = boost::dll::program_location().parent_path() / "prusaslicer-updater.exe";
|
||||
if (boost::filesystem::exists(path_updater)) {
|
||||
// run updater. Original args: /silent -restartapp prusa-slicer.exe -startappfirst
|
||||
|
||||
// Using quoted string as mentioned in CreateProcessW docs.
|
||||
std::wstring wcmd = L"\"" + dir_entry.path().wstring() + L"\"";
|
||||
wcmd += L" /silent";
|
||||
// Using quoted string as mentioned in CreateProcessW docs, silent execution parameter.
|
||||
std::wstring wcmd = L"\"" + path_updater.wstring() + L"\" /silent";
|
||||
|
||||
// additional information
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
// additional information
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
// set the size of the structures
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
// set the size of the structures
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
// start the program up
|
||||
if (CreateProcessW(NULL, // the path
|
||||
wcmd.data(), // Command line
|
||||
NULL, // Process handle not inheritable
|
||||
NULL, // Thread handle not inheritable
|
||||
FALSE, // Set handle inheritance to FALSE
|
||||
0, // No creation flags
|
||||
NULL, // Use parent's environment block
|
||||
NULL, // Use parent's starting directory
|
||||
&si, // Pointer to STARTUPINFO structure
|
||||
&pi // Pointer to PROCESS_INFORMATION structure (removed extra parentheses)
|
||||
)) {
|
||||
// Close process and thread handles.
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
return true;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to start prusaslicer-updater.exe with command " << wcmd;
|
||||
}
|
||||
break;
|
||||
// start the program up
|
||||
if (CreateProcessW(NULL, // the path
|
||||
wcmd.data(), // Command line
|
||||
NULL, // Process handle not inheritable
|
||||
NULL, // Thread handle not inheritable
|
||||
FALSE, // Set handle inheritance to FALSE
|
||||
0, // No creation flags
|
||||
NULL, // Use parent's environment block
|
||||
NULL, // Use parent's starting directory
|
||||
&si, // Pointer to STARTUPINFO structure
|
||||
&pi // Pointer to PROCESS_INFORMATION structure (removed extra parentheses)
|
||||
)) {
|
||||
// Close process and thread handles.
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
return true;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to start prusaslicer-updater.exe with command " << wcmd;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif //_WIN32
|
||||
|
||||
|
||||
|
||||
wxString file_wildcards(FileType file_type, const std::string &custom_extension)
|
||||
{
|
||||
static const std::string defaults[FT_SIZE] = {
|
||||
@ -738,14 +732,11 @@ void GUI_App::post_init()
|
||||
// sees something else than "we want something" on the first start.
|
||||
show_send_system_info_dialog_if_needed();
|
||||
}
|
||||
bool updater_running =
|
||||
#ifdef _WIN32
|
||||
// Run external updater on Windows.
|
||||
run_updater_win();
|
||||
#else
|
||||
false;
|
||||
if (! run_updater_win())
|
||||
// "prusaslicer-updater.exe" was not started, run our own update check.
|
||||
#endif // _WIN32
|
||||
if (!updater_running)
|
||||
this->preset_updater->slic3r_update_notify();
|
||||
});
|
||||
}
|
||||
|
@ -916,7 +916,7 @@ void ObjectList::list_manipulation(const wxPoint& mouse_pos, bool evt_context_me
|
||||
if (is_windows10() && m_objects_model->HasWarningIcon(item) &&
|
||||
mouse_pos.x > 2 * wxGetApp().em_unit() && mouse_pos.x < 4 * wxGetApp().em_unit())
|
||||
fix_through_netfabb();
|
||||
else
|
||||
else if (evt_context_menu)
|
||||
show_context_menu(evt_context_menu); // show context menu for "Name" column too
|
||||
}
|
||||
// workaround for extruder editing under OSX
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
||||
#include "slic3r/GUI/MsgDialog.hpp"
|
||||
#include "slic3r/GUI/NotificationManager.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
@ -199,6 +200,12 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
||||
if (act_volume_ids.empty()) {
|
||||
stop_worker_thread_request();
|
||||
close();
|
||||
if (! m_parent.get_selection().is_single_volume()) {
|
||||
MessageDialog msg((wxWindow*)wxGetApp().mainframe,
|
||||
_L("Simplification is currently only allowed when a single part is selected"),
|
||||
_L("Error"));
|
||||
msg.ShowModal();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -264,7 +271,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
||||
pos.y -= m_gui_cfg->window_offset_y;
|
||||
// minimal top left value
|
||||
ImVec2 tl(m_gui_cfg->window_padding,
|
||||
m_gui_cfg->window_padding);
|
||||
m_gui_cfg->window_padding + m_parent.get_main_toolbar_height());
|
||||
if (pos.x < tl.x) pos.x = tl.x;
|
||||
if (pos.y < tl.y) pos.y = tl.y;
|
||||
// maximal bottom right value
|
||||
|
@ -271,11 +271,9 @@ void HollowedMesh::on_update()
|
||||
m_old_hollowing_timestamp = timestamp;
|
||||
|
||||
indexed_triangle_set interior = print_object->hollowed_interior_mesh();
|
||||
if (!interior.empty()) {
|
||||
its_flip_triangles(interior);
|
||||
m_hollowed_interior_transformed = std::make_unique<TriangleMesh>(std::move(interior));
|
||||
m_hollowed_interior_transformed->transform(trafo_inv);
|
||||
}
|
||||
its_flip_triangles(interior);
|
||||
m_hollowed_interior_transformed = std::make_unique<TriangleMesh>(std::move(interior));
|
||||
m_hollowed_interior_transformed->transform(trafo_inv);
|
||||
}
|
||||
else {
|
||||
m_hollowed_mesh_transformed.reset(nullptr);
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "GUI.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "Search.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
|
||||
#include "../Utils/MacDarkMode.hpp"
|
||||
#include "nanosvg/nanosvg.h"
|
||||
@ -1030,7 +1031,7 @@ std::vector<unsigned char> ImGuiWrapper::load_svg(const std::string& bitmap_name
|
||||
{
|
||||
std::vector<unsigned char> empty_vector;
|
||||
|
||||
NSVGimage* image = ::nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, { { "#808080", "#FFFFFF" } });
|
||||
NSVGimage* image = BitmapCache::nsvgParseFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f, { { "\"#808080\"", "\"#FFFFFF\"" } });
|
||||
if (image == nullptr)
|
||||
return empty_vector;
|
||||
|
||||
|
@ -229,7 +229,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
||||
}
|
||||
// check unsaved changes only if project wasn't saved
|
||||
else if (plater()->is_project_dirty() && saved_project == wxID_NO && event.CanVeto() &&
|
||||
!wxGetApp().check_and_save_current_preset_changes(_L("PrusaSlicer is closing"), _L("Closing PrusaSlicer while some presets are modified."))) {
|
||||
(plater()->is_presets_dirty() && !wxGetApp().check_and_save_current_preset_changes(_L("PrusaSlicer is closing"), _L("Closing PrusaSlicer while some presets are modified.")))) {
|
||||
event.Veto();
|
||||
return;
|
||||
}
|
||||
@ -486,7 +486,7 @@ void MainFrame::update_layout()
|
||||
case ESettingsLayout::GCodeViewer:
|
||||
{
|
||||
m_main_sizer->Add(m_plater, 1, wxEXPAND);
|
||||
m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, 0, {}, {}, true);
|
||||
m_plater->set_bed_shape({ { 0.0, 0.0 }, { 200.0, 0.0 }, { 200.0, 200.0 }, { 0.0, 200.0 } }, 0.0, {}, {}, true);
|
||||
m_plater->get_collapse_toolbar().set_enabled(false);
|
||||
m_plater->collapse_sidebar(true);
|
||||
m_plater->Show();
|
||||
|
@ -375,7 +375,7 @@ void Mouse3DController::load_config(const AppConfig &appconfig)
|
||||
appconfig.get_mouse_device_swap_yz(device_name, swap_yz);
|
||||
// clamp to valid values
|
||||
Params params;
|
||||
params.translation.scale = Params::DefaultTranslationScale * std::clamp(translation_speed, 0.1, 10.0);
|
||||
params.translation.scale = Params::DefaultTranslationScale * std::clamp(translation_speed, Params::MinTranslationScale, Params::MaxTranslationScale);
|
||||
params.translation.deadzone = std::clamp(translation_deadzone, 0.0, Params::MaxTranslationDeadzone);
|
||||
params.rotation.scale = Params::DefaultRotationScale * std::clamp(rotation_speed, 0.1f, 10.0f);
|
||||
params.rotation.deadzone = std::clamp(rotation_deadzone, 0.0f, Params::MaxRotationDeadzone);
|
||||
@ -469,7 +469,7 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
|
||||
imgui.text_colored(color, _L("Speed:"));
|
||||
|
||||
float translation_scale = (float)params_copy.translation.scale / Params::DefaultTranslationScale;
|
||||
if (imgui.slider_float(_L("Translation") + "##1", &translation_scale, 0.1f, 10.0f, "%.1f")) {
|
||||
if (imgui.slider_float(_L("Translation") + "##1", &translation_scale, Params::MinTranslationScale, Params::MaxTranslationScale, "%.1f")) {
|
||||
params_copy.translation.scale = Params::DefaultTranslationScale * (double)translation_scale;
|
||||
params_changed = true;
|
||||
}
|
||||
|
@ -33,6 +33,8 @@ class Mouse3DController
|
||||
// to copy the parameters.
|
||||
struct Params
|
||||
{
|
||||
static constexpr double MinTranslationScale = 0.1;
|
||||
static constexpr double MaxTranslationScale = 30.;
|
||||
static constexpr double DefaultTranslationScale = 2.5;
|
||||
static constexpr double MaxTranslationDeadzone = 0.2;
|
||||
static constexpr double DefaultTranslationDeadzone = 0.0;
|
||||
|
@ -1214,7 +1214,7 @@ void Sidebar::show_info_sizer()
|
||||
ModelObjectPtrs objects = p->plater->model().objects;
|
||||
int obj_idx = selection.get_object_idx();
|
||||
|
||||
if (m_mode < comExpert || objects.empty() || obj_idx < 0 || obj_idx > 1000 ||
|
||||
if (m_mode < comExpert || objects.empty() || obj_idx < 0 || obj_idx == 1000 ||
|
||||
objects[obj_idx]->volumes.empty() || // hack to avoid crash when deleting the last object on the bed
|
||||
(selection.is_single_full_object() && objects[obj_idx]->instances.size()> 1) ||
|
||||
!(selection.is_single_full_instance() || selection.is_single_volume())) {
|
||||
@ -1675,6 +1675,7 @@ struct Plater::priv
|
||||
~priv();
|
||||
|
||||
bool is_project_dirty() const { return dirty_state.is_dirty(); }
|
||||
bool is_presets_dirty() const { return dirty_state.is_presets_dirty(); }
|
||||
void update_project_dirty_from_presets() { dirty_state.update_from_presets(); }
|
||||
int save_project_if_dirty(const wxString& reason) {
|
||||
int res = wxID_NO;
|
||||
@ -3866,18 +3867,20 @@ void Plater::priv::set_current_panel(wxPanel* panel)
|
||||
|
||||
preview->get_canvas3d()->bind_event_handlers();
|
||||
|
||||
// see: Plater::priv::object_list_changed()
|
||||
// FIXME: it may be better to have a single function making this check and let it be called wherever needed
|
||||
bool export_in_progress = this->background_process.is_export_scheduled();
|
||||
bool model_fits = view3D->get_canvas3d()->check_volumes_outside_state() != ModelInstancePVS_Partly_Outside;
|
||||
if (!model.objects.empty() && !export_in_progress && model_fits) {
|
||||
if (wxGetApp().is_editor()) {
|
||||
// see: Plater::priv::object_list_changed()
|
||||
// FIXME: it may be better to have a single function making this check and let it be called wherever needed
|
||||
bool export_in_progress = this->background_process.is_export_scheduled();
|
||||
bool model_fits = view3D->get_canvas3d()->check_volumes_outside_state() != ModelInstancePVS_Partly_Outside;
|
||||
if (!model.objects.empty() && !export_in_progress && model_fits) {
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
preview->get_canvas3d()->init_gcode_viewer();
|
||||
preview->get_canvas3d()->init_gcode_viewer();
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
q->reslice();
|
||||
q->reslice();
|
||||
}
|
||||
// keeps current gcode preview, if any
|
||||
preview->reload_print(true);
|
||||
}
|
||||
// keeps current gcode preview, if any
|
||||
preview->reload_print(true);
|
||||
|
||||
preview->set_as_dirty();
|
||||
// reset cached size to force a resize on next call to render() to keep imgui in synch with canvas size
|
||||
@ -5016,6 +5019,7 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame)
|
||||
}
|
||||
|
||||
bool Plater::is_project_dirty() const { return p->is_project_dirty(); }
|
||||
bool Plater::is_presets_dirty() const { return p->is_presets_dirty(); }
|
||||
void Plater::update_project_dirty_from_presets() { p->update_project_dirty_from_presets(); }
|
||||
int Plater::save_project_if_dirty(const wxString& reason) { return p->save_project_if_dirty(reason); }
|
||||
void Plater::reset_project_dirty_after_save() { p->reset_project_dirty_after_save(); }
|
||||
|
@ -140,6 +140,7 @@ public:
|
||||
~Plater() = default;
|
||||
|
||||
bool is_project_dirty() const;
|
||||
bool is_presets_dirty() const;
|
||||
void update_project_dirty_from_presets();
|
||||
int save_project_if_dirty(const wxString& reason);
|
||||
void reset_project_dirty_after_save();
|
||||
|
@ -15,6 +15,7 @@ public:
|
||||
void reset_initial_presets();
|
||||
|
||||
bool is_dirty() const { return m_plater_dirty || m_project_config_dirty || m_presets_dirty; }
|
||||
bool is_presets_dirty() const { return m_presets_dirty; }
|
||||
|
||||
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||
void render_debug_window() const;
|
||||
|
@ -4239,6 +4239,11 @@ void TabSLAMaterial::build()
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
build_preset_description_line(optgroup.get());
|
||||
|
||||
page = add_options_page(L("Material printing profile"), "note.png");
|
||||
optgroup = page->new_optgroup(L("Material printing profile"));
|
||||
option = optgroup->get_option("material_print_speed");
|
||||
optgroup->append_single_option_line(option);
|
||||
}
|
||||
|
||||
// Reload current config (aka presets->edited_preset->config) into the UI fields.
|
||||
@ -4249,6 +4254,13 @@ void TabSLAMaterial::reload_config()
|
||||
Tab::reload_config();
|
||||
}
|
||||
|
||||
void TabSLAMaterial::toggle_options()
|
||||
{
|
||||
const Preset ¤t_printer = wxGetApp().preset_bundle->printers.get_edited_preset();
|
||||
std::string model = current_printer.config.opt_string("printer_model");
|
||||
m_config_manipulation.toggle_field("material_print_speed", model != "SL1");
|
||||
}
|
||||
|
||||
void TabSLAMaterial::update()
|
||||
{
|
||||
if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF)
|
||||
|
@ -480,7 +480,7 @@ public:
|
||||
|
||||
void build() override;
|
||||
void reload_config() override;
|
||||
void toggle_options() override {};
|
||||
void toggle_options() override;
|
||||
void update() override;
|
||||
void init_options_list() override;
|
||||
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptSLA; }
|
||||
|
Loading…
Reference in New Issue
Block a user