Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate into et_transformations
This commit is contained in:
commit
8de9a1669c
61 changed files with 31124 additions and 29811 deletions
|
@ -237,14 +237,14 @@ documentation_link = https://help.prusa3d.com/article/prusaslicer-printables-com
|
|||
weight = 3
|
||||
|
||||
[hint:Cut tool]
|
||||
text = Cut tool\nDid you know that you can cut a model at any angle and even create aligning pins with the updated Cut tool? Learn more in the documentation.
|
||||
text = Cut tool\nDid you know that you can cut a model at any angle and even create aligning pins with the updated <a>Cut tool</a>? Learn more in the documentation.
|
||||
documentation_link = https://help.prusa3d.com/article/cut-tool_1779
|
||||
hypertext_type = gizmo
|
||||
hypertext_gizmo_item = cut
|
||||
weight = 3
|
||||
|
||||
[hint:Measurement tool]
|
||||
text = Measurement tool\nDid you know that you can measure the distances between points, edges and planes, the radius of a hole or the angle between edges or planes? Learn more in the documentation.
|
||||
text = Measurement tool\nDid you know that you can <a>measure</a> the distances between points, edges and planes, the radius of a hole or the angle between edges or planes? Learn more in the documentation.
|
||||
documentation_link = https://help.prusa3d.com/article/measurement-tool_399451
|
||||
hypertext_type = gizmo
|
||||
hypertext_gizmo_item = measure
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -108,6 +108,10 @@ inline cInt Round(double val)
|
|||
return static_cast<cInt>((val < 0) ? (val - 0.5) : (val + 0.5));
|
||||
}
|
||||
|
||||
// Overriding the Eigen operators because we don't want to compare Z coordinate if IntPoint is 3 dimensional.
|
||||
inline bool operator==(const IntPoint &l, const IntPoint &r) { return l.x() == r.x() && l.y() == r.y(); }
|
||||
inline bool operator!=(const IntPoint &l, const IntPoint &r) { return l.x() != r.x() || l.y() != r.y(); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// PolyTree methods ...
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -178,19 +182,25 @@ double Area(const Path &poly)
|
|||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
double Area(const OutRec &outRec)
|
||||
double Area(const OutPt *op)
|
||||
{
|
||||
OutPt *op = outRec.Pts;
|
||||
const OutPt *startOp = op;
|
||||
if (!op) return 0;
|
||||
double a = 0;
|
||||
do {
|
||||
a += (double)(op->Prev->Pt.x() + op->Pt.x()) * (double)(op->Prev->Pt.y() - op->Pt.y());
|
||||
op = op->Next;
|
||||
} while (op != outRec.Pts);
|
||||
} while (op != startOp);
|
||||
return a * 0.5;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
double Area(const OutRec &outRec)
|
||||
{
|
||||
return Area(outRec.Pts);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool PointIsVertex(const IntPoint &Pt, OutPt *pp)
|
||||
{
|
||||
OutPt *pp2 = pp;
|
||||
|
@ -524,27 +534,32 @@ bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2)
|
|||
p = btmPt2->Next;
|
||||
while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next;
|
||||
double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt));
|
||||
return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n);
|
||||
|
||||
if (std::max(dx1p, dx1n) == std::max(dx2p, dx2n) &&
|
||||
std::min(dx1p, dx1n) == std::min(dx2p, dx2n))
|
||||
return Area(btmPt1) > 0; //if otherwise identical use orientation
|
||||
else
|
||||
return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Called by GetLowermostRec()
|
||||
OutPt* GetBottomPt(OutPt *pp)
|
||||
{
|
||||
OutPt* dups = 0;
|
||||
OutPt* dups = nullptr;
|
||||
OutPt* p = pp->Next;
|
||||
while (p != pp)
|
||||
{
|
||||
if (p->Pt.y() > pp->Pt.y())
|
||||
{
|
||||
pp = p;
|
||||
dups = 0;
|
||||
dups = nullptr;
|
||||
}
|
||||
else if (p->Pt.y() == pp->Pt.y() && p->Pt.x() <= pp->Pt.x())
|
||||
{
|
||||
if (p->Pt.x() < pp->Pt.x())
|
||||
{
|
||||
dups = 0;
|
||||
dups = nullptr;
|
||||
pp = p;
|
||||
} else
|
||||
{
|
||||
|
@ -565,6 +580,7 @@ OutPt* GetBottomPt(OutPt *pp)
|
|||
}
|
||||
return pp;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool Pt2IsBetweenPt1AndPt3(const IntPoint &pt1,
|
||||
|
|
|
@ -38,16 +38,16 @@ class AnyPtr {
|
|||
}
|
||||
|
||||
public:
|
||||
template<class TT = T, class = std::enable_if_t<std::is_convertible_v<TT, T>>>
|
||||
template<class TT = T, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
|
||||
AnyPtr(TT *p = nullptr) : ptr{p}
|
||||
{}
|
||||
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT, T>>>
|
||||
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
|
||||
AnyPtr(std::unique_ptr<TT> p) : ptr{std::unique_ptr<T>(std::move(p))}
|
||||
{}
|
||||
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT, T>>>
|
||||
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
|
||||
AnyPtr(std::shared_ptr<TT> p) : ptr{std::shared_ptr<T>(std::move(p))}
|
||||
{}
|
||||
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT, T>>>
|
||||
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
|
||||
AnyPtr(std::weak_ptr<TT> p) : ptr{std::weak_ptr<T>(std::move(p))}
|
||||
{}
|
||||
|
||||
|
@ -59,16 +59,16 @@ public:
|
|||
AnyPtr &operator=(AnyPtr &&other) noexcept { ptr = std::move(other.ptr); return *this; }
|
||||
AnyPtr &operator=(const AnyPtr &other) = delete;
|
||||
|
||||
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT, T>>>
|
||||
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
|
||||
AnyPtr &operator=(TT *p) { ptr = p; return *this; }
|
||||
|
||||
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT, T>>>
|
||||
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
|
||||
AnyPtr &operator=(std::unique_ptr<TT> p) { ptr = std::move(p); return *this; }
|
||||
|
||||
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT, T>>>
|
||||
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
|
||||
AnyPtr &operator=(std::shared_ptr<TT> p) { ptr = p; return *this; }
|
||||
|
||||
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT, T>>>
|
||||
template<class TT, class = std::enable_if_t<std::is_convertible_v<TT*, T*>>>
|
||||
AnyPtr &operator=(std::weak_ptr<TT> p) { ptr = std::move(p); return *this; }
|
||||
|
||||
const T &operator*() const { return *get_ptr(*this); }
|
||||
|
|
|
@ -26,7 +26,6 @@ MeshBoolean::cgal::CGALMeshPtr get_cgalmesh(const CSGPartT &csgpart)
|
|||
MeshBoolean::cgal::CGALMeshPtr ret;
|
||||
|
||||
indexed_triangle_set m = *its;
|
||||
auto tr = get_transform(csgpart);
|
||||
its_transform(m, get_transform(csgpart), true);
|
||||
|
||||
try {
|
||||
|
|
|
@ -1194,7 +1194,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
m_placeholder_parser.set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
|
||||
|
||||
std::vector<unsigned char> is_extruder_used(print.config().nozzle_diameter.size(), 0);
|
||||
for (unsigned int extruder_id : print.extruders())
|
||||
for (unsigned int extruder_id : tool_ordering.all_extruders())
|
||||
is_extruder_used[extruder_id] = true;
|
||||
m_placeholder_parser.set("is_extruder_used", new ConfigOptionBools(is_extruder_used));
|
||||
}
|
||||
|
@ -2793,8 +2793,14 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
|
|||
acceleration = m_config.first_layer_acceleration_over_raft.value;
|
||||
} else if (m_config.bridge_acceleration.value > 0 && path.role().is_bridge()) {
|
||||
acceleration = m_config.bridge_acceleration.value;
|
||||
} else if (m_config.top_solid_infill_acceleration > 0 && path.role() == ExtrusionRole::TopSolidInfill) {
|
||||
acceleration = m_config.top_solid_infill_acceleration.value;
|
||||
} else if (m_config.solid_infill_acceleration > 0 && path.role().is_solid_infill()) {
|
||||
acceleration = m_config.solid_infill_acceleration.value;
|
||||
} else if (m_config.infill_acceleration.value > 0 && path.role().is_infill()) {
|
||||
acceleration = m_config.infill_acceleration.value;
|
||||
} else if (m_config.external_perimeter_acceleration > 0 && path.role().is_external_perimeter()) {
|
||||
acceleration = m_config.external_perimeter_acceleration.value;
|
||||
} else if (m_config.perimeter_acceleration.value > 0 && path.role().is_perimeter()) {
|
||||
acceleration = m_config.perimeter_acceleration.value;
|
||||
} else {
|
||||
|
|
|
@ -3378,9 +3378,9 @@ void GCodeProcessor::process_T(const std::string_view command)
|
|||
extra_time += m_kissslicer_toolchange_time_correction;
|
||||
simulate_st_synchronize(extra_time);
|
||||
|
||||
// specific to single extruder multi material, set the extruder temperature
|
||||
// if not done yet
|
||||
if (m_single_extruder_multi_material && m_extruder_temps[m_extruder_id] == 0.0f)
|
||||
// specific to single extruder multi material, set the new extruder temperature
|
||||
// to match the old one
|
||||
if (m_single_extruder_multi_material)
|
||||
m_extruder_temps[m_extruder_id] = m_extruder_temps[old_extruder_id];
|
||||
|
||||
m_result.extruders_count = std::max<size_t>(m_result.extruders_count, m_extruder_id + 1);
|
||||
|
|
|
@ -53,22 +53,7 @@ void Layer::make_slices()
|
|||
this->lslices = slices;
|
||||
}
|
||||
|
||||
// prepare lslices ordered by print order
|
||||
this->lslice_indices_sorted_by_print_order.clear();
|
||||
this->lslice_indices_sorted_by_print_order.reserve(lslices.size());
|
||||
// prepare ordering points
|
||||
Points ordering_points;
|
||||
ordering_points.reserve( this->lslices.size());
|
||||
for (const ExPolygon &ex : this->lslices)
|
||||
ordering_points.push_back(ex.contour.first_point());
|
||||
|
||||
// sort slices
|
||||
std::vector<Points::size_type> order = chain_points(ordering_points);
|
||||
|
||||
// populate slices vector
|
||||
for (size_t i : order) {
|
||||
this->lslice_indices_sorted_by_print_order.emplace_back(i);
|
||||
}
|
||||
this->lslice_indices_sorted_by_print_order = chain_expolygons(this->lslices);
|
||||
}
|
||||
|
||||
// used by Layer::build_up_down_graph()
|
||||
|
@ -105,7 +90,7 @@ static void connect_layer_slices(
|
|||
const coord_t offset_below,
|
||||
const coord_t offset_above
|
||||
#ifndef NDEBUG
|
||||
, const coord_t offset_end
|
||||
, const coord_t offset_end
|
||||
#endif // NDEBUG
|
||||
)
|
||||
{
|
||||
|
@ -127,9 +112,7 @@ static void connect_layer_slices(
|
|||
{
|
||||
#ifndef NDEBUG
|
||||
auto assert_intersection_valid = [this](int i, int j) {
|
||||
assert(i != j);
|
||||
if (i > j)
|
||||
std::swap(i, j);
|
||||
assert(i < j);
|
||||
assert(i >= m_offset_below);
|
||||
assert(i < m_offset_above);
|
||||
assert(j >= m_offset_above);
|
||||
|
@ -140,35 +123,47 @@ static void connect_layer_slices(
|
|||
if (polynode.Contour.size() >= 3) {
|
||||
// If there is an intersection point, it should indicate which contours (one from layer below, the other from layer above) intersect.
|
||||
// Otherwise the contour is fully inside another contour.
|
||||
int32_t i = 0, j = 0;
|
||||
int32_t i = -1, j = -1;
|
||||
for (int icontour = 0; icontour <= polynode.ChildCount(); ++ icontour) {
|
||||
const bool first = icontour == 0;
|
||||
const ClipperLib_Z::Path &contour = first ? polynode.Contour : polynode.Childs[icontour - 1]->Contour;
|
||||
const ClipperLib_Z::Path &contour = icontour == 0 ? polynode.Contour : polynode.Childs[icontour - 1]->Contour;
|
||||
if (contour.size() >= 3) {
|
||||
if (first) {
|
||||
i = contour.front().z();
|
||||
j = i;
|
||||
if (i < 0) {
|
||||
std::tie(i, j) = m_intersections[-i - 1];
|
||||
assert(assert_intersection_valid(i, j));
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
for (const ClipperLib_Z::IntPoint& pt : contour) {
|
||||
for (const ClipperLib_Z::IntPoint &pt : contour) {
|
||||
j = pt.z();
|
||||
if (j < 0) {
|
||||
std::tie(i, j) = m_intersections[-j - 1];
|
||||
const auto &intersection = m_intersections[-j - 1];
|
||||
assert(intersection.first <= intersection.second);
|
||||
if (intersection.second < m_offset_above) {
|
||||
// Ignore intersection of polygons on the 1st layer.
|
||||
assert(intersection.first >= m_offset_below);
|
||||
j = i;
|
||||
} else if (intersection.first >= m_offset_above) {
|
||||
// Ignore intersection of polygons on the 2nd layer
|
||||
assert(intersection.second < m_offset_end);
|
||||
j = i;
|
||||
} else {
|
||||
std::tie(i, j) = m_intersections[-j - 1];
|
||||
assert(assert_intersection_valid(i, j));
|
||||
goto end;
|
||||
}
|
||||
} else if (i == -1) {
|
||||
// First source contour of this expolygon was found.
|
||||
i = j;
|
||||
} else if (i != j) {
|
||||
// Second source contour of this expolygon was found.
|
||||
if (i > j)
|
||||
std::swap(i, j);
|
||||
assert(assert_intersection_valid(i, j));
|
||||
goto end;
|
||||
}
|
||||
else if (i != j)
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
end:
|
||||
bool found = false;
|
||||
if (i == j) {
|
||||
if (i == -1) {
|
||||
// This should not happen. It may only happen if the source contours had just self intersections or intersections with contours at the same layer.
|
||||
assert(false);
|
||||
} else if (i == j) {
|
||||
// The contour is completely inside another contour.
|
||||
Point pt(polynode.Contour.front().x(), polynode.Contour.front().y());
|
||||
if (i < m_offset_above) {
|
||||
|
@ -202,8 +197,6 @@ static void connect_layer_slices(
|
|||
}
|
||||
} else {
|
||||
assert(assert_intersection_valid(i, j));
|
||||
if (i > j)
|
||||
std::swap(i, j);
|
||||
i -= m_offset_below;
|
||||
j -= m_offset_above;
|
||||
assert(i >= 0 && i < m_below.lslices_ex.size());
|
||||
|
|
|
@ -318,7 +318,7 @@ public:
|
|||
|
||||
Layer *upper_layer;
|
||||
Layer *lower_layer;
|
||||
bool slicing_errors;
|
||||
// bool slicing_errors;
|
||||
coordf_t slice_z; // Z used for slicing in unscaled coordinates
|
||||
coordf_t print_z; // Z used for printing in unscaled coordinates
|
||||
coordf_t height; // layer height in unscaled coordinates
|
||||
|
@ -387,7 +387,8 @@ protected:
|
|||
friend std::string fix_slicing_errors(LayerPtrs&, const std::function<void()>&);
|
||||
|
||||
Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) :
|
||||
upper_layer(nullptr), lower_layer(nullptr), slicing_errors(false),
|
||||
upper_layer(nullptr), lower_layer(nullptr),
|
||||
//slicing_errors(false),
|
||||
slice_z(slice_z), print_z(print_z), height(height),
|
||||
m_id(id), m_object(object) {}
|
||||
virtual ~Layer();
|
||||
|
|
|
@ -443,6 +443,7 @@ static std::vector<std::string> s_Preset_print_options {
|
|||
"enable_dynamic_overhang_speeds", "dynamic_overhang_speeds", "overhang_overlap_levels",
|
||||
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
|
||||
"bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "first_layer_speed_over_raft", "perimeter_acceleration", "infill_acceleration",
|
||||
"external_perimeter_acceleration", "top_solid_infill_acceleration", "solid_infill_acceleration",
|
||||
"bridge_acceleration", "first_layer_acceleration", "first_layer_acceleration_over_raft", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
|
||||
"min_skirt_length", "brim_width", "brim_separation", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
|
||||
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
|
||||
|
@ -457,7 +458,7 @@ static std::vector<std::string> s_Preset_print_options {
|
|||
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
|
||||
"ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
|
||||
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
|
||||
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "clip_multipart_objects",
|
||||
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio",
|
||||
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "gcode_resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
|
||||
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
|
||||
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||
|
|
|
@ -74,6 +74,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||
"duplicate_distance",
|
||||
"end_gcode",
|
||||
"end_filament_gcode",
|
||||
"external_perimeter_acceleration",
|
||||
"extrusion_axis",
|
||||
"extruder_clearance_height",
|
||||
"extruder_clearance_radius",
|
||||
|
@ -125,10 +126,12 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||
"retract_speed",
|
||||
"single_extruder_multi_material_priming",
|
||||
"slowdown_below_layer_time",
|
||||
"solid_infill_acceleration",
|
||||
"standby_temperature_delta",
|
||||
"start_gcode",
|
||||
"start_filament_gcode",
|
||||
"toolchange_gcode",
|
||||
"top_solid_infill_acceleration",
|
||||
"thumbnails",
|
||||
"thumbnails_format",
|
||||
"use_firmware_retraction",
|
||||
|
|
|
@ -598,14 +598,6 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.f));
|
||||
|
||||
def = this->add("clip_multipart_objects", coBool);
|
||||
def->label = L("Clip multi-part objects");
|
||||
def->tooltip = L("When printing multi-material objects, this settings will make Slic3r "
|
||||
"to clip the overlapping object parts one by the other "
|
||||
"(2nd part will be clipped by the 1st, 3rd part will be clipped by the 1st and 2nd etc).");
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
def = this->add("colorprint_heights", coFloats);
|
||||
def->label = L("Colorprint height");
|
||||
def->tooltip = L("Heights at which a filament change is to occur.");
|
||||
|
@ -1398,12 +1390,31 @@ void PrintConfigDef::init_fff_params()
|
|||
def = this->add("infill_acceleration", coFloat);
|
||||
def->label = L("Infill");
|
||||
def->tooltip = L("This is the acceleration your printer will use for infill. Set zero to disable "
|
||||
"acceleration control for infill.");
|
||||
"acceleration control for infill.");
|
||||
def->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("solid_infill_acceleration", coFloat);
|
||||
def->label = L("Solid infill");
|
||||
def->tooltip = L("This is the acceleration your printer will use for solid infill. Set zero to use "
|
||||
"the value for infill.");
|
||||
def->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("top_solid_infill_acceleration", coFloat);
|
||||
def->label = L("Top solid infill");
|
||||
def->tooltip = L("This is the acceleration your printer will use for top solid infill. Set zero to use "
|
||||
"the value for solid infill.");
|
||||
def->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
|
||||
def = this->add("infill_every_layers", coInt);
|
||||
def->label = L("Combine infill every");
|
||||
def->category = L("Infill");
|
||||
|
@ -1950,6 +1961,14 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("external_perimeter_acceleration", coFloat);
|
||||
def->label = L("External perimeters");
|
||||
def->tooltip = L("This is the acceleration your printer will use for external perimeters. "
|
||||
"Set zero to use the value for perimeters.");
|
||||
def->sidetext = L("mm/s²");
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("perimeter_extruder", coInt);
|
||||
def->label = L("Perimeter extruder");
|
||||
def->category = L("Extruders");
|
||||
|
@ -4025,6 +4044,22 @@ void PrintConfigDef::init_sla_params()
|
|||
def->set_default_value(new ConfigOptionFloat(0.001));
|
||||
}
|
||||
|
||||
// Ignore the following obsolete configuration keys:
|
||||
static std::set<std::string> PrintConfigDef_ignore = {
|
||||
"clip_multipart_objects",
|
||||
"duplicate_x", "duplicate_y", "gcode_arcs", "multiply_x", "multiply_y",
|
||||
"support_material_tool", "acceleration", "adjust_overhang_flow",
|
||||
"standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid",
|
||||
"start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start",
|
||||
"seal_position", "vibration_limit", "bed_size",
|
||||
"print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe",
|
||||
"serial_port", "serial_speed",
|
||||
// Introduced in some PrusaSlicer 2.3.1 alpha, later renamed or removed.
|
||||
"fuzzy_skin_perimeter_mode", "fuzzy_skin_shape",
|
||||
// Introduced in PrusaSlicer 2.3.0-alpha2, later replaced by automatic calculation based on extrusion width.
|
||||
"wall_add_middle_threshold", "wall_split_middle_threshold",
|
||||
};
|
||||
|
||||
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
|
||||
{
|
||||
// handle legacy options
|
||||
|
@ -4098,32 +4133,17 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||
}
|
||||
}*/
|
||||
|
||||
// Ignore the following obsolete configuration keys:
|
||||
static std::set<std::string> ignore = {
|
||||
"duplicate_x", "duplicate_y", "gcode_arcs", "multiply_x", "multiply_y",
|
||||
"support_material_tool", "acceleration", "adjust_overhang_flow",
|
||||
"standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid",
|
||||
"start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start",
|
||||
"seal_position", "vibration_limit", "bed_size",
|
||||
"print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe",
|
||||
"serial_port", "serial_speed",
|
||||
// Introduced in some PrusaSlicer 2.3.1 alpha, later renamed or removed.
|
||||
"fuzzy_skin_perimeter_mode", "fuzzy_skin_shape",
|
||||
// Introduced in PrusaSlicer 2.3.0-alpha2, later replaced by automatic calculation based on extrusion width.
|
||||
"wall_add_middle_threshold", "wall_split_middle_threshold",
|
||||
};
|
||||
|
||||
// In PrusaSlicer 2.3.0-alpha0 the "monotonous" infill was introduced, which was later renamed to "monotonic".
|
||||
if (value == "monotonous" && (opt_key == "top_fill_pattern" || opt_key == "bottom_fill_pattern" || opt_key == "fill_pattern"))
|
||||
value = "monotonic";
|
||||
|
||||
if (ignore.find(opt_key) != ignore.end()) {
|
||||
opt_key = "";
|
||||
if (PrintConfigDef_ignore.find(opt_key) != PrintConfigDef_ignore.end()) {
|
||||
opt_key = {};
|
||||
return;
|
||||
}
|
||||
|
||||
if (! print_config_def.has(opt_key)) {
|
||||
opt_key = "";
|
||||
opt_key = {};
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -487,7 +487,6 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionFloat, brim_separation))
|
||||
((ConfigOptionEnum<BrimType>, brim_type))
|
||||
((ConfigOptionFloat, brim_width))
|
||||
((ConfigOptionBool, clip_multipart_objects))
|
||||
((ConfigOptionBool, dont_support_bridges))
|
||||
((ConfigOptionFloat, elefant_foot_compensation))
|
||||
((ConfigOptionFloatOrPercent, extrusion_width))
|
||||
|
@ -755,6 +754,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
|||
((ConfigOptionInts, disable_fan_first_layers))
|
||||
((ConfigOptionEnum<DraftShield>, draft_shield))
|
||||
((ConfigOptionFloat, duplicate_distance))
|
||||
((ConfigOptionFloat, external_perimeter_acceleration))
|
||||
((ConfigOptionFloat, extruder_clearance_height))
|
||||
((ConfigOptionFloat, extruder_clearance_radius))
|
||||
((ConfigOptionStrings, extruder_colour))
|
||||
|
@ -797,12 +797,14 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
|||
((ConfigOptionInt, skirt_height))
|
||||
((ConfigOptionInt, skirts))
|
||||
((ConfigOptionInts, slowdown_below_layer_time))
|
||||
((ConfigOptionFloat, solid_infill_acceleration))
|
||||
((ConfigOptionBool, spiral_vase))
|
||||
((ConfigOptionInt, standby_temperature_delta))
|
||||
((ConfigOptionInts, temperature))
|
||||
((ConfigOptionInt, threads))
|
||||
((ConfigOptionPoints, thumbnails))
|
||||
((ConfigOptionEnum<GCodeThumbnailsFormat>, thumbnails_format))
|
||||
((ConfigOptionFloat, top_solid_infill_acceleration))
|
||||
((ConfigOptionBools, wipe))
|
||||
((ConfigOptionBool, wipe_tower))
|
||||
((ConfigOptionFloat, wipe_tower_x))
|
||||
|
|
|
@ -424,8 +424,12 @@ void PrintObject::generate_support_spots()
|
|||
float(this->print()->m_config.perimeter_acceleration.getFloat()),
|
||||
this->config().raft_layers.getInt(), this->config().brim_type.value,
|
||||
float(this->config().brim_width.getFloat())};
|
||||
auto [supp_points, partial_objects] = SupportSpotsGenerator::full_search(this, cancel_func, params);
|
||||
this->m_shared_regions->generated_support_points = {this->trafo_centered(), supp_points, partial_objects};
|
||||
auto [supp_points, partial_objects] = SupportSpotsGenerator::full_search(this, cancel_func, params);
|
||||
Transform3d po_transform = this->trafo_centered();
|
||||
if (this->layer_count() > 0) {
|
||||
po_transform = Geometry::translation_transform(Vec3d{0, 0, this->layers().front()->bottom_z()}) * po_transform;
|
||||
}
|
||||
this->m_shared_regions->generated_support_points = {po_transform, supp_points, partial_objects};
|
||||
m_print->throw_if_canceled();
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(debug) << "Searching support spots - end";
|
||||
|
@ -620,8 +624,7 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||
|| opt_key == "slicing_mode") {
|
||||
steps.emplace_back(posSlice);
|
||||
} else if (
|
||||
opt_key == "clip_multipart_objects"
|
||||
|| opt_key == "elefant_foot_compensation"
|
||||
opt_key == "elefant_foot_compensation"
|
||||
|| opt_key == "support_material_contact_distance"
|
||||
|| opt_key == "xy_size_compensation") {
|
||||
steps.emplace_back(posSlice);
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#include "ClipperUtils.hpp"
|
||||
#include "ElephantFootCompensation.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "Layer.hpp"
|
||||
#include "MultiMaterialSegmentation.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
|
@ -237,9 +238,6 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
|||
const PrintObjectRegions &print_object_regions,
|
||||
const std::vector<float> &zs,
|
||||
std::vector<VolumeSlices> &&volume_slices,
|
||||
// If clipping is disabled, then ExPolygons produced by different volumes will never be merged, thus they will be allowed to overlap.
|
||||
// It is up to the model designer to handle these overlaps.
|
||||
const bool clip_multipart_objects,
|
||||
const std::function<void()> &throw_on_cancel_callback)
|
||||
{
|
||||
model_volumes_sort_by_id(model_volumes);
|
||||
|
@ -308,7 +306,7 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
|||
}
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, zs_complex.size()),
|
||||
[&slices_by_region, &print_object_regions, &zs_complex, &layer_ranges_regions_to_slices, clip_multipart_objects, &throw_on_cancel_callback]
|
||||
[&slices_by_region, &print_object_regions, &zs_complex, &layer_ranges_regions_to_slices, &throw_on_cancel_callback]
|
||||
(const tbb::blocked_range<size_t> &range) {
|
||||
float z = zs_complex[range.begin()].second;
|
||||
auto it_layer_range = layer_range_first(print_object_regions.layer_ranges, z);
|
||||
|
@ -359,7 +357,7 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
|||
if (next_region_same_modifier)
|
||||
// To be used in the following iteration.
|
||||
temp_slices[idx_region + 1].expolygons = std::move(source);
|
||||
} else if ((region.model_volume->is_model_part() && clip_multipart_objects) || region.model_volume->is_negative_volume()) {
|
||||
} else if (region.model_volume->is_model_part() || region.model_volume->is_negative_volume()) {
|
||||
// Clip every non-zero region preceding it.
|
||||
for (int idx_region2 = 0; idx_region2 < idx_region; ++ idx_region2)
|
||||
if (! temp_slices[idx_region2].expolygons.empty()) {
|
||||
|
@ -388,10 +386,7 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
|||
merged = true;
|
||||
}
|
||||
}
|
||||
// Don't unite the regions if ! clip_multipart_objects. In that case it is user's responsibility
|
||||
// to handle region overlaps. Indeed, one may intentionally let the regions overlap to produce crossing perimeters
|
||||
// for example.
|
||||
if (merged && clip_multipart_objects)
|
||||
if (merged)
|
||||
expolygons = closing_ex(expolygons, float(scale_(EPSILON)));
|
||||
slices_by_region[temp_slices[i].region_id][z_idx] = std::move(expolygons);
|
||||
i = j;
|
||||
|
@ -404,6 +399,10 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
|||
return slices_by_region;
|
||||
}
|
||||
|
||||
// Layer::slicing_errors is no more set since 1.41.1 or possibly earlier, thus this code
|
||||
// was not really functional for a long day and nobody missed it.
|
||||
// Could we reuse this fixing code one day?
|
||||
/*
|
||||
std::string fix_slicing_errors(LayerPtrs &layers, const std::function<void()> &throw_if_canceled)
|
||||
{
|
||||
// Collect layers with slicing errors.
|
||||
|
@ -486,6 +485,7 @@ std::string fix_slicing_errors(LayerPtrs &layers, const std::function<void()> &t
|
|||
"The model has overlapping or self-intersecting facets. I tried to repair it, "
|
||||
"however you might want to check the results or repair the input file and retry.\n";
|
||||
}
|
||||
*/
|
||||
|
||||
// Called by make_perimeters()
|
||||
// 1) Decides Z positions of the layers,
|
||||
|
@ -508,12 +508,18 @@ void PrintObject::slice()
|
|||
m_layers = new_layers(this, generate_object_layers(m_slicing_params, layer_height_profile));
|
||||
this->slice_volumes();
|
||||
m_print->throw_if_canceled();
|
||||
#if 0
|
||||
// Layer::slicing_errors is no more set since 1.41.1 or possibly earlier, thus this code
|
||||
// was not really functional for a long day and nobody missed it.
|
||||
// Could we reuse this fixing code one day?
|
||||
|
||||
// Fix the model.
|
||||
//FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend.
|
||||
std::string warning = fix_slicing_errors(m_layers, [this](){ m_print->throw_if_canceled(); });
|
||||
m_print->throw_if_canceled();
|
||||
if (! warning.empty())
|
||||
BOOST_LOG_TRIVIAL(info) << warning;
|
||||
#endif
|
||||
// Update bounding boxes, back up raw slices of complex models.
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||
|
@ -696,7 +702,6 @@ void PrintObject::slice_volumes()
|
|||
slice_volumes_inner(
|
||||
print->config(), this->config(), this->trafo_centered(),
|
||||
this->model_object()->volumes, m_shared_regions->layer_ranges, slice_zs, throw_on_cancel_callback),
|
||||
m_config.clip_multipart_objects,
|
||||
throw_on_cancel_callback);
|
||||
|
||||
for (size_t region_id = 0; region_id < region_slices.size(); ++ region_id) {
|
||||
|
@ -806,8 +811,12 @@ void PrintObject::slice_volumes()
|
|||
if (elephant_foot_compensation_scaled > 0.f && ! m_layers.empty()) {
|
||||
// The Elephant foot has been compensated, therefore the 1st layer's lslices are shrank with the Elephant foot compensation value.
|
||||
// Store the uncompensated value there.
|
||||
assert(m_layers.front()->id() == 0);
|
||||
m_layers.front()->lslices = std::move(lslices_1st_layer);
|
||||
//FIXME is this operation needed? MMU painting and brim now have to do work arounds to work with compensated layer, not with the uncompensated layer.
|
||||
// There may be subtle issues removing this block such as support raft sticking too well with the first object layer.
|
||||
Layer &layer = *m_layers.front();
|
||||
assert(layer.id() == 0);
|
||||
layer.lslices = std::move(lslices_1st_layer);
|
||||
layer.lslice_indices_sorted_by_print_order = chain_expolygons(layer.lslices);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1076,6 +1076,15 @@ std::vector<size_t> chain_points(const Points &points, Point *start_near)
|
|||
return out;
|
||||
}
|
||||
|
||||
std::vector<size_t> chain_expolygons(const ExPolygons &expolygons, Point *start_near)
|
||||
{
|
||||
Points ordering_points;
|
||||
ordering_points.reserve(expolygons.size());
|
||||
for (const ExPolygon &ex : expolygons)
|
||||
ordering_points.push_back(ex.contour.first_point());
|
||||
return chain_points(ordering_points);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// #define DEBUG_SVG_OUTPUT
|
||||
#endif /* NDEBUG */
|
||||
|
|
|
@ -12,7 +12,11 @@ namespace ClipperLib { class PolyNode; }
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
class ExPolygon;
|
||||
using ExPolygons = std::vector<ExPolygon>;
|
||||
|
||||
std::vector<size_t> chain_points(const Points &points, Point *start_near = nullptr);
|
||||
std::vector<size_t> chain_expolygons(const ExPolygons &expolygons, Point *start_near = nullptr);
|
||||
|
||||
std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
|
||||
void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const std::vector<std::pair<size_t, bool>> &chain);
|
||||
|
|
|
@ -269,7 +269,8 @@ void set_current_thread_qos()
|
|||
#ifdef __APPLE__
|
||||
// OSX specific: Set Quality of Service to "user initiated", so that the threads will be scheduled to high performance
|
||||
// cores if available.
|
||||
pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0);
|
||||
// With QOS_CLASS_USER_INITIATED the worker threads drop priority once slicer loses user focus.
|
||||
pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
|
||||
#endif // __APPLE__
|
||||
}
|
||||
|
||||
|
|
|
@ -493,6 +493,9 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
|||
if (height == 0.0f)
|
||||
height = 0.1f;
|
||||
|
||||
static const float brim_height = 0.2f;
|
||||
const float scaled_brim_height = brim_height / height;
|
||||
|
||||
TriangleMesh mesh;
|
||||
ColorRGBA color = ColorRGBA::DARK_YELLOW();
|
||||
|
||||
|
@ -506,8 +509,6 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
|||
depth = std::max(depth, 10.f);
|
||||
float min_width = 30.f;
|
||||
|
||||
const float scaled_brim_height = 0.2f / height;
|
||||
|
||||
// We'll now create the box with jagged edge. y-coordinates of the pre-generated model
|
||||
// are shifted so that the front edge has y=0 and centerline of the back edge has y=depth:
|
||||
// We split the box in three main pieces,
|
||||
|
@ -553,6 +554,10 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
|||
// central parts generator
|
||||
auto generate_central = [&]() {
|
||||
const std::vector<Vec3f> vertices = {
|
||||
// this part is not watertight to avoid to have different geometries for the cases
|
||||
// brim_width < 10.0
|
||||
// brim_width == 10.0
|
||||
// brim_width > 10.0
|
||||
{ 38.453f, -(depth + brim_width), 0.0f },
|
||||
{ 61.547f, -(depth + brim_width), 0.0f },
|
||||
{ 38.453f, -(depth + brim_width), scaled_brim_height },
|
||||
|
@ -562,33 +567,33 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
|||
{ 38.453f, -depth, 1.0f },
|
||||
{ 61.547f, -depth, 1.0f },
|
||||
{ 38.453f, 0.0f, 1.0f },
|
||||
{ 38.453f + 0.57735f * brim_width, brim_width, 1.0f },
|
||||
{ 44.2265f, 10.0f, 1.0f },
|
||||
{ 50.0f - 0.57735f * brim_width, brim_width, 1.0f },
|
||||
{ 50.0f, 0.0f, 1.0f },
|
||||
{ 55.7735f, -10.0f, 1.0f },
|
||||
{ 61.547f, 0.0f, 1.0f },
|
||||
{ 38.453f, 0.0f, scaled_brim_height },
|
||||
{ 38.453f, brim_width, scaled_brim_height },
|
||||
{ 38.453f + 0.57735f * brim_width, brim_width, scaled_brim_height },
|
||||
{ 50.0f - 0.57735f * brim_width, brim_width, scaled_brim_height },
|
||||
{ 44.2265f, 10.0f, scaled_brim_height },
|
||||
{ 50.0f, 0.0f, scaled_brim_height },
|
||||
{ 55.7735f, -10.0f, scaled_brim_height },
|
||||
{ 61.547f, 0.0f, scaled_brim_height },
|
||||
{ 38.453f, 0.0f, 0.0f },
|
||||
{ 44.2265f, 10.0f, 0.0f },
|
||||
{ 50.0f, 0.0f, 0.0f },
|
||||
{ 55.7735f, -10.0f, 0.0f },
|
||||
{ 61.547f, 0.0f, 0.0f },
|
||||
{ 38.453f, brim_width, scaled_brim_height },
|
||||
{ 61.547f, brim_width, scaled_brim_height },
|
||||
{ 38.453f, brim_width, 0.0f },
|
||||
{ 38.453f + 0.57735f * brim_width, brim_width, 0.0f },
|
||||
{ 44.2265f, 10.0f, 0.0f },
|
||||
{ 50.0f - 0.57735f * brim_width, brim_width, 0.0f },
|
||||
{ 61.547f, brim_width, 0.0f }
|
||||
{ 61.547f, brim_width, 0.0f },
|
||||
};
|
||||
|
||||
const std::vector<Vec3i> triangles = {
|
||||
{ 0, 1, 3 }, { 0, 3, 2 }, { 2, 3, 5 }, { 2, 5, 4 }, { 4, 5, 7 }, { 4, 7, 6 }, { 7, 14, 13 }, { 7, 13, 6 },
|
||||
{ 6, 13, 12 }, { 6, 12, 8 }, { 8, 12, 11 }, { 8, 11, 9 }, { 9, 11, 10 }, { 18, 19, 22 }, { 22, 19, 21 }, { 19, 20, 21 },
|
||||
{ 15, 17, 16 }, { 17, 15, 8 }, { 17, 8, 9 }, { 21, 13, 14 }, { 21, 20, 13 }, { 20, 19, 12 }, { 20, 12, 13 }, { 19, 18, 11 },
|
||||
{ 19, 11, 12 }, { 27, 26, 18 }, { 27, 18, 22 }, { 26, 25, 18 }, { 18, 25, 11 }, { 11, 25, 10 }, { 25, 24, 17 }, { 25, 17, 9 },
|
||||
{ 25, 9, 10 }, { 24, 23, 16 }, { 24, 16, 17 }, { 1, 26, 27 }, { 1, 23, 26 }, { 1, 0, 23 }, { 0, 23, 24 }, { 24, 25, 26 }
|
||||
{ 0, 1, 3 }, { 0, 3, 2 }, { 2, 3, 5 }, { 2, 5, 4 }, { 4, 5, 7 }, { 4, 7, 6 },
|
||||
{ 6, 7, 11 }, { 6, 11, 10 }, { 6, 10, 8 }, { 8, 10, 9 }, { 11, 7, 12 }, { 14, 13, 8 },
|
||||
{ 14, 8, 9 }, { 19, 18, 13 }, { 19, 13, 14 }, { 15, 14, 9 }, { 15, 9, 10 }, { 20, 19, 14 },
|
||||
{ 20, 14, 15 }, { 16, 15, 10 }, { 16, 10, 11 }, { 21, 20, 15 }, { 21, 15, 16 }, { 17, 16, 11 },
|
||||
{ 17, 11, 12 }, { 22, 21, 16 }, { 22, 16, 17 }, { 15, 16, 17 }, { 13, 15, 23 }, { 15, 17, 24 },
|
||||
{ 15, 24, 23 }, { 26, 25, 23 }, { 26, 23, 24 }, { 0, 25, 1 }, { 1, 25, 26 }, { 20, 18, 19 }
|
||||
};
|
||||
|
||||
indexed_triangle_set its;
|
||||
|
@ -614,7 +619,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
|||
// We have the mesh ready. It has one tooth and width of min_width. We will now
|
||||
// append several of these together until we are close to the required width
|
||||
// of the block. Than we can scale it precisely.
|
||||
size_t n = std::max(1, int(width / min_width)); // How many shall be merged?
|
||||
const size_t n = std::max(1, int(width / min_width)); // How many shall be merged?
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
mesh.merge(tooth_mesh);
|
||||
tooth_mesh.translate(100.0f, 0.0f, 0.0f);
|
||||
|
@ -695,8 +700,13 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
|||
mesh.merge(TriangleMesh(std::move(data)));
|
||||
mesh.scale(Vec3f(width / (n * 100.0f), 1.0f, height)); // Scaling to proper width
|
||||
}
|
||||
else
|
||||
mesh = make_cube(width, depth, height);
|
||||
else {
|
||||
mesh = make_cube(width, depth, height - brim_height);
|
||||
mesh.translate(0.0f, 0.0f, brim_height);
|
||||
TriangleMesh brim_mesh = make_cube(width + 2.0f * brim_width, depth + 2.0f * brim_width, brim_height);
|
||||
brim_mesh.translate(-brim_width, -brim_width, 0.0f);
|
||||
mesh.merge(brim_mesh);
|
||||
}
|
||||
|
||||
volumes.emplace_back(new GLVolume(color));
|
||||
GLVolume& v = *volumes.back();
|
||||
|
|
|
@ -261,7 +261,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
|||
toggle_field(el, has_top_solid_infill || (has_spiral_vase && has_bottom_solid_infill));
|
||||
|
||||
bool have_default_acceleration = config->opt_float("default_acceleration") > 0;
|
||||
for (auto el : { "perimeter_acceleration", "infill_acceleration",
|
||||
for (auto el : { "perimeter_acceleration", "infill_acceleration", "top_solid_infill_acceleration",
|
||||
"solid_infill_acceleration", "external_perimeter_acceleration"
|
||||
"bridge_acceleration", "first_layer_acceleration" })
|
||||
toggle_field(el, have_default_acceleration);
|
||||
|
||||
|
|
|
@ -1315,10 +1315,12 @@ PageUpdate::PageUpdate(ConfigWizard *parent)
|
|||
box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); });
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace DownloaderUtils
|
||||
{
|
||||
namespace {
|
||||
#ifdef _WIN32
|
||||
|
||||
wxString get_downloads_path()
|
||||
{
|
||||
wxString ret;
|
||||
|
@ -1330,7 +1332,6 @@ namespace DownloaderUtils
|
|||
CoTaskMemFree(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#elif __APPLE__
|
||||
wxString get_downloads_path()
|
||||
{
|
||||
|
@ -1348,9 +1349,8 @@ namespace DownloaderUtils
|
|||
}
|
||||
return wxString();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
Worker::Worker(wxWindow* parent)
|
||||
: wxBoxSizer(wxHORIZONTAL)
|
||||
, m_parent(parent)
|
||||
|
@ -1432,16 +1432,16 @@ PageDownloader::PageDownloader(ConfigWizard* parent)
|
|||
)));
|
||||
#endif
|
||||
|
||||
box_allow_downloads->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->downloader->allow(event.IsChecked()); });
|
||||
box_allow_downloads->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->m_downloader->allow(event.IsChecked()); });
|
||||
|
||||
downloader = new DownloaderUtils::Worker(this);
|
||||
append(downloader);
|
||||
downloader->allow(box_allow_value);
|
||||
m_downloader = new DownloaderUtils::Worker(this);
|
||||
append(m_downloader);
|
||||
m_downloader->allow(box_allow_value);
|
||||
}
|
||||
|
||||
bool PageDownloader::on_finish_downloader() const
|
||||
{
|
||||
return downloader->on_finish();
|
||||
return m_downloader->on_finish();
|
||||
}
|
||||
|
||||
bool DownloaderUtils::Worker::perform_register(const std::string& path_override/* = {}*/)
|
||||
|
@ -3035,9 +3035,11 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||
|
||||
#ifdef __linux__
|
||||
// Desktop integration on Linux
|
||||
BOOST_LOG_TRIVIAL(debug) << "ConfigWizard::priv::apply_config integrate_desktop" << page_welcome->integrate_desktop() << " perform_registration_linux " << page_downloader->downloader->get_perform_registration_linux();
|
||||
if (page_welcome->integrate_desktop() || page_downloader->downloader->get_perform_registration_linux())
|
||||
DesktopIntegrationDialog::perform_desktop_integration(page_downloader->downloader->get_perform_registration_linux());
|
||||
BOOST_LOG_TRIVIAL(debug) << "ConfigWizard::priv::apply_config integrate_desktop" << page_welcome->integrate_desktop() << " perform_registration_linux " << page_downloader->m_downloader->get_perform_registration_linux();
|
||||
if (page_welcome->integrate_desktop())
|
||||
DesktopIntegrationDialog::perform_desktop_integration();
|
||||
if (page_downloader->m_downloader->get_perform_registration_linux())
|
||||
DesktopIntegrationDialog::perform_downloader_desktop_integration();
|
||||
#endif
|
||||
|
||||
// Decide whether to create snapshot based on run_reason and the reset profile checkbox
|
||||
|
@ -3175,7 +3177,8 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||
|
||||
// apply materials in app_config
|
||||
for (const std::string& section_name : {AppConfig::SECTION_FILAMENTS, AppConfig::SECTION_MATERIALS})
|
||||
app_config->set_section(section_name, appconfig_new.get_section(section_name));
|
||||
if (appconfig_new.has_section(section_name))
|
||||
app_config->set_section(section_name, appconfig_new.get_section(section_name));
|
||||
|
||||
app_config->set_vendors(appconfig_new);
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <memory>
|
||||
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/textctrl.h>
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
|
||||
|
@ -14,6 +16,36 @@ class PresetUpdater;
|
|||
|
||||
namespace GUI {
|
||||
|
||||
namespace DownloaderUtils {
|
||||
class Worker : public wxBoxSizer
|
||||
{
|
||||
wxWindow* m_parent{ nullptr };
|
||||
wxTextCtrl* m_input_path{ nullptr };
|
||||
bool downloader_checked{ false };
|
||||
#ifdef __linux__
|
||||
bool perform_registration_linux{ false };
|
||||
#endif // __linux__
|
||||
|
||||
void deregister();
|
||||
|
||||
public:
|
||||
Worker(wxWindow* parent);
|
||||
~Worker() {}
|
||||
|
||||
void allow(bool allow_) { downloader_checked = allow_; }
|
||||
bool is_checked() const { return downloader_checked; }
|
||||
wxString path_name() const { return m_input_path ? m_input_path->GetValue() : wxString(); }
|
||||
|
||||
void set_path_name(wxString name);
|
||||
void set_path_name(const std::string& name);
|
||||
|
||||
bool on_finish();
|
||||
bool perform_register(const std::string& path_override = {});
|
||||
#ifdef __linux__
|
||||
bool get_perform_registration_linux() { return perform_registration_linux; }
|
||||
#endif // __linux__
|
||||
};
|
||||
}
|
||||
|
||||
class ConfigWizard: public DPIDialog
|
||||
{
|
||||
|
|
|
@ -10,12 +10,10 @@
|
|||
#include <boost/filesystem.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/choice.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/listbox.h>
|
||||
#include <wx/checklst.h>
|
||||
#include <wx/radiobut.h>
|
||||
|
@ -418,44 +416,10 @@ struct PageUpdate: ConfigWizardPage
|
|||
PageUpdate(ConfigWizard *parent);
|
||||
};
|
||||
|
||||
namespace DownloaderUtils {
|
||||
wxString get_downloads_path();
|
||||
|
||||
class Worker : public wxBoxSizer
|
||||
{
|
||||
wxWindow* m_parent {nullptr};
|
||||
wxTextCtrl* m_input_path {nullptr};
|
||||
bool downloader_checked {false};
|
||||
#ifdef __linux__
|
||||
bool perform_registration_linux { false };
|
||||
#endif // __linux__
|
||||
|
||||
void deregister();
|
||||
|
||||
public:
|
||||
Worker(wxWindow* parent);
|
||||
~Worker(){}
|
||||
|
||||
void allow(bool allow_) { downloader_checked = allow_; }
|
||||
bool is_checked() const { return downloader_checked; }
|
||||
wxString path_name() const { return m_input_path ? m_input_path->GetValue() : wxString(); }
|
||||
|
||||
void set_path_name(wxString name);
|
||||
void set_path_name(const std::string& name);
|
||||
|
||||
bool on_finish();
|
||||
bool perform_register(const std::string& path_override = {});
|
||||
#ifdef __linux__
|
||||
bool get_perform_registration_linux() { return perform_registration_linux; }
|
||||
#endif // __linux__
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
struct PageDownloader : ConfigWizardPage
|
||||
{
|
||||
DownloaderUtils::Worker* downloader{ nullptr };
|
||||
DownloaderUtils::Worker* m_downloader { nullptr };
|
||||
|
||||
PageDownloader(ConfigWizard* parent);
|
||||
|
||||
|
|
|
@ -218,9 +218,9 @@ bool DesktopIntegrationDialog::integration_possible()
|
|||
{
|
||||
return true;
|
||||
}
|
||||
void DesktopIntegrationDialog::perform_desktop_integration(bool perform_downloader)
|
||||
void DesktopIntegrationDialog::perform_desktop_integration()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << "performing desktop integration. With downloader integration: " << perform_downloader;
|
||||
BOOST_LOG_TRIVIAL(debug) << "performing desktop integration.";
|
||||
// Path to appimage
|
||||
const char *appimage_env = std::getenv("APPIMAGE");
|
||||
std::string excutable_path;
|
||||
|
@ -423,38 +423,6 @@ void DesktopIntegrationDialog::perform_desktop_integration(bool perform_download
|
|||
show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. PrusaSlicer desktop file was probably created successfully."));
|
||||
}
|
||||
}
|
||||
|
||||
if (perform_downloader)
|
||||
{
|
||||
std::string desktop_file_downloader = GUI::format(
|
||||
"[Desktop Entry]\n"
|
||||
"Name=PrusaSlicer URL Protocol%1%\n"
|
||||
"Exec=\"%3%\" --single-instance %%u\n"
|
||||
"Icon=PrusaSlicer%4%\n"
|
||||
"Terminal=false\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=x-scheme-handler/prusaslicer;\n"
|
||||
"StartupNotify=false\n"
|
||||
, name_suffix, version_suffix, excutable_path, version_suffix);
|
||||
|
||||
// desktop file for downloader as part of main app
|
||||
std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (create_desktop_file(desktop_path, desktop_file_downloader)) {
|
||||
// save path to desktop file
|
||||
app_config->set("desktop_integration_URL_path", desktop_path);
|
||||
// finish registration on mime type
|
||||
std::string command = GUI::format("xdg-mime default PrusaSlicerURLProtocol%1%.desktop x-scheme-handler/prusaslicer", version_suffix);
|
||||
BOOST_LOG_TRIVIAL(debug) << "system command: " << command;
|
||||
int r = system(command.c_str());
|
||||
BOOST_LOG_TRIVIAL(debug) << "system result: " << r;
|
||||
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create URL Protocol desktop file";
|
||||
show_error(nullptr, _L("Performing desktop integration failed - could not create URL Protocol desktop file."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess);
|
||||
}
|
||||
void DesktopIntegrationDialog::undo_desktop_intgration()
|
||||
|
@ -487,15 +455,162 @@ void DesktopIntegrationDialog::undo_desktop_intgration()
|
|||
std::remove(path.c_str());
|
||||
}
|
||||
}
|
||||
// URL Protocol
|
||||
path = std::string(app_config->get("desktop_integration_URL_path"));
|
||||
if (!path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationSuccess);
|
||||
}
|
||||
void DesktopIntegrationDialog::perform_downloader_desktop_integration()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << "performing downloader desktop integration.";
|
||||
// Path to appimage
|
||||
const char* appimage_env = std::getenv("APPIMAGE");
|
||||
std::string excutable_path;
|
||||
if (appimage_env) {
|
||||
try {
|
||||
excutable_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env)).string();
|
||||
}
|
||||
catch (std::exception&) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed - boost::filesystem::canonical did not return appimage path.";
|
||||
show_error(nullptr, _L("Performing downloader desktop integration failed - boost::filesystem::canonical did not return appimage path."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// not appimage - find executable
|
||||
excutable_path = boost::dll::program_location().string();
|
||||
//excutable_path = wxStandardPaths::Get().GetExecutablePath().string();
|
||||
BOOST_LOG_TRIVIAL(debug) << "non-appimage path to executable: " << excutable_path;
|
||||
if (excutable_path.empty())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed - no executable found.";
|
||||
show_error(nullptr, _L("Performing downloader desktop integration failed - Could not find executable."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Escape ' characters in appimage, other special symbols will be esacaped in desktop file by 'excutable_path'
|
||||
//boost::replace_all(excutable_path, "'", "'\\''");
|
||||
excutable_path = escape_string(excutable_path);
|
||||
|
||||
// Find directories icons and applications
|
||||
// $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored.
|
||||
// If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
|
||||
// $XDG_DATA_DIRS defines the preference-ordered set of base directories to search for data files in addition to the $XDG_DATA_HOME base directory.
|
||||
// The directories in $XDG_DATA_DIRS should be seperated with a colon ':'.
|
||||
// If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used.
|
||||
std::vector<std::string>target_candidates;
|
||||
resolve_path_from_var("XDG_DATA_HOME", target_candidates);
|
||||
resolve_path_from_var("XDG_DATA_DIRS", target_candidates);
|
||||
|
||||
AppConfig* app_config = wxGetApp().app_config;
|
||||
// suffix string to create different desktop file for alpha, beta.
|
||||
|
||||
std::string version_suffix;
|
||||
std::string name_suffix;
|
||||
std::string version(SLIC3R_VERSION);
|
||||
if (version.find("alpha") != std::string::npos)
|
||||
{
|
||||
version_suffix = "-alpha";
|
||||
name_suffix = " - alpha";
|
||||
}
|
||||
else if (version.find("beta") != std::string::npos)
|
||||
{
|
||||
version_suffix = "-beta";
|
||||
name_suffix = " - beta";
|
||||
}
|
||||
|
||||
// theme path to icon destination
|
||||
std::string icon_theme_path;
|
||||
std::string icon_theme_dirs;
|
||||
|
||||
if (platform_flavor() == PlatformFlavor::LinuxOnChromium) {
|
||||
icon_theme_path = "hicolor/96x96/apps/";
|
||||
icon_theme_dirs = "/hicolor/96x96/apps";
|
||||
}
|
||||
|
||||
std::string target_dir_desktop;
|
||||
|
||||
// desktop file
|
||||
// iterate thru target_candidates to find applications folder
|
||||
|
||||
std::string desktop_file_downloader = GUI::format(
|
||||
"[Desktop Entry]\n"
|
||||
"Name=PrusaSlicer URL Protocol%1%\n"
|
||||
"Exec=\"%2%\" --single-instance %%u\n"
|
||||
"Terminal=false\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=x-scheme-handler/prusaslicer;\n"
|
||||
"StartupNotify=false\n"
|
||||
"NoDisplay=true\n"
|
||||
, name_suffix, excutable_path);
|
||||
|
||||
// desktop file for downloader as part of main app
|
||||
std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (create_desktop_file(desktop_path, desktop_file_downloader)) {
|
||||
// save path to desktop file
|
||||
app_config->set("desktop_integration_URL_path", desktop_path);
|
||||
// finish registration on mime type
|
||||
std::string command = GUI::format("xdg-mime default PrusaSlicerURLProtocol%1%.desktop x-scheme-handler/prusaslicer", version_suffix);
|
||||
BOOST_LOG_TRIVIAL(debug) << "system command: " << command;
|
||||
int r = system(command.c_str());
|
||||
BOOST_LOG_TRIVIAL(debug) << "system result: " << r;
|
||||
|
||||
}
|
||||
|
||||
bool candidate_found = false;
|
||||
for (size_t i = 0; i < target_candidates.size(); ++i) {
|
||||
if (contains_path_dir(target_candidates[i], "applications")) {
|
||||
target_dir_desktop = target_candidates[i];
|
||||
// Write slicer desktop file
|
||||
std::string path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (create_desktop_file(path, desktop_file_downloader)) {
|
||||
app_config->set("desktop_integration_URL_path", path);
|
||||
candidate_found = true;
|
||||
BOOST_LOG_TRIVIAL(debug) << "PrusaSlicerURLProtocol.desktop file installation success.";
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// write failed - try another path
|
||||
BOOST_LOG_TRIVIAL(debug) << "Attempt to PrusaSlicerURLProtocol.desktop file installation failed. failed path: " << target_candidates[i];
|
||||
target_dir_desktop.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
// if all failed - try creating default home folder
|
||||
if (!candidate_found) {
|
||||
// create $HOME/.local/share
|
||||
create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications");
|
||||
// create desktop file
|
||||
target_dir_desktop = GUI::format("%1%/.local/share", wxFileName::GetHomeDir());
|
||||
std::string path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (contains_path_dir(target_dir_desktop, "applications")) {
|
||||
if (!create_desktop_file(path, desktop_file_downloader)) {
|
||||
// Desktop file not written - end desktop integration
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed - could not create desktop file.";
|
||||
return;
|
||||
}
|
||||
app_config->set("desktop_integration_URL_path", path);
|
||||
}
|
||||
else {
|
||||
// Desktop file not written - end desktop integration
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed because the application directory was not found.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
assert(!target_dir_desktop.empty());
|
||||
if (target_dir_desktop.empty()) {
|
||||
// Desktop file not written - end desktop integration
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed because the application directory was not found.";
|
||||
show_error(nullptr, _L("Performing downloader desktop integration failed because the application directory was not found."));
|
||||
return;
|
||||
}
|
||||
|
||||
// finish registration on mime type
|
||||
std::string command = GUI::format("xdg-mime default PrusaSlicerURLProtocol%1%.desktop x-scheme-handler/prusaslicer", version_suffix);
|
||||
BOOST_LOG_TRIVIAL(debug) << "system command: " << command;
|
||||
int r = system(command.c_str());
|
||||
BOOST_LOG_TRIVIAL(debug) << "system result: " << r;
|
||||
|
||||
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess);
|
||||
}
|
||||
void DesktopIntegrationDialog::undo_downloader_registration()
|
||||
{
|
||||
const AppConfig *app_config = wxGetApp().app_config;
|
||||
|
@ -532,7 +647,7 @@ DesktopIntegrationDialog::DesktopIntegrationDialog(wxWindow *parent)
|
|||
wxButton *btn_perform = new wxButton(this, wxID_ANY, _L("Perform"));
|
||||
btn_szr->Add(btn_perform, 0, wxALL, 10);
|
||||
|
||||
btn_perform->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::perform_desktop_integration(false); EndModal(wxID_ANY); });
|
||||
btn_perform->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::perform_desktop_integration(); EndModal(wxID_ANY); });
|
||||
|
||||
if (can_undo){
|
||||
wxButton *btn_undo = new wxButton(this, wxID_ANY, _L("Undo"));
|
||||
|
|
|
@ -29,10 +29,11 @@ public:
|
|||
// if perform_downloader:
|
||||
// Creates Destktop files for PrusaSlicer downloader feature
|
||||
// Regiters PrusaSlicer to start on prusaslicer:// URL
|
||||
static void perform_desktop_integration(bool perform_downloader);
|
||||
static void perform_desktop_integration();
|
||||
// Deletes Desktop files and icons for both PrusaSlicer and GcodeViewer at paths stored in App Config.
|
||||
static void undo_desktop_intgration();
|
||||
|
||||
static void perform_downloader_desktop_integration();
|
||||
static void undo_downloader_registration();
|
||||
private:
|
||||
|
||||
|
|
|
@ -137,13 +137,30 @@ void FileGet::priv::get_perform()
|
|||
std::string extension = boost::filesystem::extension(dest_path);
|
||||
std::string just_filename = m_filename.substr(0, m_filename.size() - extension.size());
|
||||
std::string final_filename = just_filename;
|
||||
|
||||
size_t version = 0;
|
||||
while (boost::filesystem::exists(m_dest_folder / (final_filename + extension)) || boost::filesystem::exists(m_dest_folder / (final_filename + extension + "." + std::to_string(get_current_pid()) + ".download")))
|
||||
// Find unsed filename
|
||||
try {
|
||||
size_t version = 0;
|
||||
while (boost::filesystem::exists(m_dest_folder / (final_filename + extension)) || boost::filesystem::exists(m_dest_folder / (final_filename + extension + "." + std::to_string(get_current_pid()) + ".download")))
|
||||
{
|
||||
++version;
|
||||
if (version > 999) {
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_ERROR);
|
||||
evt->SetString(GUI::format_wxstr(L"Failed to find suitable filename. Last name: %1%." , (m_dest_folder / (final_filename + extension)).string()));
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
return;
|
||||
}
|
||||
final_filename = GUI::format("%1%(%2%)", just_filename, std::to_string(version));
|
||||
}
|
||||
} catch (const boost::filesystem::filesystem_error& e)
|
||||
{
|
||||
++version;
|
||||
final_filename = just_filename + "(" + std::to_string(version) + ")";
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_ERROR);
|
||||
evt->SetString(e.what());
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
return;
|
||||
}
|
||||
|
||||
m_filename = final_filename + extension;
|
||||
|
||||
m_tmp_path = m_dest_folder / (m_filename + "." + std::to_string(get_current_pid()) + ".download");
|
||||
|
|
|
@ -172,16 +172,20 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar
|
|||
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX)
|
||||
, m_selected_paths (selected_paths)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
wxGetApp().UpdateDarkUI(this);
|
||||
#else
|
||||
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
#endif
|
||||
|
||||
int em = em_unit();
|
||||
|
||||
wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
|
||||
m_avc = new ArchiveViewCtrl(this, wxSize(60 * em, 30 * em));
|
||||
m_avc->AppendToggleColumn(L"\u2714", 0, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);
|
||||
m_avc = new ArchiveViewCtrl(this, wxSize(45 * em, 30 * em));
|
||||
wxDataViewColumn* toggle_column = m_avc->AppendToggleColumn(L"\u2714", 0, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em);
|
||||
m_avc->AppendTextColumn("filename", 1);
|
||||
|
||||
|
||||
std::vector<std::shared_ptr<ArchiveViewNode>> stack;
|
||||
|
||||
std::function<void(std::vector<std::shared_ptr<ArchiveViewNode> >&, size_t)> reduce_stack = [] (std::vector<std::shared_ptr<ArchiveViewNode>>& stack, size_t size) {
|
||||
|
@ -233,40 +237,51 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar
|
|||
}
|
||||
// sorting files will help adjust_stack function to not create multiple same folders
|
||||
std::sort(filtered_entries.begin(), filtered_entries.end(), [](const boost::filesystem::path& p1, const boost::filesystem::path& p2){ return p1.string() > p2.string(); });
|
||||
size_t entry_count = 0;
|
||||
size_t depth = 1;
|
||||
for (const boost::filesystem::path& path : filtered_entries)
|
||||
{
|
||||
std::shared_ptr<ArchiveViewNode> parent(nullptr);
|
||||
|
||||
adjust_stack(path, stack);
|
||||
depth = std::max(depth, adjust_stack(path, stack));
|
||||
if (!stack.empty())
|
||||
parent = stack.back();
|
||||
if (std::regex_match(path.extension().string(), pattern_drop)) { // this leaves out non-compatible files
|
||||
m_avc->get_model()->AddFile(parent, GUI::format_wxstr(path.filename().string()), false)->set_fullpath(/*std::move(path)*/path); // filename string to wstring?
|
||||
entry_count++;
|
||||
}
|
||||
}
|
||||
if (entry_count == 1)
|
||||
on_all_button();
|
||||
|
||||
toggle_column->SetWidth((4 + depth) * em);
|
||||
|
||||
wxBoxSizer* btn_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
wxButton* btn_all = new wxButton(this, wxID_ANY, "All");
|
||||
wxButton* btn_all = new wxButton(this, wxID_ANY, _L("All"));
|
||||
btn_all->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_all_button(); });
|
||||
btn_sizer->Add(btn_all, 0, wxLeft);
|
||||
btn_sizer->Add(btn_all, 0);
|
||||
|
||||
wxButton* btn_none = new wxButton(this, wxID_ANY, "None");
|
||||
wxButton* btn_none = new wxButton(this, wxID_ANY, _L("None"));
|
||||
btn_none->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_none_button(); });
|
||||
btn_sizer->Add(btn_none, 0, wxLeft);
|
||||
btn_sizer->Add(btn_none, 0, wxLEFT, em);
|
||||
|
||||
btn_sizer->AddStretchSpacer();
|
||||
wxButton* btn_run = new wxButton(this, wxID_OK, "Open");
|
||||
wxButton* btn_run = new wxButton(this, wxID_OK, _L("Open"));
|
||||
btn_run->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_open_button(); });
|
||||
btn_sizer->Add(btn_run, 0, wxRIGHT);
|
||||
btn_sizer->Add(btn_run, 0, wxRIGHT, em);
|
||||
|
||||
wxButton* cancel_btn = new wxButton(this, wxID_CANCEL, "Cancel");
|
||||
wxButton* cancel_btn = new wxButton(this, wxID_CANCEL, _L("Cancel"));
|
||||
cancel_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { this->EndModal(wxID_CANCEL); });
|
||||
btn_sizer->Add(cancel_btn, 0, wxRIGHT);
|
||||
btn_sizer->Add(cancel_btn, 0, wxRIGHT, em);
|
||||
|
||||
topSizer->Add(m_avc, 1, wxEXPAND | wxALL, 10);
|
||||
topSizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 10);
|
||||
this->SetMinSize(wxSize(80 * em, 30 * em));
|
||||
this->SetSizer(topSizer);
|
||||
SetMinSize(wxSize(40 * em, 30 * em));
|
||||
|
||||
for (const wxString& id : {_L("All"), _L("None"), _L("Open"), _L("Cancel") })
|
||||
wxGetApp().UpdateDarkUI(static_cast<wxButton*>(FindWindowByLabel(id, this)));
|
||||
}
|
||||
|
||||
void FileArchiveDialog::on_dpi_changed(const wxRect& suggested_rect)
|
||||
|
@ -277,9 +292,8 @@ void FileArchiveDialog::on_dpi_changed(const wxRect& suggested_rect)
|
|||
//for (auto btn : { m_save_btn, m_transfer_btn, m_discard_btn })
|
||||
// if (btn) btn->msw_rescale();
|
||||
|
||||
const wxSize& size = wxSize(70 * em, 30 * em);
|
||||
SetMinSize(size);
|
||||
|
||||
const wxSize& size = wxSize(45 * em, 40 * em);
|
||||
SetSize(size);
|
||||
//m_tree->Rescale(em);
|
||||
|
||||
Fit();
|
||||
|
|
|
@ -3972,8 +3972,9 @@ void GLCanvas3D::update_sequential_clearance()
|
|||
|
||||
Pointf3s& cache_hull_2d = m_sequential_print_clearance.m_hull_2d_cache.emplace_back(Pointf3s());
|
||||
cache_hull_2d.reserve(hull_2d.points.size());
|
||||
const Transform3d inv_trafo = trafo.get_matrix().inverse();
|
||||
for (const Point& p : hull_2d.points) {
|
||||
cache_hull_2d.emplace_back(unscale<double>(p.x()), unscale<double>(p.y()), 0.0);
|
||||
cache_hull_2d.emplace_back(inv_trafo * Vec3d(unscale<double>(p.x()), unscale<double>(p.y()), 0.0));
|
||||
}
|
||||
}
|
||||
m_sequential_print_clearance_first_displacement = false;
|
||||
|
@ -7055,7 +7056,7 @@ void GLCanvas3D::GizmoHighlighter::init(GLGizmosManager* manager, GLGizmosManage
|
|||
{
|
||||
if (m_timer.IsRunning())
|
||||
invalidate();
|
||||
if (!gizmo || !canvas)
|
||||
if (gizmo == GLGizmosManager::EType::Undefined || !canvas)
|
||||
return;
|
||||
|
||||
m_timer.Start(300, false);
|
||||
|
|
|
@ -79,7 +79,6 @@
|
|||
#include "DesktopIntegrationDialog.hpp"
|
||||
#include "SendSystemInfoDialog.hpp"
|
||||
#include "Downloader.hpp"
|
||||
#include "ConfigWizard_private.hpp"
|
||||
|
||||
#include "BitmapCache.hpp"
|
||||
#include "Notebook.hpp"
|
||||
|
@ -2890,6 +2889,7 @@ void GUI_App::MacOpenURL(const wxString& url)
|
|||
{
|
||||
if (app_config && !app_config->get_bool("downloader_url_registered"))
|
||||
{
|
||||
notification_manager()->push_notification(NotificationType::URLNotRegistered);
|
||||
BOOST_LOG_TRIVIAL(error) << "Recieved command to open URL, but it is not allowed in app configuration. URL: " << url;
|
||||
return;
|
||||
}
|
||||
|
@ -3081,11 +3081,11 @@ void GUI_App::show_downloader_registration_dialog()
|
|||
), SLIC3R_APP_NAME, SLIC3R_VERSION)
|
||||
, true, wxYES_NO);
|
||||
if (msg.ShowModal() == wxID_YES) {
|
||||
auto downloader = new DownloaderUtils::Worker(nullptr);
|
||||
downloader->perform_register(app_config->get("url_downloader_dest"));
|
||||
auto downloader_worker = new DownloaderUtils::Worker(nullptr);
|
||||
downloader_worker->perform_register(app_config->get("url_downloader_dest"));
|
||||
#ifdef __linux__
|
||||
if (downloader->get_perform_registration_linux())
|
||||
DesktopIntegrationDialog::perform_desktop_integration(true);
|
||||
if (downloader_worker->get_perform_registration_linux())
|
||||
DesktopIntegrationDialog::perform_downloader_desktop_integration();
|
||||
#endif // __linux__
|
||||
} else {
|
||||
app_config->set("downloader_url_registered", "0");
|
||||
|
|
|
@ -306,8 +306,8 @@ public:
|
|||
Plater* plater();
|
||||
const Plater* plater() const;
|
||||
Model& model();
|
||||
NotificationManager * notification_manager();
|
||||
GalleryDialog * gallery_dialog();
|
||||
NotificationManager* notification_manager();
|
||||
GalleryDialog * gallery_dialog();
|
||||
Downloader* downloader();
|
||||
|
||||
// Parameters extracted from the command line to be passed to GUI after initialization.
|
||||
|
|
|
@ -244,7 +244,7 @@ std::string GLGizmoCut3D::get_tooltip() const
|
|||
if (m_hover_id == Z || (m_dragging && m_hover_id == CutPlane)) {
|
||||
double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0;
|
||||
std::string unit_str = " " + (m_imperial_units ? _u8L("inch") : _u8L("mm"));
|
||||
const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center);
|
||||
const BoundingBoxf3& tbb = m_transformed_bounding_box;
|
||||
if (tbb.max.z() >= 0.0) {
|
||||
double top = (tbb.min.z() <= 0.0 ? tbb.max.z() : tbb.size().z()) * koef;
|
||||
tooltip += format(top, 2) + " " + unit_str + " (" + _u8L("Top part") + ")";
|
||||
|
@ -401,7 +401,7 @@ bool GLGizmoCut3D::is_looking_forward() const
|
|||
|
||||
void GLGizmoCut3D::update_clipper()
|
||||
{
|
||||
BoundingBoxf3 box = bounding_box();
|
||||
BoundingBoxf3 box = m_bounding_box;
|
||||
|
||||
// update cut_normal
|
||||
Vec3d beg, end = beg = m_plane_center;
|
||||
|
@ -549,7 +549,7 @@ bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& v
|
|||
return !is_approx(old_val, value);
|
||||
};
|
||||
|
||||
const BoundingBoxf3 bbox = bounding_box();
|
||||
const BoundingBoxf3 bbox = m_bounding_box;
|
||||
const float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0) * (m_imperial_units ? f_mm_to_in : 1.f);
|
||||
|
||||
ImGuiWrapper::text(label);
|
||||
|
@ -795,7 +795,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers()
|
|||
|
||||
const Transform3d view_matrix = wxGetApp().plater()->get_camera().get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m;
|
||||
|
||||
const double mean_size = get_grabber_mean_size(bounding_box());
|
||||
const double mean_size = get_grabber_mean_size(m_bounding_box);
|
||||
double size;
|
||||
|
||||
const bool dragging_by_cut_plane = m_dragging && m_hover_id == CutPlane;
|
||||
|
@ -1033,7 +1033,7 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform()
|
|||
else if (!cut_line_processing()){
|
||||
const Transform3d trafo = translation_transform(m_plane_center) * m_rotation_m;
|
||||
|
||||
const BoundingBoxf3 box = bounding_box();
|
||||
const BoundingBoxf3 box = m_bounding_box;
|
||||
|
||||
const double size = get_half_size(get_grabber_mean_size(box));
|
||||
Vec3d scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size);
|
||||
|
@ -1068,25 +1068,16 @@ bool GLGizmoCut3D::on_is_activable() const
|
|||
if (object_idx < 0 || selection.is_wipe_tower())
|
||||
return false;
|
||||
|
||||
bool is_dowel_object = false;
|
||||
if (const ModelObject* mo = wxGetApp().plater()->model().objects[object_idx]; mo->is_cut()) {
|
||||
int solid_connector_cnt = 0;
|
||||
int connectors_cnt = 0;
|
||||
for (const ModelVolume* volume : mo->volumes) {
|
||||
if (volume->is_cut_connector()) {
|
||||
connectors_cnt++;
|
||||
if (volume->is_model_part())
|
||||
solid_connector_cnt++;
|
||||
}
|
||||
if (connectors_cnt > 1)
|
||||
break;
|
||||
}
|
||||
is_dowel_object = connectors_cnt == 1 && solid_connector_cnt == 1;
|
||||
if (const ModelObject* mo = wxGetApp().plater()->model().objects[object_idx];
|
||||
mo->is_cut() && mo->volumes.size() == 1) {
|
||||
const ModelVolume* volume = mo->volumes[0];
|
||||
if (volume->is_cut_connector() && volume->cut_info.connector_type == CutConnectorType::Dowel)
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is assumed in GLCanvas3D::do_rotate, do not change this
|
||||
// without updating that function too.
|
||||
return selection.is_single_full_instance() && !is_dowel_object && !m_parent.is_layers_editing_enabled();
|
||||
return selection.is_single_full_instance() && !m_parent.is_layers_editing_enabled();
|
||||
}
|
||||
|
||||
bool GLGizmoCut3D::on_is_selectable() const
|
||||
|
@ -1192,7 +1183,11 @@ void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data)
|
|||
|
||||
Vec3d rotation = Vec3d::Zero();
|
||||
rotation[m_hover_id] = theta;
|
||||
m_rotation_m = m_start_dragging_m * rotation_transform(rotation);
|
||||
|
||||
const Transform3d rotation_tmp = m_start_dragging_m * rotation_transform(rotation);
|
||||
if (m_rotation_m.rotation() != rotation_tmp.rotation())
|
||||
m_transformed_bounding_box = transformed_bounding_box(m_plane_center);
|
||||
m_rotation_m = rotation_tmp;
|
||||
|
||||
m_angle = theta;
|
||||
while (m_angle > two_pi)
|
||||
|
@ -1254,9 +1249,13 @@ void GLGizmoCut3D::on_stop_dragging()
|
|||
|
||||
void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool force/* = false*/)
|
||||
{
|
||||
if (m_plane_center == center_pos)
|
||||
return;
|
||||
|
||||
bool can_set_center_pos = force;
|
||||
BoundingBoxf3 tbb;
|
||||
if (!can_set_center_pos) {
|
||||
const BoundingBoxf3 tbb = transformed_bounding_box(center_pos);
|
||||
tbb = transformed_bounding_box(center_pos);
|
||||
if (tbb.max.z() > -1. && tbb.min.z() < 1.)
|
||||
can_set_center_pos = true;
|
||||
else {
|
||||
|
@ -1269,6 +1268,7 @@ void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool force/* = false*
|
|||
}
|
||||
|
||||
if (can_set_center_pos) {
|
||||
m_transformed_bounding_box = tbb;
|
||||
m_plane_center = center_pos;
|
||||
m_center_offset = m_plane_center - m_bb_center;
|
||||
}
|
||||
|
@ -1288,7 +1288,7 @@ BoundingBoxf3 GLGizmoCut3D::bounding_box() const
|
|||
return ret;
|
||||
}
|
||||
|
||||
BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center, bool revert_move /*= false*/) const
|
||||
BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center) const
|
||||
{
|
||||
// #ysFIXME !!!
|
||||
BoundingBoxf3 ret;
|
||||
|
@ -1308,10 +1308,7 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center,
|
|||
Vec3d cut_center_offset = plane_center - instance_offset;
|
||||
cut_center_offset[Z] -= sel_info->get_sla_shift();
|
||||
|
||||
const auto move = translation_transform(-cut_center_offset);
|
||||
const auto move2 = translation_transform(plane_center);
|
||||
|
||||
const auto cut_matrix = (revert_move ? move2 : Transform3d::Identity()) * m_rotation_m.inverse() * move;
|
||||
const auto cut_matrix = Transform3d::Identity() * m_rotation_m.inverse() * translation_transform(-cut_center_offset);
|
||||
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
const Selection::IndicesList& idxs = selection.get_volume_idxs();
|
||||
|
@ -1344,6 +1341,8 @@ bool GLGizmoCut3D::update_bb()
|
|||
const BoundingBoxf3 box = bounding_box();
|
||||
if (m_max_pos != box.max || m_min_pos != box.min) {
|
||||
|
||||
m_bounding_box = box;
|
||||
|
||||
invalidate_cut_plane();
|
||||
|
||||
m_max_pos = box.max;
|
||||
|
@ -1397,7 +1396,7 @@ void GLGizmoCut3D::init_picking_models()
|
|||
}
|
||||
|
||||
if (!m_plane.model.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) {
|
||||
const double cp_width = 0.02 * get_grabber_mean_size(bounding_box());
|
||||
const double cp_width = 0.02 * get_grabber_mean_size(m_bounding_box);
|
||||
indexed_triangle_set its = its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4);
|
||||
m_plane.model.init_from(its);
|
||||
m_plane.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
|
||||
|
@ -1640,9 +1639,8 @@ void GLGizmoCut3D::render_build_size()
|
|||
{
|
||||
double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0;
|
||||
wxString unit_str = " " + (m_imperial_units ? _L("in") : _L("mm"));
|
||||
const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center);
|
||||
|
||||
Vec3d tbb_sz = tbb.size();
|
||||
Vec3d tbb_sz = m_transformed_bounding_box.size();
|
||||
wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str +
|
||||
", Y: " + double_to_string(tbb_sz.y() * koef, 2) + unit_str +
|
||||
", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str;
|
||||
|
@ -1655,7 +1653,7 @@ void GLGizmoCut3D::render_build_size()
|
|||
|
||||
void GLGizmoCut3D::reset_cut_plane()
|
||||
{
|
||||
set_center(bounding_box().center());
|
||||
set_center(m_bb_center);
|
||||
m_rotation_m = Transform3d::Identity();
|
||||
m_angle_arc.reset();
|
||||
update_clipper();
|
||||
|
@ -1755,7 +1753,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
|
|||
|
||||
const bool has_connectors = !connectors.empty();
|
||||
|
||||
const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && bounding_box().center() == m_plane_center;
|
||||
const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && m_bb_center == m_plane_center;
|
||||
m_imgui->disabled_begin(is_cut_plane_init);
|
||||
if (render_reset_button("cut_plane", _u8L("Reset cutting plane")))
|
||||
reset_cut_plane();
|
||||
|
@ -1847,10 +1845,9 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
|
|||
add_vertical_scaled_interval(0.75f);
|
||||
|
||||
m_imgui->disabled_begin(has_connectors);
|
||||
add_horizontal_shift(m_imgui->scaled(/*1*/.2f));
|
||||
ImGuiWrapper::text(_L("Cut to") + ":");
|
||||
|
||||
ImGui::SameLine();
|
||||
add_horizontal_scaled_interval(1.2f);
|
||||
if (m_imgui->radio_button(_L("Objects"), !m_keep_as_parts))
|
||||
m_keep_as_parts = false;
|
||||
ImGui::SameLine();
|
||||
|
@ -1999,38 +1996,6 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit)
|
|||
render_debug_input_window(x);
|
||||
}
|
||||
|
||||
// get volume transformation regarding to the "border". Border is related from the size of connectors
|
||||
Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) const
|
||||
{
|
||||
bool is_prizm_dowel = m_connector_type == CutConnectorType::Dowel && m_connector_style == size_t(CutConnectorStyle::Prizm);
|
||||
#if ENABLE_WORLD_COORDINATE
|
||||
const Transform3d connector_trafo = is_prizm_dowel ?
|
||||
Geometry::translation_transform(-m_connector_depth_ratio * Vec3d::UnitZ()) * m_rotation_m * Geometry::scale_transform({ 0.5 * m_connector_size, 0.5 * m_connector_size, 2 * m_connector_depth_ratio }) :
|
||||
m_rotation_m * Geometry::scale_transform({ 0.5 * m_connector_size, 0.5 * m_connector_size, m_connector_depth_ratio });
|
||||
|
||||
#else
|
||||
const Transform3d connector_trafo = assemble_transform(
|
||||
is_prizm_dowel ? Vec3d(0.0, 0.0, -m_connector_depth_ratio) : Vec3d::Zero(),
|
||||
Transformation(m_rotation_m).get_rotation(),
|
||||
Vec3d(0.5*m_connector_size, 0.5*m_connector_size, is_prizm_dowel ? 2 * m_connector_depth_ratio : m_connector_depth_ratio),
|
||||
Vec3d::Ones());
|
||||
#endif // ENABLE_WORLD_COORDINATE
|
||||
const Vec3d connector_bb = m_connector_mesh.transformed_bounding_box(connector_trafo).size();
|
||||
|
||||
const Vec3d bb = volume->mesh().bounding_box().size();
|
||||
|
||||
// calculate an unused border - part of the the volume, where we can't put connectors
|
||||
const Vec3d border_scale(connector_bb.x() / bb.x(), connector_bb.y() / bb.y(), connector_bb.z() / bb.z());
|
||||
|
||||
const Transform3d vol_matrix = volume->get_matrix();
|
||||
const Vec3d vol_trans = vol_matrix.translation();
|
||||
// offset of the volume will be changed after scaling, so calculate the needed offset and set it to a volume_trafo
|
||||
const Vec3d offset(vol_trans.x() * border_scale.x(), vol_trans.y() * border_scale.y(), vol_trans.z() * border_scale.z());
|
||||
|
||||
// scale and translate volume to suppress to put connectors too close to the border
|
||||
return translation_transform(offset) * scale_transform(Vec3d::Ones() - border_scale) * vol_matrix;
|
||||
}
|
||||
|
||||
bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos)
|
||||
{
|
||||
// check if connector pos is out of clipping plane
|
||||
|
@ -2081,7 +2046,7 @@ bool GLGizmoCut3D::is_conflict_for_connector(size_t idx, const CutConnectors& co
|
|||
const BoundingBoxf3 cur_tbb = m_shapes[cur_connector.attribs].model.get_bounding_box().transformed(matrix);
|
||||
|
||||
// check if connector's bounding box is inside the object's bounding box
|
||||
if (!bounding_box().contains(cur_tbb)) {
|
||||
if (!m_bounding_box.contains(cur_tbb)) {
|
||||
m_info_stats.outside_bb++;
|
||||
return true;
|
||||
}
|
||||
|
@ -2572,8 +2537,7 @@ CommonGizmosDataID GLGizmoCut3D::on_get_requirements() const {
|
|||
return CommonGizmosDataID(
|
||||
int(CommonGizmosDataID::SelectionInfo)
|
||||
| int(CommonGizmosDataID::InstancesHider)
|
||||
| int(CommonGizmosDataID::ObjectClipper)
|
||||
| int(CommonGizmosDataID::Raycaster));
|
||||
| int(CommonGizmosDataID::ObjectClipper));
|
||||
}
|
||||
|
||||
void GLGizmoCut3D::data_changed()
|
||||
|
|
|
@ -43,6 +43,9 @@ class GLGizmoCut3D : public GLGizmoBase
|
|||
Vec3d m_bb_center{ Vec3d::Zero() };
|
||||
Vec3d m_center_offset{ Vec3d::Zero() };
|
||||
|
||||
BoundingBoxf3 m_bounding_box;
|
||||
BoundingBoxf3 m_transformed_bounding_box;
|
||||
|
||||
// values from RotationGizmo
|
||||
double m_radius{ 0.0 };
|
||||
double m_grabber_radius{ 0.0 };
|
||||
|
@ -193,7 +196,7 @@ public:
|
|||
void invalidate_cut_plane();
|
||||
|
||||
BoundingBoxf3 bounding_box() const;
|
||||
BoundingBoxf3 transformed_bounding_box(const Vec3d& plane_center, bool revert_move = false) const;
|
||||
BoundingBoxf3 transformed_bounding_box(const Vec3d& plane_center) const;
|
||||
|
||||
protected:
|
||||
bool on_init() override;
|
||||
|
@ -263,7 +266,6 @@ private:
|
|||
void render_connect_mode_radio_button(CutConnectorMode mode);
|
||||
bool render_reset_button(const std::string& label_id, const std::string& tooltip) const;
|
||||
bool render_connect_type_radio_button(CutConnectorType type);
|
||||
Transform3d get_volume_transformation(const ModelVolume* volume) const;
|
||||
bool is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos);
|
||||
bool is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos);
|
||||
void render_connectors();
|
||||
|
|
|
@ -519,6 +519,13 @@ bool GLGizmoFdmSupports::has_backend_supports()
|
|||
|
||||
void GLGizmoFdmSupports::auto_generate()
|
||||
{
|
||||
std::string err = wxGetApp().plater()->fff_print().validate();
|
||||
if (!err.empty()) {
|
||||
MessageDialog dlg(GUI::wxGetApp().plater(), _L("Automatic painting requires valid print setup. \n") + from_u8(err), _L("Warning"), wxOK);
|
||||
dlg.ShowModal();
|
||||
return;
|
||||
}
|
||||
|
||||
ModelObject *mo = m_c->selection_info()->model_object();
|
||||
bool not_painted = std::all_of(mo->volumes.begin(), mo->volumes.end(), [](const ModelVolume* vol){
|
||||
return vol->type() != ModelVolumeType::MODEL_PART || vol->supported_facets.empty();
|
||||
|
|
|
@ -239,10 +239,10 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection)
|
|||
m_bounding_box = box;
|
||||
m_center = box_trafo.translation();
|
||||
m_orient_matrix = Geometry::translation_transform(m_center);
|
||||
if (!wxGetApp().obj_manipul()->is_world_coordinates()) {
|
||||
if (!wxGetApp().obj_manipul()->is_world_coordinates() || m_force_local_coordinate) {
|
||||
const GLVolume& v = *selection.get_first_volume();
|
||||
m_orient_matrix = m_orient_matrix * v.get_instance_transformation().get_rotation_matrix();
|
||||
if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates())
|
||||
if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates() || m_force_local_coordinate)
|
||||
m_orient_matrix = m_orient_matrix * v.get_volume_transformation().get_rotation_matrix();
|
||||
}
|
||||
|
||||
|
|
|
@ -740,6 +740,7 @@ void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_t
|
|||
const float icons_size_x = 2.0f * m_layout.scaled_icons_size() * inv_cnv_w;
|
||||
const float icons_size_y = 2.0f * m_layout.scaled_icons_size() * inv_cnv_h;
|
||||
const float stride_y = 2.0f * m_layout.scaled_stride_y() * inv_cnv_h;
|
||||
top_y -= stride_y;
|
||||
|
||||
for (size_t idx : selectable_idxs) {
|
||||
if (idx == highlighted_type) {
|
||||
|
|
|
@ -118,8 +118,6 @@ void RotoptimizeJob::finalize(bool canceled, std::exception_ptr &eptr)
|
|||
// Correct the z offset of the object which was corrupted be
|
||||
// the rotation
|
||||
o->ensure_on_bed();
|
||||
|
||||
// m_plater->find_new_position(o->instances);
|
||||
}
|
||||
|
||||
if (!canceled)
|
||||
|
|
|
@ -120,7 +120,9 @@ enum class NotificationType
|
|||
// Short meesage to fill space between start and finish of export
|
||||
ExportOngoing,
|
||||
// Progressbar of download from prusaslicer:// url
|
||||
URLDownload
|
||||
URLDownload,
|
||||
// MacOS specific - PS comes forward even when downloader is not allowed
|
||||
URLNotRegistered,
|
||||
};
|
||||
|
||||
class NotificationManager
|
||||
|
@ -916,6 +918,16 @@ private:
|
|||
{NotificationType::UndoDesktopIntegrationFail, NotificationLevel::WarningNotificationLevel, 10,
|
||||
_u8L("Undo desktop integration failed.") },
|
||||
{NotificationType::ExportOngoing, NotificationLevel::RegularNotificationLevel, 0, _u8L("Exporting.") },
|
||||
{NotificationType::URLNotRegistered
|
||||
, NotificationLevel::RegularNotificationLevel
|
||||
, 10
|
||||
, _u8L("PrusaSlicer recieved a download request from Printables.com, but it's not allowed. You can allow it")
|
||||
, _u8L("here.")
|
||||
, [](wxEvtHandler* evnthndlr) {
|
||||
wxGetApp().open_preferences("downloader_url_registered", "Other");
|
||||
return true;
|
||||
} },
|
||||
|
||||
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) {
|
||||
// wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
|
||||
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") },
|
||||
|
|
|
@ -1038,25 +1038,50 @@ void ogStaticText::SetText(const wxString& value, bool wrap/* = true*/)
|
|||
|
||||
void ogStaticText::SetPathEnd(const std::string& link)
|
||||
{
|
||||
#ifndef __linux__
|
||||
|
||||
Bind(wxEVT_ENTER_WINDOW, [this, link](wxMouseEvent& event) {
|
||||
SetToolTip(OptionsGroup::get_url(get_app_config()->get("suppress_hyperlinks") != "1" ? link : std::string()));
|
||||
FocusText(true);
|
||||
event.Skip();
|
||||
});
|
||||
Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& event) { FocusText(false); event.Skip(); });
|
||||
|
||||
Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& event) {
|
||||
if (HasCapture())
|
||||
return;
|
||||
this->CaptureMouse();
|
||||
event.Skip();
|
||||
} );
|
||||
});
|
||||
Bind(wxEVT_LEFT_UP, [link, this](wxMouseEvent& event) {
|
||||
if (!HasCapture())
|
||||
return;
|
||||
ReleaseMouse();
|
||||
OptionsGroup::launch_browser(link);
|
||||
event.Skip();
|
||||
} );
|
||||
Bind(wxEVT_ENTER_WINDOW, [this, link](wxMouseEvent& event) {
|
||||
SetToolTip(OptionsGroup::get_url(!get_app_config()->get_bool("suppress_hyperlinks") ? link : std::string()));
|
||||
FocusText(true);
|
||||
event.Skip();
|
||||
});
|
||||
Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& event) { FocusText(false); event.Skip(); });
|
||||
|
||||
#else
|
||||
|
||||
// Workaround: On Linux wxStaticText doesn't receive wxEVT_ENTER(LEAVE)_WINDOW events,
|
||||
// so implement this behaviour trough wxEVT_MOTION events for this control and it's parent
|
||||
Bind(wxEVT_MOTION, [link, this](wxMouseEvent& event) {
|
||||
SetToolTip(OptionsGroup::get_url(!get_app_config()->get_bool("suppress_hyperlinks") ? link : std::string()));
|
||||
FocusText(true);
|
||||
event.Skip();
|
||||
});
|
||||
GetParent()->Bind(wxEVT_MOTION, [this](wxMouseEvent& event) {
|
||||
FocusText(false);
|
||||
event.Skip();
|
||||
});
|
||||
|
||||
// On Linux a mouse capturing causes a totally application freeze
|
||||
Bind(wxEVT_LEFT_UP, [link, this](wxMouseEvent& event) {
|
||||
OptionsGroup::launch_browser(link);
|
||||
event.Skip();
|
||||
});
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void ogStaticText::FocusText(bool focus)
|
||||
|
@ -1065,7 +1090,10 @@ void ogStaticText::FocusText(bool focus)
|
|||
return;
|
||||
|
||||
SetFont(focus ? Slic3r::GUI::wxGetApp().link_font() :
|
||||
Slic3r::GUI::wxGetApp().normal_font());
|
||||
Slic3r::GUI::wxGetApp().normal_font());
|
||||
#ifdef __linux__
|
||||
this->GetContainingSizer()->Layout();
|
||||
#endif
|
||||
Refresh();
|
||||
}
|
||||
|
||||
|
|
|
@ -462,7 +462,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
|
|||
choice->set_selection();
|
||||
}
|
||||
|
||||
update();
|
||||
update(true);
|
||||
}
|
||||
|
||||
void PhysicalPrinterDialog::update_printhost_buttons()
|
||||
|
@ -632,11 +632,12 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change)
|
|||
|
||||
Choice* choice = dynamic_cast<Choice*>(ht);
|
||||
choice->set_values(types);
|
||||
int index_in_choice = (printer_change ? 0 : last_in_conf);
|
||||
int dif = (int)ht->m_opt.enum_def->values().size() - (int)types.size();
|
||||
int index_in_choice = (printer_change ? std::clamp(last_in_conf - ((int)ht->m_opt.enum_def->values().size() - (int)types.size()), 0, (int)ht->m_opt.enum_def->values().size() - 1) : last_in_conf);
|
||||
choice->set_value(index_in_choice);
|
||||
if (link.supported && link.label == _(ht->m_opt.enum_def->label(index_in_choice)))
|
||||
m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(htPrusaLink));
|
||||
else if (link.supported && link.label == _(ht->m_opt.enum_def->label(index_in_choice)))
|
||||
else if (connect.supported && connect.label == _(ht->m_opt.enum_def->label(index_in_choice)))
|
||||
m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(htPrusaConnect));
|
||||
else {
|
||||
int host_type = std::clamp(index_in_choice + ((int)ht->m_opt.enum_def->values().size() - (int)types.size()), 0, (int)ht->m_opt.enum_def->values().size() - 1);
|
||||
|
|
|
@ -3116,38 +3116,6 @@ void Plater::priv::mirror(Axis axis)
|
|||
view3D->mirror_selection(axis);
|
||||
}
|
||||
|
||||
void Plater::find_new_position(const ModelInstancePtrs &instances)
|
||||
{
|
||||
arrangement::ArrangePolygons movable, fixed;
|
||||
arrangement::ArrangeParams arr_params = get_arrange_params(this);
|
||||
|
||||
for (const ModelObject *mo : p->model.objects)
|
||||
for (ModelInstance *inst : mo->instances) {
|
||||
auto it = std::find(instances.begin(), instances.end(), inst);
|
||||
auto arrpoly = get_arrange_poly(inst, this);
|
||||
|
||||
if (it == instances.end())
|
||||
fixed.emplace_back(std::move(arrpoly));
|
||||
else {
|
||||
arrpoly.setter = [it](const arrangement::ArrangePolygon &p) {
|
||||
if (p.is_arranged() && p.bed_idx == 0) {
|
||||
Vec2d t = p.translation.cast<double>();
|
||||
(*it)->apply_arrange_result(t, p.rotation);
|
||||
}
|
||||
};
|
||||
movable.emplace_back(std::move(arrpoly));
|
||||
}
|
||||
}
|
||||
|
||||
if (auto wt = get_wipe_tower_arrangepoly(*this))
|
||||
fixed.emplace_back(*wt);
|
||||
|
||||
arrangement::arrange(movable, fixed, this->build_volume().polygon(), arr_params);
|
||||
|
||||
for (auto & m : movable)
|
||||
m.apply();
|
||||
}
|
||||
|
||||
void Plater::priv::split_object()
|
||||
{
|
||||
int obj_idx = get_selected_object_idx();
|
||||
|
|
|
@ -330,7 +330,6 @@ public:
|
|||
GLCanvas3D* get_current_canvas3D();
|
||||
|
||||
void arrange();
|
||||
void find_new_position(const ModelInstancePtrs &instances);
|
||||
|
||||
void set_current_canvas_as_dirty();
|
||||
void unbind_canvas_event_handlers();
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include "ButtonsDescription.hpp"
|
||||
#include "OG_CustomCtrl.hpp"
|
||||
#include "GLCanvas3D.hpp"
|
||||
#include "ConfigWizard_private.hpp"
|
||||
#include "ConfigWizard.hpp"
|
||||
|
||||
#include <boost/dll/runtime_symbol_info.hpp>
|
||||
|
||||
|
@ -712,7 +712,7 @@ void PreferencesDialog::accept(wxEvent&)
|
|||
return;
|
||||
#ifdef __linux__
|
||||
if( downloader->get_perform_registration_linux())
|
||||
DesktopIntegrationDialog::perform_desktop_integration(true);
|
||||
DesktopIntegrationDialog::perform_downloader_desktop_integration();
|
||||
#endif // __linux__
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ class PreferencesDialog : public DPIDialog
|
|||
wxColourPickerCtrl* m_mode_advanced { nullptr };
|
||||
wxColourPickerCtrl* m_mode_expert { nullptr };
|
||||
|
||||
DownloaderUtils::Worker* downloader{ nullptr };
|
||||
DownloaderUtils::Worker* downloader { nullptr };
|
||||
|
||||
wxBookCtrlBase* tabs {nullptr};
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "Tab.hpp"
|
||||
|
||||
#define FTS_FUZZY_MATCH_IMPLEMENTATION
|
||||
#include "ExtraRenderers.hpp"
|
||||
#include "fts_fuzzy_match.h"
|
||||
|
||||
#include "imgui/imconfig.h"
|
||||
|
@ -500,6 +501,10 @@ SearchDialog::SearchDialog(OptionsSearcher* searcher)
|
|||
search_list_model = new SearchListModel(this);
|
||||
search_list->AssociateModel(search_list_model);
|
||||
|
||||
#ifdef __WXMSW__
|
||||
search_list->AppendColumn(new wxDataViewColumn("", new BitmapTextRenderer(true, wxDATAVIEW_CELL_INERT), SearchListModel::colIconMarkedText, wxCOL_WIDTH_AUTOSIZE, wxALIGN_LEFT));
|
||||
search_list->GetColumn(SearchListModel::colIconMarkedText)->SetWidth(48 * em_unit());
|
||||
#else
|
||||
search_list->AppendBitmapColumn("", SearchListModel::colIcon);
|
||||
|
||||
wxDataViewTextRenderer* const markupRenderer = new wxDataViewTextRenderer();
|
||||
|
@ -512,6 +517,7 @@ SearchDialog::SearchDialog(OptionsSearcher* searcher)
|
|||
|
||||
search_list->GetColumn(SearchListModel::colIcon )->SetWidth(3 * em_unit());
|
||||
search_list->GetColumn(SearchListModel::colMarkedText)->SetWidth(40 * em_unit());
|
||||
#endif
|
||||
|
||||
wxBoxSizer* check_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
|
@ -725,10 +731,12 @@ void SearchDialog::OnLeftDown(wxMouseEvent& event)
|
|||
void SearchDialog::msw_rescale()
|
||||
{
|
||||
const int& em = em_unit();
|
||||
|
||||
#ifdef __WXMSW__
|
||||
search_list->GetColumn(SearchListModel::colIconMarkedText)->SetWidth(48 * em);
|
||||
#else
|
||||
search_list->GetColumn(SearchListModel::colIcon )->SetWidth(3 * em);
|
||||
search_list->GetColumn(SearchListModel::colMarkedText)->SetWidth(45 * em);
|
||||
|
||||
#endif
|
||||
const wxSize& size = wxSize(40 * em, 30 * em);
|
||||
SetMinSize(size);
|
||||
|
||||
|
@ -787,8 +795,13 @@ void SearchListModel::sys_color_changed()
|
|||
|
||||
wxString SearchListModel::GetColumnType(unsigned int col) const
|
||||
{
|
||||
#ifdef __WXMSW__
|
||||
if (col == colIconMarkedText)
|
||||
return "DataViewBitmapText";
|
||||
#else
|
||||
if (col == colIcon)
|
||||
return "wxBitmap";
|
||||
#endif
|
||||
return "string";
|
||||
}
|
||||
|
||||
|
@ -797,12 +810,20 @@ void SearchListModel::GetValueByRow(wxVariant& variant,
|
|||
{
|
||||
switch (col)
|
||||
{
|
||||
#ifdef __WXMSW__
|
||||
case colIconMarkedText: {
|
||||
const ScalableBitmap& icon = m_icon[m_values[row].second];
|
||||
variant << DataViewBitmapText(m_values[row].first, icon.bmp().GetBitmapFor(icon.parent()));
|
||||
break;
|
||||
}
|
||||
#else
|
||||
case colIcon:
|
||||
variant << m_icon[m_values[row].second].bmp().GetBitmapFor(m_icon[m_values[row].second].parent());
|
||||
break;
|
||||
case colMarkedText:
|
||||
variant = m_values[row].first;
|
||||
break;
|
||||
#endif
|
||||
case colMax:
|
||||
wxFAIL_MSG("invalid column");
|
||||
default:
|
||||
|
|
|
@ -202,8 +202,12 @@ class SearchListModel : public wxDataViewVirtualListModel
|
|||
|
||||
public:
|
||||
enum {
|
||||
#ifdef __WXMSW__
|
||||
colIconMarkedText,
|
||||
#else
|
||||
colIcon,
|
||||
colMarkedText,
|
||||
#endif
|
||||
colMax
|
||||
};
|
||||
|
||||
|
|
|
@ -1571,7 +1571,10 @@ void TabPrint::build()
|
|||
optgroup->append_single_option_line("first_layer_speed_over_raft");
|
||||
|
||||
optgroup = page->new_optgroup(L("Acceleration control (advanced)"));
|
||||
optgroup->append_single_option_line("external_perimeter_acceleration");
|
||||
optgroup->append_single_option_line("perimeter_acceleration");
|
||||
optgroup->append_single_option_line("top_solid_infill_acceleration");
|
||||
optgroup->append_single_option_line("solid_infill_acceleration");
|
||||
optgroup->append_single_option_line("infill_acceleration");
|
||||
optgroup->append_single_option_line("bridge_acceleration");
|
||||
optgroup->append_single_option_line("first_layer_acceleration");
|
||||
|
@ -1638,9 +1641,6 @@ void TabPrint::build()
|
|||
optgroup->append_single_option_line("xy_size_compensation");
|
||||
optgroup->append_single_option_line("elefant_foot_compensation", "elephant-foot-compensation_114487");
|
||||
|
||||
optgroup = page->new_optgroup(L("Other"));
|
||||
optgroup->append_single_option_line("clip_multipart_objects");
|
||||
|
||||
optgroup = page->new_optgroup(L("Arachne perimeter generator"));
|
||||
optgroup->append_single_option_line("wall_transition_angle");
|
||||
optgroup->append_single_option_line("wall_transition_filter_deviation");
|
||||
|
@ -1729,9 +1729,7 @@ void TabPrint::update_description_lines()
|
|||
if (m_post_process_explanation) {
|
||||
m_post_process_explanation->SetText(
|
||||
_L("Post processing scripts shall modify G-code file in place."));
|
||||
#ifndef __linux__
|
||||
m_post_process_explanation->SetPathEnd("post-processing-scripts_283913");
|
||||
#endif // __linux__
|
||||
}
|
||||
// upadte G-code substitutions from the current configuration
|
||||
{
|
||||
|
|
|
@ -734,11 +734,14 @@ bool PrusaLink::get_storage(wxArrayString& output) const
|
|||
const auto path = section.second.get_optional<std::string>("path");
|
||||
const auto space = section.second.get_optional<std::string>("free_space");
|
||||
const auto read_only = section.second.get_optional<bool>("read_only");
|
||||
const auto ro = section.second.get_optional<bool>("ro"); // In PrusaLink 0.7.0RC2 "read_only" value is stored under "ro".
|
||||
const auto available = section.second.get_optional<bool>("available");
|
||||
if (path && (!available || *available)) {
|
||||
StorageInfo si;
|
||||
si.name = boost::nowide::widen(*path);
|
||||
si.read_only = read_only ? *read_only : false; // If read_only is missing, assume it is NOT read only.
|
||||
// If read_only is missing, assume it is NOT read only.
|
||||
// si.read_only = read_only ? *read_only : false; // version without "ro"
|
||||
si.read_only = (read_only ? *read_only : (ro ? *ro : false));
|
||||
si.free_space = space ? std::stoll(*space) : 1; // If free_space is missing, assume there is free space.
|
||||
storage.emplace_back(std::move(si));
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
set(SLIC3R_APP_NAME "PrusaSlicer")
|
||||
set(SLIC3R_APP_KEY "PrusaSlicer")
|
||||
set(SLIC3R_VERSION "2.6.0-alpha3")
|
||||
set(SLIC3R_VERSION "2.6.0-alpha4")
|
||||
set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN")
|
||||
set(SLIC3R_RC_VERSION "2,6,0,0")
|
||||
set(SLIC3R_RC_VERSION_DOTS "2.6.0.0")
|
||||
|
|
Loading…
Add table
Reference in a new issue