Merge remote-tracking branch 'upstream/main' into feedback

This commit is contained in:
Clifford Garwood
2026-05-01 01:30:40 -04:00
1033 changed files with 24477 additions and 1712 deletions
+27 -11
View File
@@ -54,6 +54,12 @@ WallToolPathsParams make_paths_params(const int layer_id, const PrintObjectConfi
input_params.wall_distribution_count = print_object_config.wall_distribution_count.value;
input_params.is_top_or_bottom_layer = false; // Set to default value
if (const auto& wall_maximum_resolution_opt = print_object_config.wall_maximum_resolution)
input_params.wall_maximum_resolution = scaled<coord_t>(wall_maximum_resolution_opt.value);
if (const auto& wall_maximum_deviation_opt = print_object_config.wall_maximum_deviation)
input_params.wall_maximum_deviation = scaled<coord_t>(wall_maximum_deviation_opt.value);
}
return input_params;
@@ -125,7 +131,11 @@ void simplify(Polygon &thiss, const int64_t smallest_line_segment_squared, const
accumulated_area_removed += removed_area_next;
const int64_t length2 = (current - previous).cast<int64_t>().squaredNorm();
if (length2 < scaled<int64_t>(25.)) {
// Orca:
// Checking if the segment's length is smaller than 5 microns (0.005mm).
// The value of `length2` is scaled and squared, so we need to compare it with the squared value of 5 microns
if (length2 < Slic3r::sqr(scaled<coord_t>(0.005))) {
// We're allowed to always delete segments of less than 5 micron.
continue;
}
@@ -143,6 +153,7 @@ void simplify(Polygon &thiss, const int64_t smallest_line_segment_squared, const
//h^2 = (L / b)^2 [square it]
//h^2 = L^2 / b^2 [factor the divisor]
const int64_t height_2 = double(area_removed_so_far) * double(area_removed_so_far) / double(base_length_2);
// Orca: The value of `height_2` is squared, so we need to compare it with the squared value
if ((height_2 <= Slic3r::sqr(scaled<coord_t>(0.005)) //Almost exactly colinear (barring rounding errors).
&& Line::distance_to_infinite(current, previous, next) <= scaled<double>(0.005))) // make sure that height_2 is not small because of cancellation of positive and negative areas
continue;
@@ -473,8 +484,8 @@ const std::vector<VariableWidthLines> &WallToolPaths::generate()
if (this->inset_count < 1)
return toolpaths;
const coord_t smallest_segment = Slic3r::Arachne::meshfix_maximum_resolution();
const coord_t allowed_distance = Slic3r::Arachne::meshfix_maximum_deviation();
const coord_t smallest_segment = m_params.wall_maximum_resolution;
const coord_t allowed_distance = m_params.wall_maximum_deviation;
const coord_t epsilon_offset = (allowed_distance / 2) - 1;
const double transitioning_angle = Geometry::deg2rad(m_params.wall_transition_angle);
const coord_t discretization_step_size = scaled<coord_t>(0.8);
@@ -547,7 +558,7 @@ const std::vector<VariableWidthLines> &WallToolPaths::generate()
separateOutInnerContour();
simplifyToolPaths(toolpaths);
simplifyToolPaths(toolpaths, m_params);
removeEmptyToolPaths(toolpaths);
assert(std::is_sorted(toolpaths.cbegin(), toolpaths.cend(),
@@ -688,16 +699,21 @@ void WallToolPaths::removeSmallLines(std::vector<VariableWidthLines> &toolpaths)
}
}
void WallToolPaths::simplifyToolPaths(std::vector<VariableWidthLines> &toolpaths)
void WallToolPaths::simplifyToolPaths(std::vector<VariableWidthLines>& toolpaths, const WallToolPathsParams& params)
{
for (size_t toolpaths_idx = 0; toolpaths_idx < toolpaths.size(); ++toolpaths_idx)
const int64_t maximum_resolution = params.wall_maximum_resolution;
const int64_t maximum_deviation = params.wall_maximum_deviation;
const int64_t smallest_line_segment_squared = maximum_resolution * maximum_resolution;
const int64_t allowed_error_distance_squared = maximum_deviation * maximum_deviation;
const int64_t maximum_extrusion_area_deviation = Slic3r::Arachne::meshfix_maximum_extrusion_area_deviation(); // unit: μm²
for (VariableWidthLines& lines : toolpaths)
{
const int64_t maximum_resolution = Slic3r::Arachne::meshfix_maximum_resolution();
const int64_t maximum_deviation = Slic3r::Arachne::meshfix_maximum_deviation();
const int64_t maximum_extrusion_area_deviation = Slic3r::Arachne::meshfix_maximum_extrusion_area_deviation(); // unit: μm²
for (auto& line : toolpaths[toolpaths_idx])
for (ExtrusionLine& line : lines)
{
line.simplify(maximum_resolution * maximum_resolution, maximum_deviation * maximum_deviation, maximum_extrusion_area_deviation);
line.simplify(smallest_line_segment_squared, allowed_error_distance_squared, maximum_extrusion_area_deviation);
}
}
}
+7 -3
View File
@@ -15,7 +15,7 @@
namespace Slic3r::Arachne
{
constexpr bool fill_outline_gaps = true;
constexpr bool fill_outline_gaps = true;
inline coord_t meshfix_maximum_resolution() { return scaled<coord_t>(0.5); }
inline coord_t meshfix_maximum_deviation() { return scaled<coord_t>(0.025); }
inline coord_t meshfix_maximum_extrusion_area_deviation() { return scaled<coord_t>(2.); }
@@ -31,6 +31,9 @@ public:
float wall_transition_filter_deviation;
int wall_distribution_count;
bool is_top_or_bottom_layer;
coord_t wall_maximum_resolution = meshfix_maximum_resolution();
coord_t wall_maximum_deviation = meshfix_maximum_deviation();
};
WallToolPathsParams make_paths_params(const int layer_id, const PrintObjectConfig &print_object_config, const PrintConfig &print_config);
@@ -116,10 +119,11 @@ protected:
/*!
* Simplifies the variable-width toolpaths by calling the simplify on every line in the toolpath using the provided
* settings.
* \param settings The settings as provided by the user
* \param toolpaths The toolpaths vector to simplify
* \param params The settings as provided by the user
* \return
*/
static void simplifyToolPaths(std::vector<VariableWidthLines> &toolpaths);
static void simplifyToolPaths(std::vector<VariableWidthLines>& toolpaths, const WallToolPathsParams& params);
private:
const Polygons& outline; //<! A reference to the outline polygon that is the designated area
@@ -106,7 +106,11 @@ void ExtrusionLine::simplify(const int64_t smallest_line_segment_squared, const
accumulated_area_removed += removed_area_next;
const int64_t length2 = (current - previous).cast<int64_t>().squaredNorm();
if (length2 < scaled<coord_t>(0.025))
// Orca:
// Checking if the segment's length is smaller than 5 microns (0.005mm).
// The value of `length2` is scaled and squared, so we need to compare it with the squared value of 5 microns
if (length2 < Slic3r::sqr(scaled<coord_t>(0.005)))
{
// We're allowed to always delete segments of less than 5 micron. The width in this case doesn't matter that much.
continue;
@@ -128,8 +132,9 @@ void ExtrusionLine::simplify(const int64_t smallest_line_segment_squared, const
//h^2 = L^2 / b^2 [factor the divisor]
const auto height_2 = int64_t(double(area_removed_so_far) * double(area_removed_so_far) / double(base_length_2));
const int64_t extrusion_area_error = calculateExtrusionAreaDeviationError(previous, current, next);
if ((height_2 <= scaled<coord_t>(0.001) //Almost exactly colinear (barring rounding errors).
&& Line::distance_to_infinite(current.p, previous.p, next.p) <= scaled<double>(0.001)) // Make sure that height_2 is not small because of cancellation of positive and negative areas
// Orca: The value of `height_2` is squared, so we need to compare it with the squared value
if ((height_2 <= Slic3r::sqr(scaled<coord_t>(0.005)) // Almost exactly colinear (barring rounding errors).
&& Line::distance_to_infinite(current.p, previous.p, next.p) <= scaled<double>(0.005)) // Make sure that height_2 is not small because of cancellation of positive and negative areas
// We shouldn't remove middle junctions of colinear segments if the area changed for the C-P segment is exceeding the maximum allowed
&& extrusion_area_error <= maximum_extrusion_area_deviation)
{
+1
View File
@@ -199,6 +199,7 @@ enum ConfigOptionType {
enum ConfigOptionMode {
comSimple = 0,
comAdvanced,
comExpert,
comDevelop,
};
+8 -1
View File
@@ -1320,7 +1320,14 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.density = f->print_object_config->internal_bridge_density.get_abs_value(1.0);
params.dont_adjust = true;
}
// BBS: make fill
// Orca: Elefant foot compensation for solid layers above bottommost by infill density manipulation.
float elefant_density = f->print_object_config->elefant_foot_layers_density.get_abs_value(1.0);
if (!is_approx(elefant_density, 1.0f) && surface_fill.surface.is_solid_infill()) {
size_t elefant_layers = f->print_object_config->elefant_foot_compensation_layers.value;
if (f->layer_id > 0 && f->layer_id <= elefant_layers)
params.density = elefant_density * (elefant_layers - (f->layer_id - 1)) / elefant_layers;
}
// make fill
f->fill_surface_extrusion(&surface_fill.surface,
params,
m_regions[surface_fill.region_id]->fills.entities);
+23 -9
View File
@@ -4629,8 +4629,7 @@ LayerResult GCode::process_layer(
break;
}
case CalibMode::Calib_Temp_Tower: {
auto offset = static_cast<unsigned int>(print_z / 10.001) * 5;
gcode += writer().set_temperature(print.calib_params().start - offset);
gcode += writer().set_temperature(this->interpolate_value_across_layers(static_cast<float>(print.calib_params().start), static_cast<float>(print.calib_params().end), 5.0f));
break;
}
case CalibMode::Calib_VFA_Tower: {
@@ -7232,14 +7231,29 @@ std::string GCode::extrusion_role_to_string_for_parser(const ExtrusionRole & rol
}
}
// Calculate the interpolated value for the current layer between start_value and end_value
float GCode::interpolate_value_across_layers(float start_value, float end_value) const {
if (m_layer_index == 1) {
// Calculate the interpolated value for the current layer between start_value and end_value.
// Step will create equal layers steps from first to last value.
// Step = 0 means gradual interpolation finishing at last value.
float GCode::interpolate_value_across_layers(float start_value, float end_value, float step) const
{
if (m_layer_index <= 1) {
return start_value;
} else {
float ratio = (m_layer_index - 2.0f) / (m_layer_count - 3.0f);
ratio = std::max(0.0f, std::min(1.0f, ratio)); // clamp
return start_value + ratio * (end_value - start_value);
}
else {
bool use_steps = step > 0.f;
if (use_steps) {
if (start_value > end_value) {
start_value += step;
} else {
end_value += step;
}
}
float ratio = m_layer_index / (m_layer_count - 1.f);
float value = start_value + ratio * (end_value - start_value);
if (use_steps) {
value = trunc(value / step) * step;
}
return value;
}
}
+4 -2
View File
@@ -237,8 +237,10 @@ public:
bool enable_cooling_markers() const { return m_enable_cooling_markers; }
std::string extrusion_role_to_string_for_parser(const ExtrusionRole &);
// Calculate the interpolated value for the current layer between start_value and end_value
float interpolate_value_across_layers(float start_value, float end_value) const;
// Calculate the interpolated value for the current layer between start_value and end_value.
// Step will create equal layers steps from first to last value.
// Step = 0 means gradual interpolation finishing at last value.
float interpolate_value_across_layers(float start_value, float end_value, float step = 0.0f) const;
// For Perl bindings, to be used exclusively by unit tests.
unsigned int layer_count() const { return m_layer_count; }
+30 -8
View File
@@ -220,7 +220,13 @@ ConflictComputeOpt ConflictChecker::find_inter_of_lines(const LineWithIDs &lines
ConflictResultOpt ConflictChecker::find_inter_of_lines_in_diff_objs(PrintObjectPtrs objs,
std::optional<const FakeWipeTower *> wtdptr) // find the first intersection point of lines in different objects
{
if (objs.size() <= 1 && !wtdptr) { return {}; }
if (objs.empty() && !wtdptr) { return {}; }
// Orca: check if we have enough items to potentially conflict (instances count)
size_t total_instances = 0;
for (auto obj : objs) total_instances += obj->instances().size();
if (total_instances <= 1 && !wtdptr) return {};
LinesBucketQueue conflictQueue;
if (wtdptr.has_value()) { // wipe tower at 0 by default
@@ -238,8 +244,19 @@ ConflictResultOpt ConflictChecker::find_inter_of_lines_in_diff_objs(PrintObjectP
}
for (PrintObject *obj : objs) {
auto layers = getAllLayersExtrusionPathsFromObject(obj);
conflictQueue.emplace_back_bucket(std::move(layers.perimeters), obj, obj->instances().front().shift);
conflictQueue.emplace_back_bucket(std::move(layers.support), obj, obj->instances().front().shift);
// Orca: check for collisions between all instances
const auto& instances = obj->instances();
for (size_t inst_idx = 0; inst_idx < instances.size(); ++inst_idx) {
const PrintInstance& inst = instances[inst_idx];
const bool is_last_instance = inst_idx + 1 == instances.size();
if (is_last_instance) {
conflictQueue.emplace_back_bucket(std::move(layers.perimeters), &inst, inst.shift);
conflictQueue.emplace_back_bucket(std::move(layers.support), &inst, inst.shift);
} else {
conflictQueue.emplace_back_bucket(ExtrusionLayers(layers.perimeters), &inst, inst.shift);
conflictQueue.emplace_back_bucket(ExtrusionLayers(layers.support), &inst, inst.shift);
}
}
}
std::vector<LineWithIDs> layersLines;
@@ -272,13 +289,18 @@ ConflictResultOpt ConflictChecker::find_inter_of_lines_in_diff_objs(PrintObjectP
const FakeWipeTower *wtdp = wtdptr.value();
if (ptr1 == wtdp || ptr2 == wtdp) {
if (ptr2 == wtdp) { std::swap(ptr1, ptr2); }
const PrintObject *obj2 = reinterpret_cast<const PrintObject *>(ptr2);
return std::make_optional<ConflictResult>("WipeTower", obj2->model_object()->name, conflictPrintZ, nullptr, ptr2);
// ptr1 is now wipe tower, ptr2 is PrintInstance*
const PrintInstance *inst2 = reinterpret_cast<const PrintInstance *>(ptr2);
const PrintObject *obj2 = inst2->print_object;
return std::make_optional<ConflictResult>("WipeTower", obj2->model_object()->name, conflictPrintZ, nullptr, inst2);
}
}
const PrintObject *obj1 = reinterpret_cast<const PrintObject *>(ptr1);
const PrintObject *obj2 = reinterpret_cast<const PrintObject *>(ptr2);
return std::make_optional<ConflictResult>(obj1->model_object()->name, obj2->model_object()->name, conflictPrintZ, ptr1, ptr2);
const PrintInstance *inst1 = reinterpret_cast<const PrintInstance *>(ptr1);
const PrintInstance *inst2 = reinterpret_cast<const PrintInstance *>(ptr2);
const PrintObject *obj1 = inst1->print_object;
const PrintObject *obj2 = inst2->print_object;
return std::make_optional<ConflictResult>(obj1->model_object()->name, obj2->model_object()->name, conflictPrintZ, inst1, inst2);
} else
return {};
}
+5 -1
View File
@@ -33,7 +33,11 @@ const std::string& FanMover::process_gcode(const std::string& gcode, bool flush)
if (flush) {
while (!m_buffer.empty()) {
m_process_output += m_buffer.front().raw + "\n";
BufferData &front = m_buffer.front();
m_process_output += front.raw + "\n";
// Orca: Keep the emitted fan state in sync when flushing buffered fan commands.
if (front.fan_speed >= 0)
m_front_buffer_fan_speed = front.fan_speed;
remove_from_buffer(m_buffer.begin());
}
}
+13 -3
View File
@@ -1729,8 +1729,12 @@ void PerimeterGenerator::process_no_bridge(Surfaces& all_surfaces, coord_t perim
//compute our unsupported surface
ExPolygons unsupported = diff_ex(last, *this->lower_slices, ApplySafetyOffset::Yes);
if (!unsupported.empty()) {
//remove small overhangs
ExPolygons unsupported_filtered = offset2_ex(unsupported, double(-perimeter_spacing), double(perimeter_spacing));
// remove small overhangs (when using chbFilled we need to be less aggressive in removing small overhangs,
// to avoid affecting bridging detection.)
const int outset_divisor = this->config->counterbore_hole_bridging.value == chbFilled ? 2 : 1;
ExPolygons unsupported_filtered = offset2_ex(unsupported, double(-perimeter_spacing),
double(perimeter_spacing) / outset_divisor);
if (!unsupported_filtered.empty()) {
//to_draw.insert(to_draw.end(), last.begin(), last.end());
//extract only the useful part of the lower layer. The safety offset is really needed here.
@@ -1773,7 +1777,7 @@ void PerimeterGenerator::process_no_bridge(Surfaces& all_surfaces, coord_t perim
}
}
unsupported_filtered = intersection_ex(last,
offset2_ex(unsupported_filtered, double(-perimeter_spacing / 2), double(bridged_infill_margin + perimeter_spacing / 2)));
offset_ex(unsupported_filtered, 0.5 * double(bridged_infill_margin)));
if (this->config->counterbore_hole_bridging.value == chbFilled) {
for (ExPolygon& expol : unsupported_filtered) {
//check if the holes won't be covered by the upper layer
@@ -1825,6 +1829,12 @@ void PerimeterGenerator::process_no_bridge(Surfaces& all_surfaces, coord_t perim
}
//TODO: add other polys as holes inside this one (-margin)
} else if (/*this->config->counterbore_hole_bridging.value == chbBridgesOverhangs || */this->config->counterbore_hole_bridging.value == chbBridges) {
// Partially bridged counterbore handling should not rewrite generic bridge islands
// because by doing so regular bridges will lose their overhang-wall perimeters.
if (surface->expolygon.holes.empty()) {
unsupported_filtered.clear(); // "Partially bridged" only applies to hole-bearing bridge islands.
continue;
}
//simplify to avoid most of artefacts from printing lines.
ExPolygons bridgeable_simplified;
for (ExPolygon& poly : bridgeable) {
+4 -3
View File
@@ -925,7 +925,7 @@ static std::vector<std::string> s_Preset_print_options {
"outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width",
"skin_infill_line_width","skeleton_infill_line_width",
"top_surface_line_width", "support_line_width", "infill_wall_overlap","top_bottom_infill_wall_overlap", "bridge_flow", "internal_bridge_flow",
"elefant_foot_compensation", "elefant_foot_compensation_layers", "xy_contour_compensation", "xy_hole_compensation", "resolution", "enable_prime_tower", "prime_tower_enable_framework",
"elefant_foot_compensation", "elefant_foot_compensation_layers", "elefant_foot_layers_density", "xy_contour_compensation", "xy_hole_compensation", "resolution", "enable_prime_tower", "prime_tower_enable_framework",
"prime_tower_width", "prime_tower_brim_width", "prime_tower_skip_points", "prime_volume",
"prime_tower_infill_gap",
"prime_tower_flat_ironing",
@@ -940,8 +940,9 @@ static std::vector<std::string> s_Preset_print_options {
"support_bottom_interface_spacing", "enable_overhang_speed", "slowdown_for_curled_perimeters", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed",
"initial_layer_infill_speed", "only_one_wall_top",
"timelapse_type",
"wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
"wall_distribution_count", "min_feature_size", "min_bead_width", "post_process", "process_change_extrusion_role_gcode", "min_length_factor",
"wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
"wall_distribution_count", "min_feature_size", "min_bead_width", "post_process", "process_change_extrusion_role_gcode",
"min_length_factor", "wall_maximum_resolution", "wall_maximum_deviation",
"small_perimeter_speed", "small_perimeter_threshold","bridge_angle","internal_bridge_angle", "filter_out_gap_fill", "travel_acceleration","inner_wall_acceleration", "min_width_top_surface",
"default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk","travel_jerk","default_junction_deviation",
"top_solid_infill_flow_ratio","bottom_solid_infill_flow_ratio","only_one_wall_first_layer", "print_flow_ratio", "seam_gap",
+68 -39
View File
@@ -615,7 +615,6 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
std::for_each(exclude_polys.begin(), exclude_polys.end(),
[&print_origin](Polygon& p) { p.translate(scale_(print_origin.x()), scale_(print_origin.y())); });
std::map<ObjectID, Polygon> map_model_object_to_convex_hull;
struct print_instance_info
{
const PrintInstance *print_instance;
@@ -650,27 +649,13 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
for (const PrintObject *print_object : print.objects()) {
assert(! print_object->model_object()->instances.empty());
assert(! print_object->instances().empty());
ObjectID model_object_id = print_object->model_object()->id();
auto it_convex_hull = map_model_object_to_convex_hull.find(model_object_id);
// Get convex hull of all printable volumes assigned to this print object.
ModelInstance *model_instance0 = print_object->model_object()->instances.front();
if (it_convex_hull == map_model_object_to_convex_hull.end()) {
// Calculate the convex hull of a printable object.
// Grow convex hull with the clearance margin.
// FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2)
// which causes that the warning will be showed after arrangement with the
// appropriate object distance. Even if I set this to jtMiter the warning still shows up.
it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id,
print_object->model_object()->convex_hull_2d(Geometry::assemble_transform(
{ 0.0, 0.0, model_instance0->get_offset().z() }, model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())));
}
// Make a copy, so it may be rotated for instances.
Polygon convex_hull0 = it_convex_hull->second;
const double z_diff = Geometry::rotation_diff_z(model_instance0->get_rotation(), print_object->instances().front().model_instance->get_rotation());
if (std::abs(z_diff) > EPSILON)
convex_hull0.rotate(z_diff);
// Orca: check convex hull intersection for each instance individually to handle rotation/offset differences correctly
// Now we check that no instance of convex_hull intersects any of the previously checked object instances.
for (const PrintInstance &instance : print_object->instances()) {
Polygon convex_hull0 = print_object->model_object()->convex_hull_2d(Geometry::assemble_transform(
{ 0.0, 0.0, instance.model_instance->get_offset().z() }, instance.model_instance->get_rotation(), instance.model_instance->get_scaling_factor(), instance.model_instance->get_mirror()));
Polygon convex_hull_no_offset = convex_hull0, convex_hull;
auto tmp = offset(convex_hull_no_offset, obj_distance, jtRound, scale_(0.1));
if (!tmp.empty()) { // tmp may be empty due to clipper's bug, see STUDIO-2452
@@ -684,7 +669,9 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
if (!intersection(exclude_polys, convex_hull_no_offset).empty()) {
if (single_object_exception.string.empty()) {
single_object_exception.string = (boost::format(L("%1% is too close to exclusion area, there may be collisions when printing.")) %instance.model_instance->get_object()->name).str();
single_object_exception.object = instance.model_instance->get_object();
// single_object_exception.object = instance.model_instance->get_object();
//ORCA: Pass ModelInstance instead of ModelObject
single_object_exception.object = instance.model_instance;
}
else {
single_object_exception.string += "\n"+(boost::format(L("%1% is too close to exclusion area, there may be collisions when printing.")) %instance.model_instance->get_object()->name).str();
@@ -701,12 +688,16 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
bool has_exception = false;
if (single_object_exception.string.empty()) {
single_object_exception.string = (boost::format(L("%1% is too close to others, and collisions may be caused.")) %instance.model_instance->get_object()->name).str();
single_object_exception.object = instance.model_instance->get_object();
// single_object_exception.object = instance.model_instance->get_object();
//ORCA: Pass ModelInstance instead of ModelObject for better selection
single_object_exception.object = instance.model_instance;
has_exception = true;
}
else {
single_object_exception.string += "\n"+(boost::format(L("%1% is too close to others, and collisions may be caused.")) %instance.model_instance->get_object()->name).str();
single_object_exception.object = nullptr;
// single_object_exception.object = nullptr;
// ORCA: Keep the first object so jump works
// has_exception = true;
has_exception = true;
}
@@ -953,33 +944,52 @@ static StringObjectException layered_print_cleareance_valid(const Print &print,
wrapping_poly.points.emplace_back(scale_(pt.x() + print_origin.x()), scale_(pt.y() + print_origin.y()));
}
std::map<const ModelVolume*, Polygon> map_model_volume_to_convex_hull;
Polygons convex_hulls_other;
// Orca: check convex hull intersection for each instance individually
for (auto& inst : print_instances_ordered) {
Polygons current_instance_hulls;
for (const ModelVolume *v : inst->print_object->model_object()->volumes) {
if (!v->is_model_part()) continue;
auto it_convex_hull = map_model_volume_to_convex_hull.find(v);
if (it_convex_hull == map_model_volume_to_convex_hull.end()) {
auto volume_hull = v->get_convex_hull_2d(Geometry::assemble_transform(Vec3d::Zero(), inst->model_instance->get_rotation(),
inst->model_instance->get_scaling_factor(), inst->model_instance->get_mirror()));
volume_hull.translate(inst->shift - inst->print_object->center_offset());
auto volume_hull = v->get_convex_hull_2d(Geometry::assemble_transform(Vec3d::Zero(), inst->model_instance->get_rotation(),
inst->model_instance->get_scaling_factor(), inst->model_instance->get_mirror()));
volume_hull.translate(inst->shift - inst->print_object->center_offset());
it_convex_hull = map_model_volume_to_convex_hull.emplace_hint(it_convex_hull, v, volume_hull);
}
Polygon &convex_hull = it_convex_hull->second;
Polygons convex_hulls_temp;
convex_hulls_temp.push_back(convex_hull);
if (!intersection(exclude_polys, convex_hull).empty()) {
if (!intersection(exclude_polys, volume_hull).empty()) {
// return {inst->model_instance->get_object()->name + L(" is too close to exclusion area, there may be collisions when printing.") + "\n",
// inst->model_instance->get_object()};
//ORCA: Pass ModelInstance instead of ModelObject
return {inst->model_instance->get_object()->name + L(" is too close to exclusion area, there may be collisions when printing.") + "\n",
inst->model_instance->get_object()};
inst->model_instance};
}
if (print_config.enable_wrapping_detection.value && !intersection(wrapping_poly, convex_hull).empty()) {
if (print_config.enable_wrapping_detection.value && !intersection(wrapping_poly, volume_hull).empty()) {
// return {inst->model_instance->get_object()->name + L(" is too close to clumping detection area, there may be collisions when printing.") + "\n",
// inst->model_instance->get_object()};
//ORCA: Pass ModelInstance instead of ModelObject
return {inst->model_instance->get_object()->name + L(" is too close to clumping detection area, there may be collisions when printing.") + "\n",
inst->model_instance->get_object()};
inst->model_instance};
}
convex_hulls_other.emplace_back(convex_hull);
current_instance_hulls.emplace_back(volume_hull);
}
if (!intersection(convex_hulls_other, current_instance_hulls).empty()) {
if (warning) {
if (warning->string.empty()) {
warning->string = (boost::format(L("%1% is too close to others, and collisions may be caused.")) % inst->model_instance->get_object()->name).str();
// warning->object = inst->model_instance->get_object();
//ORCA: Pass ModelInstance instead of ModelObject for better selection
warning->object = inst->model_instance;
} else {
warning->string += "\n" + (boost::format(L("%1% is too close to others, and collisions may be caused.")) % inst->model_instance->get_object()->name).str();
// ORCA: Keep the first object so jump works
if (!warning->object) warning->object = inst->model_instance;
}
warning->is_warning = true;
warning->type = STRING_EXCEPT_OBJECT_COLLISION_IN_LAYER_PRINT;
}
}
append(convex_hulls_other, current_instance_hulls);
}
//BBS: add the wipe tower check logic
@@ -4931,6 +4941,25 @@ ExtrusionLayers FakeWipeTower::getTrueExtrusionLayersFromWipeTower() const
{
ExtrusionLayers wtels;
wtels.type = ExtrusionLayersType::WIPE_TOWER;
//ORCA: Fallback for WipeTower2 if outer_wall is empty
if (outer_wall.empty()) {
auto fake_paths = getFakeExtrusionPathsFromWipeTower2();
float current_z = 0.f;
for (auto& layer_paths : fake_paths) {
if (layer_paths.empty()) continue;
ExtrusionLayer el;
float lh = layer_paths.front().height;
el.height = lh;
el.bottom_z = current_z;
el.layer = nullptr;
el.paths = std::move(layer_paths);
wtels.push_back(std::move(el));
current_z += lh;
}
return wtels;
}
std::vector<float> layer_heights;
layer_heights.reserve(outer_wall.size());
auto pre = outer_wall.begin();
+61 -2
View File
@@ -714,6 +714,18 @@ void PrintConfigDef::init_common_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(1));
def = this->add("elefant_foot_layers_density", coPercent);
def->label = L("Elefant foot layers density");
def->category = L("Quality");
def->tooltip = L("Density of internal solid infill for elefant foot layers compensation. "
"The initial value for the second layer is set. "
"Subsequent layers become linearly denser by the height specified in elefant_foot_compensation_layers. ");
def->sidetext = "%";
def->min = 50;
def->max = 100;
def->mode = comExpert;
def->set_default_value(new ConfigOptionPercent(100));
def = this->add("layer_height", coFloat);
def->label = L("Layer height");
def->category = L("Quality");
@@ -2602,7 +2614,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Number of cooling moves");
def->tooltip = L("Filament is cooled by being moved back and forth in the "
"cooling tubes. Specify desired number of these moves.");
def->max = 0;
def->min = 0;
def->max = 20;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInts { 4 });
@@ -6915,6 +6927,29 @@ void PrintConfigDef::init_fff_params()
def->max = 25.0;
def->set_default_value(new ConfigOptionFloat(0.5));
def = this->add("wall_maximum_resolution", coFloat);
def->label = L("Maximum wall resolution");
def->category = L("Quality");
def->tooltip = L("This value determines the smallest wall line segment length in mm. "
"The smaller you set this value, the more accurate and precise the walls will be.");
def->sidetext = L("mm"); // millimeters, CIS languages need translation
def->mode = comExpert;
def->min = 0.005;
def->max = 0.5f;
def->set_default_value(new ConfigOptionFloat(0.5f));
def = this->add("wall_maximum_deviation", coFloat);
def->label = L("Maximum wall deviation");
def->category = L("Quality");
def->tooltip = L("The maximum deviation allowed when reducing the resolution for the 'Maximum wall resolution' setting. If you increase this, "
"the print will be less accurate, but the G-Code will be smaller. 'Maximum wall deviation' limits 'Maximum wall resolution', "
"so if the two conflict, 'Maximum wall deviation' takes precedence.");
def->sidetext = L("mm"); // millimeters, CIS languages need translation
def->mode = comExpert;
def->min = 0.005f;
def->max = 0.05f;
def->set_default_value(new ConfigOptionFloat(0.025f));
def = this->add("initial_layer_min_bead_width", coPercent);
def->label = L("First layer minimum wall width");
def->category = L("Quality");
@@ -11063,8 +11098,32 @@ bool has_skirt(const DynamicPrintConfig& cfg)
|| (opt_draft_shield && opt_draft_shield->getInt() != dsDisabled);
}
float get_real_skirt_dist(const DynamicPrintConfig& cfg) {
return has_skirt(cfg) ? cfg.opt_float("skirt_distance") : 0;
if (!has_skirt(cfg)) return 0.f;
float dist = cfg.opt_float("skirt_distance");
int loops = cfg.opt_int("skirt_loops");
auto opt_draft_shield = cfg.option("draft_shield");
if (opt_draft_shield && opt_draft_shield->getInt() != dsDisabled && loops == 0) {
loops = 1;
}
float width = cfg.opt_float("initial_layer_line_width");
if (width <= 0.f) {
width = cfg.opt_float("line_width");
}
if (width <= 0.f) {
auto* nd = cfg.opt<ConfigOptionFloats>("nozzle_diameter");
if (nd && !nd->values.empty()) {
width = *std::max_element(nd->values.begin(), nd->values.end());
} else {
width = 0.4f;
}
}
return dist + loops * width;
}
static bool is_XL_printer(const std::string& printer_notes)
{
return boost::algorithm::contains(printer_notes, "PRINTER_VENDOR_PRUSA3D")
+3
View File
@@ -918,6 +918,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, bridge_no_support))
((ConfigOptionFloat, elefant_foot_compensation))
((ConfigOptionInt, elefant_foot_compensation_layers))
((ConfigOptionPercent, elefant_foot_layers_density))
((ConfigOptionFloat, max_bridge_length))
((ConfigOptionFloatOrPercent, line_width))
// Force the generation of solid shells between adjacent materials/volumes.
@@ -1016,6 +1017,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionPercent, min_bead_width))
// Orca
((ConfigOptionFloat, wall_maximum_resolution))
((ConfigOptionFloat, wall_maximum_deviation))
((ConfigOptionFloat, make_overhang_printable_angle))
((ConfigOptionFloat, make_overhang_printable_hole_size))
((ConfigOptionFloat, tree_support_branch_distance_organic))
+3
View File
@@ -1092,6 +1092,7 @@ bool PrintObject::invalidate_state_by_config_options(
} else if (
opt_key == "elefant_foot_compensation"
|| opt_key == "elefant_foot_compensation_layers"
|| opt_key == "elefant_foot_layers_density"
|| opt_key == "support_top_z_distance"
|| opt_key == "support_bottom_z_distance"
|| opt_key == "xy_hole_compensation"
@@ -1289,6 +1290,8 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "wall_transition_filter_deviation"
|| opt_key == "wall_transition_angle"
|| opt_key == "wall_distribution_count"
|| opt_key == "wall_maximum_resolution"
|| opt_key == "wall_maximum_deviation"
|| opt_key == "min_feature_size"
|| opt_key == "min_length_factor"
|| opt_key == "min_bead_width") {
+3 -3
View File
@@ -721,7 +721,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
toggle_line("brim_ears_detection_length", have_brim_ear);
// Hide Elephant foot compensation layers if elefant_foot_compensation is not enabled
toggle_line("elefant_foot_compensation_layers", config->opt_float("elefant_foot_compensation") > 0);
toggle_line("elefant_foot_compensation_layers", config->opt_float("elefant_foot_compensation") > 0 || config->option<ConfigOptionPercent>("elefant_foot_layers_density")->get_abs_value(1.0f) < 1.0f);
bool have_raft = config->opt_int("raft_layers") > 0;
bool have_support_material = config->opt_bool("enable_support") || have_raft;
@@ -882,8 +882,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
toggle_line("fuzzy_skin_persistence", (fuzzy_skin_noise_type == NoiseType::Perlin || fuzzy_skin_noise_type == NoiseType::Billow) && has_fuzzy_skin);
bool have_arachne = config->opt_enum<PerimeterGeneratorType>("wall_generator") == PerimeterGeneratorType::Arachne;
for (auto el : {"wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
"min_feature_size", "min_length_factor", "min_bead_width", "wall_distribution_count", "initial_layer_min_bead_width"})
for (auto el : {"wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", "min_feature_size", "min_length_factor",
"min_bead_width", "wall_distribution_count", "initial_layer_min_bead_width", "wall_maximum_resolution", "wall_maximum_deviation"})
toggle_line(el, have_arachne);
toggle_field("detect_thin_wall", !have_arachne);
+1
View File
@@ -2,6 +2,7 @@
#include "PartPlate.hpp"
#include "Widgets/Button.hpp"
#include "Widgets/DialogButtons.hpp"
#include "Widgets/Label.hpp"
#include "I18N.hpp"
#include "GUI_App.hpp"
#include "CapsuleButton.hpp"
+2 -1
View File
@@ -4,7 +4,8 @@
#include "GUI.hpp"
#include "DragDropPanel.hpp"
#include "Widgets/SwitchButton.hpp"
#include "wxExtensions.hpp"
#include "Widgets/Label.hpp"
namespace Slic3r { namespace GUI {
+19 -4
View File
@@ -1047,6 +1047,8 @@ wxDEFINE_EVENT(EVT_CUSTOMEVT_TICKSCHANGED, wxCommandEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_PRINTABLE, SimpleEvent);
const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25;
const double GLCanvas3D::DefaultCameraZoomToBedMarginFactor = 2.00;
@@ -3497,6 +3499,8 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
// }
// break;
//}
case 'v':
case 'V': { post_event(SimpleEvent(EVT_GLCANVAS_PRINTABLE)); break; }
default: { evt.Skip(); break; }
}
}
@@ -9698,6 +9702,7 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
std::string text;
ErrorType error = ErrorType::PLATER_WARNING;
const ModelObject* conflictObj=nullptr;
const ModelInstance* conflictInst=nullptr;
switch (warning) {
case EWarning::GCodeConflict: {
static std::string prevConflictText;
@@ -9708,12 +9713,17 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
std::string objName2 = m_gcode_viewer.m_conflict_result.value()._objName2;
double height = m_gcode_viewer.m_conflict_result.value()._height;
int layer = m_gcode_viewer.m_conflict_result.value().layer;
const PrintInstance *inst2 = reinterpret_cast<const PrintInstance *>(m_gcode_viewer.m_conflict_result.value()._obj2);
text = (boost::format(_u8L("Conflicts of G-code paths have been found at layer %d, Z = %.2lfmm. Please separate the conflicted objects farther (%s <-> %s).")) % layer %
height % objName1 % objName2)
.str();
prevConflictText = text;
const PrintObject *obj2 = reinterpret_cast<const PrintObject *>(m_gcode_viewer.m_conflict_result.value()._obj2);
conflictObj = obj2->model_object();
if (inst2) {
if (inst2->model_instance) conflictInst = inst2->model_instance;
else if (inst2->print_object) conflictObj = inst2->print_object->model_object();
}
break;
}
case EWarning::ObjectOutside: text = _u8L("An object is laid over the plate boundaries."); break;
@@ -9961,8 +9971,13 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
}
break;
case SLICING_SERIOUS_WARNING:
if (state)
notification_manager.push_slicing_serious_warning_notification(text, conflictObj ? std::vector<ModelObject const*>{conflictObj} : std::vector<ModelObject const*>{});
if (state) {
if (conflictInst) {
notification_manager.push_slicing_serious_warning_notification(text, std::vector<ModelInstance const*>{conflictInst});
} else {
notification_manager.push_slicing_serious_warning_notification(text, conflictObj ? std::vector<ModelObject const*>{conflictObj} : std::vector<ModelObject const*>{});
}
}
else
notification_manager.close_slicing_serious_warning_notification(text);
break;
+1
View File
@@ -195,6 +195,7 @@ wxDECLARE_EVENT(EVT_CUSTOMEVT_TICKSCHANGED, wxCommandEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_PRINTABLE, SimpleEvent);
class GLCanvas3D
{
+70 -21
View File
@@ -1005,11 +1005,13 @@ void GUI_App::post_init()
CallAfter([this] {
bool cw_showed = this->config_wizard_startup();
std::string http_url = get_http_url(app_config->get_country_code());
std::string language = GUI::into_u8(current_language_code());
std::string network_ver = Slic3r::NetworkAgent::get_version();
bool sys_preset = app_config->get("sync_system_preset") == "true";
this->preset_updater->sync(http_url, language, network_ver, sys_preset ? preset_bundle : nullptr);
if (!app_config->get_stealth_mode()) {
std::string http_url = get_http_url(app_config->get_country_code());
std::string language = GUI::into_u8(current_language_code());
std::string network_ver = Slic3r::NetworkAgent::get_version();
bool sys_preset = app_config->get("sync_system_preset") == "true";
this->preset_updater->sync(http_url, language, network_ver, sys_preset ? preset_bundle : nullptr);
}
this->check_new_version_sf();
if (is_user_login() && !app_config->get_stealth_mode()) {
@@ -1814,7 +1816,7 @@ bool GUI_App::hot_reload_network_plugin()
<< ", m_agent = " << (m_agent ? "valid" : "null")
<< ", version = " << loaded_version;
if (success && m_agent && m_device_manager) {
if (success && m_agent && m_device_manager && !app_config->get_stealth_mode()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": connecting to cloud server";
m_agent->connect_server();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": re-subscribing to cloud printers";
@@ -2500,7 +2502,7 @@ std::map<std::string, std::string> GUI_App::get_extra_header()
std::map<std::string, std::string> extra_headers;
extra_headers.insert(std::make_pair("X-BBL-Client-Type", "slicer"));
extra_headers.insert(std::make_pair("X-BBL-Client-Name", SLIC3R_APP_NAME));
extra_headers.insert(std::make_pair("X-BBL-Client-Version", VersionInfo::convert_full_version(SLIC3R_VERSION)));
extra_headers.insert(std::make_pair("X-BBL-Client-Version", get_bbl_client_version()));
#if defined(__WINDOWS__)
#ifdef _M_X64
extra_headers.insert(std::make_pair("X-BBL-OS-Type", "windows"));
@@ -2522,6 +2524,16 @@ std::map<std::string, std::string> GUI_App::get_extra_header()
return extra_headers;
}
std::string GUI_App::get_bbl_client_version()
{
if (BBLNetworkPlugin::instance().get_get_my_token() == nullptr) {
// Legacy Bambu plugin lacks bambu_network_get_my_token. Pin client
// version so the auth server keeps using the ?access_token= redirect.
return "01.10.01.50";
}
return VersionInfo::convert_full_version(SLIC3R_VERSION);
}
//BBS
void GUI_App::init_http_extra_header()
{
@@ -4925,6 +4937,10 @@ void GUI_App::on_update_machine_list(wxCommandEvent &evt)
void GUI_App::on_user_login_handle(wxCommandEvent &evt)
{
if (!m_agent) { return; }
if (app_config->get_stealth_mode()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": stealth mode enabled, skipping cloud connection";
return;
}
int online_login = evt.GetInt();
m_agent->connect_server();
@@ -5683,6 +5699,11 @@ void GUI_App::on_check_privacy_update(wxCommandEvent& evt)
void GUI_App::check_privacy_version(int online_login)
{
if (app_config->get_stealth_mode()) {
request_user_handle(online_login);
return;
}
update_http_extra_header();
std::string query_params = "?policy/privacy=00.00.00.00";
std::string url = get_http_url(app_config->get_country_code()) + query_params;
@@ -6572,31 +6593,59 @@ Tab* GUI_App::get_layer_tab()
return model_tabs_list[2];
}
ConfigOptionMode GUI_App::get_mode()
namespace
{
ConfigOptionMode saved_mode_from_string(const std::string& mode)
{
return mode == "expert" ? comExpert :
mode == "advanced" ? comAdvanced :
mode == "develop" ? comAdvanced :
comSimple;
}
std::string saved_mode_to_string(ConfigOptionMode mode)
{
return mode == comExpert ? "expert" :
mode == comAdvanced ? "advanced" :
"simple";
}
std::string effective_mode_to_string(ConfigOptionMode mode)
{
return mode == comDevelop ? "develop" : saved_mode_to_string(mode);
}
}
ConfigOptionMode GUI_App::get_saved_mode()
{
if (!app_config->has("user_mode"))
return comSimple;
//BBS
const auto mode = app_config->get("user_mode");
return mode == "advanced" ? comAdvanced :
mode == "simple" ? comSimple :
mode == "develop" ? comDevelop : comSimple;
return saved_mode_from_string(app_config->get("user_mode"));
}
ConfigOptionMode GUI_App::get_mode()
{
return app_config->get_bool("developer_mode") ? comDevelop : get_saved_mode();
}
std::string GUI_App::get_saved_mode_str()
{
return saved_mode_to_string(get_saved_mode());
}
std::string GUI_App::get_mode_str()
{
if (!app_config->has("user_mode"))
return "simple";
return app_config->get("user_mode");
return effective_mode_to_string(get_mode());
}
void GUI_App::save_mode(const /*ConfigOptionMode*/int mode)
{
//BBS
const std::string mode_str = mode == comAdvanced ? "advanced" :
mode == comSimple ? "simple" :
mode == comDevelop ? "develop" : "simple";
app_config->set("user_mode", mode_str);
const auto saved_mode = mode == comExpert ? comExpert :
mode == comAdvanced ? comAdvanced :
mode == comSimple ? comSimple :
get_saved_mode();
app_config->set("user_mode", saved_mode_to_string(saved_mode));
update_mode();
}
+6
View File
@@ -534,7 +534,9 @@ public:
Tab* get_plate_tab();
Tab* get_model_tab(bool part = false);
Tab* get_layer_tab();
ConfigOptionMode get_saved_mode();
ConfigOptionMode get_mode();
std::string get_saved_mode_str();
std::string get_mode_str();
void save_mode(const /*ConfigOptionMode*/int mode) ;
void update_mode();
@@ -705,6 +707,10 @@ public:
bool hot_reload_network_plugin();
std::string get_latest_network_version() const;
bool has_network_update_available() const;
// Return the client version to report to Bambu servers. Pinned to
// 01.10.01.50 when the legacy network plugin lacks get_my_token support
// so the auth server stays on the ?access_token= redirect path.
std::string get_bbl_client_version();
private:
int updating_bambu_networking();
+23 -4
View File
@@ -777,17 +777,37 @@ wxMenuItem* MenuFactory::append_menu_item_change_type(wxMenu* menu)
// Update checkmark dynamically when menu is shown - check all selected volumes
m_parent->Bind(wxEVT_UPDATE_UI, [type = info.type](wxUpdateUIEvent& evt) {
auto model = obj_list()->GetModel();
auto objs = obj_list()->objects();
bool has_type = false;
wxDataViewItemArray sels;
obj_list()->GetSelections(sels);
for (auto item : sels) {
ModelVolumeType vol_type = obj_list()->GetModel()->GetVolumeType(item);
ModelVolumeType vol_type = model->GetVolumeType(item);
if (vol_type == type) {
has_type = true;
break;
}
}
evt.Check(has_type);
// ORCA Fix crash caused by SVG/TEXT volumes cant be Support Enforcer/Blocker type
for (auto item : sels) {
if (model->GetItemType(item) == itVolume){
auto vol_idx = model->GetVolumeIdByItem(item);
auto obj_idx = model->GetObjectIdByItem(item);
if (vol_idx < 0 || obj_idx < 0)
continue;
auto vol = (*objs)[obj_idx]->volumes[vol_idx];
// disable Support Enforcer/Blocker if selection contains svg or text
if (vol != nullptr && (vol->is_svg() || vol->is_text()) && (type == ModelVolumeType::SUPPORT_BLOCKER || type == ModelVolumeType::SUPPORT_ENFORCER)){
evt.Enable(false);
break;
}
}
}
}, item->GetId());
}
@@ -827,7 +847,7 @@ void MenuFactory::append_menu_item_fill_bed(wxMenu *menu)
wxMenuItem* MenuFactory::append_menu_item_printable(wxMenu* menu)
{
// BBS: to be checked
wxMenuItem* menu_item_printable = append_menu_check_item(menu, wxID_ANY, _L("Printable"), "",
wxMenuItem* menu_item_printable = append_menu_check_item(menu, wxID_ANY, _L("Printable") + "\t" + "V", "",
[](wxCommandEvent&) { obj_list()->toggle_printable_state(); }, menu);
m_parent->Bind(wxEVT_UPDATE_UI, [](wxUpdateUIEvent& evt) {
@@ -2222,8 +2242,7 @@ void MenuFactory::append_menu_item_set_printable(wxMenu* menu)
}
}
wxString menu_text = _L("Printable");
wxMenuItem* menu_item_set_printable = append_menu_check_item(menu, wxID_ANY, menu_text, "", [this, all_printable](wxCommandEvent&) {
wxMenuItem* menu_item_set_printable = append_menu_check_item(menu, wxID_ANY, _L("Printable") + "\t" + "V", "", [this, all_printable](wxCommandEvent&) {
Selection& selection = plater()->canvas3D()->get_selection();
selection.set_printable(!all_printable);
}, menu);
+8 -7
View File
@@ -81,14 +81,14 @@ bool GLGizmoFdmSupports::on_init()
m_shortcut_key = WXK_CONTROL_L;
m_desc["perform"] = _L("Perform");
m_desc["on_overhangs_only"] = _L("On overhangs only");
m_desc["on_overhangs_only"] = _L("On highlighted overhangs only");
m_desc["remove_all"] = _L("Erase all painting");
m_desc["highlight_by_angle"] = _L("Highlight overhang areas");
m_desc["tool_type"] = _L("Tool type");
m_desc["gap_fill"] = _L("Gap fill");
m_desc["reset_direction"] = _L("Reset direction");
m_desc["clipping_of_view"] = _L("Section view");
m_desc["cursor_size"] = _L("Pen size");
m_desc["cursor_size"] = _L("Brush size");
m_desc["smart_fill_angle"] = _L("Smart fill angle");
m_desc["gap_area"] = _L("Gap area");
@@ -360,6 +360,12 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::BBLDragFloat("##gap_area_input", &TriangleSelectorPatch::gap_area, 0.05f, 0.0f, 0.0f, "%.2f");
}
m_imgui->bbl_checkbox(m_desc["on_overhangs_only"], m_paint_on_overhangs_only);
if (ImGui::IsItemHovered())
m_imgui->tooltip(format_wxstr(_L("Allows painting only on facets selected by: \"%1%\""), m_desc["highlight_by_angle"]),
max_tooltip_width);
ImGui::Separator();
float position_before_text_y = ImGui::GetCursorPos().y;
ImGui::AlignTextToFramePadding();
@@ -398,11 +404,6 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##angle_threshold_deg_input", &m_highlight_by_angle_threshold_deg, 0.05f, 0.0f, 0.0f, "%.2f");
m_imgui->bbl_checkbox(m_desc["on_overhangs_only"], m_paint_on_overhangs_only);
if (ImGui::IsItemHovered())
m_imgui->tooltip(format_wxstr(_L("Allows painting only on facets selected by: \"%1%\""), m_desc["highlight_by_angle"]),
max_tooltip_width);
ImGui::Separator();
if (m_c->object_clipper()->get_position() == 0.f) {
ImGui::AlignTextToFramePadding();
+1 -1
View File
@@ -45,7 +45,7 @@ bool GLGizmoFuzzySkin::on_init()
m_desc["tool_brush"] = _L("Brush");
m_desc["tool_smart_fill"] = _L("Smart fill");
m_desc["clipping_of_view"] = _L("Section view");
m_desc["cursor_size"] = _L("Pen size");
m_desc["cursor_size"] = _L("Brush size");
m_desc["add_fuzzy_skin"] = _L("Add fuzzy skin");
m_desc["remove_fuzzy_skin"] = _L("Remove fuzzy skin");
m_desc["smart_fill_angle"] = _L("Smart fill angle");
@@ -95,8 +95,8 @@ bool GLGizmoMmuSegmentation::on_init()
m_desc["clipping_of_view"] = _L("Section view");
m_desc["reset_direction"] = _L("Reset direction");
m_desc["cursor_size"] = _L("Pen size");
m_desc["cursor_type"] = _L("Pen shape");
m_desc["cursor_size"] = _L("Brush size");
m_desc["cursor_type"] = _L("Brush shape");
m_desc["paint"] = _L("Paint");
m_desc["erase"] = _L("Erase");
m_desc["shortcut_key"] = _L("Choose filament");
+1 -1
View File
@@ -36,7 +36,7 @@ bool GLGizmoSeam::on_init()
m_desc["clipping_of_view"] = _L("Section view");
m_desc["reset_direction"] = _L("Reset direction");
m_desc["cursor_size"] = _L("Pen size");
m_desc["cursor_size"] = _L("Brush size");
m_desc["tool_type"] = _L("Tool type");
m_desc["enforce"] = _L("Enforce seam");
m_desc["block"] = _L("Block seam");
+2 -1
View File
@@ -115,7 +115,8 @@ namespace {
ConfigOptionMode config_mode = wxGetApp().get_mode();
if (config_mode == ConfigOptionMode::comSimple) return (tag == "simple" ? TagCheckAffirmative : TagCheckNegative);
else if (config_mode == ConfigOptionMode::comAdvanced) return (tag == "advanced" ? TagCheckAffirmative : TagCheckNegative);
//else if (config_mode == ConfigOptionMode::comDevelop) return (tag == "develop" ? TagCheckAffirmative : TagCheckNegative);
else if (config_mode == ConfigOptionMode::comExpert || config_mode == ConfigOptionMode::comDevelop)
return (tag == "expert" ? TagCheckAffirmative : TagCheckNegative);
}
return TagCheckNotCompatible;
}
+100 -2
View File
@@ -3,6 +3,7 @@
#include "GUI_App.hpp"
#include "slic3r/Utils/Http.hpp"
#include "slic3r/Utils/NetworkAgent.hpp"
#include "slic3r/Utils/BBLNetworkPlugin.hpp"
namespace Slic3r {
namespace GUI {
@@ -268,9 +269,106 @@ std::shared_ptr<HttpServer::Response> HttpServer::bbl_auth_handle_request(const
std::string location_str = (boost::format("%1%?result=fail&error=%2%") % redirect_url % error_str).str();
return std::make_shared<ResponseRedirect>(location_str);
}
} else {
return std::make_shared<ResponseNotFound>();
}
// Ticket-based redirect: Bambu Lab's auth server redirects here after a
// third-party (Google) OAuth so that the access token never travels through
// the URL. We exchange the ticket via the network plugin's get_my_token,
// then run the same get_my_profile + change_user flow as access_token.
// Skip entirely on legacy plugins missing bambu_network_get_my_token —
// those clients pin X-BBL-Client-Version so the server stays on the legacy
// ?access_token= redirect path and never sends ?ticket= here.
const std::string ticket = url_get_param(url, "ticket");
const std::string ticket_redirect_url = url_get_param(url, "redirect_url");
if (!ticket.empty() && !ticket_redirect_url.empty() &&
BBLNetworkPlugin::instance().get_get_my_token() != nullptr) {
BOOST_LOG_TRIVIAL(info) << "thirdparty_login: ticket flow";
NetworkAgent* agent = wxGetApp().getAgent();
if (!agent) {
std::string location_str = (boost::format("%1%?result=fail&error=no_agent") % ticket_redirect_url).str();
return std::make_shared<ResponseRedirect>(location_str);
}
auto fail_redirect = [&ticket_redirect_url](const std::string& reason) {
std::string location_str = (boost::format("%1%?result=fail&error=%2%") % ticket_redirect_url % reason).str();
return std::make_shared<ResponseRedirect>(location_str);
};
unsigned int token_http_code = 0;
std::string token_body;
int token_result = agent->get_my_token(ticket, &token_http_code, &token_body);
if (token_result != 0) {
BOOST_LOG_TRIVIAL(warning) << "thirdparty_login: get_my_token failed, http_code=" << token_http_code;
return fail_redirect("get_my_token_error_" + std::to_string(token_result));
}
std::string access_token;
std::string refresh_token;
std::string expires_in_str;
std::string refresh_expires_in_str;
try {
json token_j = json::parse(token_body);
if (token_j.contains("accessToken"))
access_token = token_j["accessToken"].get<std::string>();
if (token_j.contains("refreshToken"))
refresh_token = token_j["refreshToken"].get<std::string>();
if (token_j.contains("expiresIn"))
expires_in_str = std::to_string(token_j["expiresIn"].get<double>());
if (token_j.contains("refreshExpiresIn"))
refresh_expires_in_str = std::to_string(token_j["refreshExpiresIn"].get<double>());
} catch (...) {
return fail_redirect("token_parse_error");
}
if (access_token.empty()) {
return fail_redirect("token_missing");
}
unsigned int profile_http_code = 0;
std::string profile_body;
int profile_result = agent->get_my_profile(access_token, &profile_http_code, &profile_body);
if (profile_result != 0) {
BOOST_LOG_TRIVIAL(warning) << "thirdparty_login: get_my_profile failed, http_code=" << profile_http_code;
return fail_redirect("get_user_profile_error_" + std::to_string(profile_result));
}
std::string user_id;
std::string user_name;
std::string user_account;
std::string user_avatar;
try {
json user_j = json::parse(profile_body);
if (user_j.contains("uidStr"))
user_id = user_j["uidStr"].get<std::string>();
if (user_j.contains("name"))
user_name = user_j["name"].get<std::string>();
if (user_j.contains("avatar"))
user_avatar = user_j["avatar"].get<std::string>();
if (user_j.contains("account"))
user_account = user_j["account"].get<std::string>();
} catch (...) {
BOOST_LOG_TRIVIAL(warning) << "thirdparty_login: profile JSON parse failed";
}
json j;
j["data"]["refresh_token"] = refresh_token;
j["data"]["token"] = access_token;
j["data"]["expires_in"] = expires_in_str;
j["data"]["refresh_expires_in"] = refresh_expires_in_str;
j["data"]["user"]["uid"] = user_id;
j["data"]["user"]["name"] = user_name;
j["data"]["user"]["account"] = user_account;
j["data"]["user"]["avatar"] = user_avatar;
agent->change_user(j.dump());
if (agent->is_user_login()) {
wxGetApp().request_user_login(1);
}
GUI::wxGetApp().CallAfter([] { wxGetApp().ShowUserLogin(false); });
std::string location_str = (boost::format("%1%?result=success") % ticket_redirect_url).str();
return std::make_shared<ResponseRedirect>(location_str);
}
return std::make_shared<ResponseNotFound>();
}
void HttpServer::ResponseNotFound::write_response(std::stringstream& ssOut)
+1
View File
@@ -259,6 +259,7 @@ void KBShortcutsDialog::fill_shortcuts()
{ "E", L("Gizmo brim ears") },
{ "I", L("Zoom in") },
{ "O", L("Zoom out") },
{ "V", L("Toggle printable for object/part") },
{ L("Tab"), L("Switch between Prepare/Preview") },
};
-19
View File
@@ -87,25 +87,6 @@ void ButtonsListCtrl::OnPaint(wxPaintEvent&)
dc.SetBrush(clr);
dc.DrawRectangle(pos.x, pos.y + size.y, size.x, sz.y - size.y);
}
#if 0
// highlight selected mode button
if (m_mode_sizer) {
const std::vector<ModeButton*>& mode_btns = m_mode_sizer->get_btns();
for (int idx = 0; idx < int(mode_btns.size()); idx++) {
ModeButton* btn = mode_btns[idx];
btn->SetBackgroundColor(btn->is_selected() ? selected_btn_bg : default_btn_bg);
//wxPoint pos = btn->GetPosition();
//wxSize size = btn->GetSize();
//const wxColour& clr = btn->is_selected() ? btn_marker_color : default_btn_bg;
//dc.SetPen(clr);
//dc.SetBrush(clr);
//dc.DrawRectangle(pos.x, pos.y + size.y, size.x, sz.y - size.y);
}
}
#endif
// Draw orange bottom line
dc.SetPen(btn_marker_color);
-2
View File
@@ -6,7 +6,6 @@
#include <wx/bookctrl.h>
#include <wx/sizer.h>
class ModeSizer;
class ScalableButton;
class Button;
@@ -40,7 +39,6 @@ private:
int m_selection {-1};
int m_btn_margin;
int m_line_margin;
//ModeSizer* m_mode_sizer {nullptr};
std::vector<wxString> m_pageLabels; // ORCA
};
+172 -9
View File
@@ -1840,14 +1840,18 @@ void NotificationManager::push_validate_error_notification(StringObjectException
{
auto po = dynamic_cast<PrintObjectBase const *>(error.object);
auto mo = po ? po->model_object() : dynamic_cast<ModelObject const *>(error.object);
//ORCA: Update to handle ModelInstance selection for validation errors with fallback
/*
std::function<bool(wxEvtHandler*)> callback;
if (mo || !error.opt_key.empty()) {
callback =
[id = mo ? mo->id() : 0, opt = error.opt_key](wxEvtHandler*) {
auto& objects = wxGetApp().model().objects;
auto iter = id.id ? std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; }) : objects.end();
if (iter != objects.end())
if (iter != objects.end()) {
wxGetApp().obj_list()->select_items({ {*iter, nullptr} });
wxGetApp().obj_list()->update_selections_on_canvas();
}
if (!opt.empty()) {
if (iter != objects.end())
wxGetApp().params_panel()->switch_to_object();
@@ -1861,6 +1865,71 @@ void NotificationManager::push_validate_error_notification(StringObjectException
}
auto link = (mo || !error.opt_key.empty()) ? _u8L("Jump to") : "";
if (mo) link += std::string(" [") + mo->name + "]";
*/
auto mi = dynamic_cast<ModelInstance const *>(error.object);
std::function<bool(wxEvtHandler*)> callback;
if (mo || mi || !error.opt_key.empty()) {
callback =
[id = mo ? mo->id() : (mi ? mi->id() : 0),
parent_id = mi ? mi->get_object()->id() : 0,
is_inst = (mi != nullptr),
opt = error.opt_key](wxEvtHandler*) {
auto& objects = wxGetApp().model().objects;
if (is_inst) {
bool selected = false;
auto iter = std::find_if(objects.begin(), objects.end(), [parent_id](auto o) { return o->id() == parent_id; });
if (iter != objects.end()) {
ModelObject* obj = *iter;
int inst_idx = -1;
for(size_t i=0; i<obj->instances.size(); ++i) {
if (obj->instances[i]->id() == id) {
inst_idx = i;
break;
}
}
if (inst_idx != -1) {
auto* model = wxGetApp().obj_list()->GetModel();
wxDataViewItem item;
wxDataViewItem objItem = model->GetObjectItem(obj);
if (objItem.IsOk()) {
int vm_obj_idx = model->GetIdByItem(objItem);
if (vm_obj_idx != -1) {
item = model->GetItemByInstanceId(vm_obj_idx, inst_idx);
}
}
if (item.IsOk()) {
wxDataViewItemArray sel_items;
sel_items.Add(item);
wxGetApp().obj_list()->select_items(sel_items);
selected = true;
}
}
if (!selected) {
wxGetApp().obj_list()->select_items({ {obj, nullptr} });
}
}
} else {
auto iter = id.id ? std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; }) : objects.end();
if (iter != objects.end())
wxGetApp().obj_list()->select_items({ {*iter, nullptr} });
}
if (!opt.empty()) {
if ((!is_inst && id.id) || (is_inst && parent_id.id)) // if object found
wxGetApp().params_panel()->switch_to_object();
wxGetApp().sidebar().jump_to_option(opt, Preset::TYPE_PRINT, L"");
}
else {
wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor);
}
return false;
};
}
auto link = (mo || mi || !error.opt_key.empty()) ? _u8L("Jump to") : "";
if (mo) link += std::string(" [") + mo->name + "]";
if (mi) link += std::string(" [") + mi->get_object()->name + "]";
if (!error.opt_key.empty()) link += std::string(" (") + error.opt_key + ")";
push_notification_data({NotificationType::ValidateError, NotificationLevel::ErrorNotificationLevel, 0, _u8L("Error:") + "\n" + error.string, link, callback}, 0);
set_slicing_progress_hidden();
@@ -2290,7 +2359,7 @@ void NotificationManager::upload_job_notification_show_error(int id, const std::
}
}
void NotificationManager::push_slicing_serious_warning_notification(const std::string &text, std::vector<ModelObject const *> objs)
void NotificationManager::push_slicing_serious_warning_notification(const std::string &text, std::vector<ModelObject const *> objs)
{
std::vector<ObjectID> ids;
for (auto optr : objs) {
@@ -2308,19 +2377,113 @@ void NotificationManager::push_slicing_serious_warning_notification(const std::s
if (!ovs.empty()) {
wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor);
wxGetApp().obj_list()->select_items(ovs);
wxGetApp().obj_list()->update_selections_on_canvas();
}
return false;
};
}
auto link = callback ? _u8L("Jump to") : "";
if (!objs.empty()) {
link += " [";
for (auto obj : objs) {
if (obj) link += obj->name + ", ";
std::vector<std::string> names;
for (auto optr : objs) {
if (optr) {
names.push_back(optr->name);
}
if (!objs.empty()) {
link.pop_back();
link.pop_back();
}
if (!names.empty()) {
link += " [";
for (size_t i = 0; i < names.size(); ++i) {
if (i > 0) link += ", ";
link += names[i];
}
link += "] ";
}
set_all_slicing_warnings_gray(false);
push_notification_data({NotificationType::SlicingSeriousWarning, NotificationLevel::SeriousWarningNotificationLevel, 0, _u8L("Serious warning:") + "\n" + text, link,
callback},
0);
set_slicing_progress_hidden();
}
void NotificationManager::push_slicing_serious_warning_notification(const std::string &text, std::vector<ModelInstance const *> insts)
{
std::vector<std::pair<ObjectID, ObjectID>> ids;
for (auto iptr : insts) {
if (iptr && iptr->get_object()) {
ids.push_back({iptr->get_object()->id(), iptr->id()});
}
}
std::function<bool(wxEvtHandler*)> callback;
if (!insts.empty()) {
callback = [ids](wxEvtHandler*) {
auto& objects = wxGetApp().model().objects;
wxDataViewItemArray sel_items;
std::vector<ObjectVolumeID> fallback_ovs;
auto* obj_list = wxGetApp().obj_list();
if (!obj_list) return false;
auto* model = obj_list->GetModel();
if (!model) return false;
for (const auto& pair : ids) {
ObjectID obj_id = pair.first;
ObjectID inst_id = pair.second;
auto iter = std::find_if(objects.begin(), objects.end(), [obj_id](auto o) { return o->id() == obj_id; });
if (iter != objects.end()) {
ModelObject* obj = *iter;
int inst_idx = -1;
for (int i=0; i<obj->instances.size(); ++i) {
if (obj->instances[i]->id() == inst_id) {
inst_idx = i;
break;
}
}
if (inst_idx != -1) {
wxDataViewItem item;
wxDataViewItem objItem = model->GetObjectItem(obj);
if (objItem.IsOk()) {
int vm_obj_idx = model->GetIdByItem(objItem);
if (vm_obj_idx != -1) {
item = model->GetItemByInstanceId(vm_obj_idx, inst_idx);
}
}
if (item.IsOk()) {
sel_items.Add(item);
} else {
fallback_ovs.push_back({obj, nullptr});
}
} else {
fallback_ovs.push_back({obj, nullptr});
}
}
}
wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor);
if (!sel_items.empty()) {
obj_list->select_items(sel_items);
obj_list->update_selections_on_canvas();
} else if (!fallback_ovs.empty()) {
obj_list->select_items(fallback_ovs);
obj_list->update_selections_on_canvas();
}
return false;
};
}
auto link = callback ? _u8L("Jump to") : "";
std::vector<std::string> names;
for (auto iptr : insts) {
if (iptr && iptr->get_object()) {
names.push_back(iptr->get_object()->name);
}
}
if (!names.empty()) {
link += " [";
for (size_t i = 0; i < names.size(); ++i) {
if (i > 0) link += ", ";
link += names[i];
}
link += "] ";
}
+1
View File
@@ -218,6 +218,7 @@ public:
void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host);
void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host);
void push_slicing_serious_warning_notification(const std::string &text, std::vector<ModelObject const *> objs);
void push_slicing_serious_warning_notification(const std::string &text, std::vector<ModelInstance const *> insts);
void close_slicing_serious_warning_notification(const std::string &text);
// Creates Slicing Error notification with a custom text and no fade out.
void push_slicing_error_notification(const std::string &text, std::vector<ModelObject const *> objs);
+37 -54
View File
@@ -22,6 +22,16 @@
namespace Slic3r {
namespace GUI {
namespace
{
int mode_to_selection(ConfigOptionMode mode)
{
return mode == comExpert ? 2 :
mode == comAdvanced ? 1 :
0;
}
}
TipsDialog::TipsDialog(wxWindow *parent, const wxString &title, const wxString &description, std::string app_key, long style,std::map<wxStandardID,wxString> option_map)
: DPIDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX),
@@ -261,15 +271,17 @@ ParamsPanel::ParamsPanel( wxWindow* parent, wxWindowID id, const wxPoint& pos, c
m_mode_icon = new ScalableButton(m_top_panel, wxID_ANY, "advanced"); // ORCA
m_mode_icon->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {
if(wxGetApp().get_mode() == comDevelop) return; // prevent change on dev mode
m_mode_view->SetValue(!m_mode_view->GetValue());
wxCommandEvent evt(wxEVT_TOGGLEBUTTON, m_mode_view->GetId()); // ParamsPanel::OnToggled(evt)
evt.SetEventObject(m_mode_view);
m_mode_view->wxEvtHandler::ProcessEvent(evt);
if (wxGetApp().get_mode() == comDevelop || m_mode_view == nullptr)
return; // prevent change on dev mode
const int selection = m_mode_view->GetSelection();
m_mode_view->SelectAndNotify((selection + 1) % 3);
});
m_mode_icon->SetToolTip(_L("Show/Hide advanced parameters"));
m_mode_view = new SwitchButton(m_top_panel, wxID_ABOUT);
m_mode_view->SetToolTip(_L("Show/Hide advanced parameters"));
m_mode_icon->SetToolTip(_L("Cycle settings visibility"));
m_mode_view = new ModeSwitchButton(m_top_panel);
m_mode_view->SetSelection(mode_to_selection(wxGetApp().get_saved_mode()));
if (wxGetApp().get_mode() == comDevelop)
m_mode_view->Enable(false);
// BBS: new layout
//m_search_btn = new ScalableButton(m_top_panel, wxID_ANY, "search", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true);
@@ -382,9 +394,6 @@ ParamsPanel::ParamsPanel( wxWindow* parent, wxWindowID id, const wxPoint& pos, c
if (m_mode_region)
m_mode_region->Bind(wxEVT_TOGGLEBUTTON, &ParamsPanel::OnToggled, this);
if (m_mode_view)
m_mode_view->Bind(wxEVT_TOGGLEBUTTON, &ParamsPanel::OnToggled, this);
Bind(wxEVT_TOGGLEBUTTON, &ParamsPanel::OnToggled, this); // For Tab's mode switch
//Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { wxGetApp().plater()->search(false); }, wxID_FIND);
//m_export_to_file->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { wxGetApp().mainframe->export_config(); });
//m_import_from_file->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { wxGetApp().mainframe->load_config_file(); });
@@ -560,28 +569,6 @@ void ParamsPanel::OnToggled(wxCommandEvent& event)
return;
}
if (wxID_ABOUT != event.GetId()) {
return;
}
// this is from tab's mode switch
bool value = dynamic_cast<SwitchButton*>(event.GetEventObject())->GetValue();
int mode_id;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": Advanced mode toogle to %1%") % value;
if (value)
{
//m_mode_region->SetBitmap(m_toggle_on_icon);
mode_id = comAdvanced;
}
else
{
//m_mode_region->SetBitmap(m_toggle_off_icon);
mode_id = comSimple;
}
Slic3r::GUI::wxGetApp().save_mode(mode_id);
event.Skip();
}
@@ -657,27 +644,22 @@ bool ParamsPanel::is_active_and_shown_tab(wxPanel* tab)
void ParamsPanel::update_mode()
{
int app_mode = Slic3r::GUI::wxGetApp().get_mode();
SwitchButton * mode_view = m_current_tab ? dynamic_cast<Tab*>(m_current_tab)->m_mode_view : nullptr;
if (mode_view == nullptr) mode_view = m_mode_view;
if (mode_view == nullptr) return;
auto sync_mode_view = [&](ModeSwitchButton* mode_view) {
if (mode_view == nullptr)
return;
//BBS: disable the mode tab and return directly when enable develop mode
if (app_mode == comDevelop)
{
mode_view->Disable();
return;
}
if (!mode_view->IsEnabled())
mode_view->Enable();
mode_view->SetSelection(mode_to_selection(Slic3r::GUI::wxGetApp().get_saved_mode()));
if (app_mode == comDevelop) {
mode_view->Enable(false);
return;
}
if (app_mode == comAdvanced)
{
mode_view->SetValue(true);
}
else
{
mode_view->SetValue(false);
}
if (!mode_view->IsEnabled())
mode_view->Enable();
};
sync_mode_view(m_mode_view);
sync_mode_view(m_current_tab ? dynamic_cast<Tab*>(m_current_tab)->m_mode_view : nullptr);
}
void ParamsPanel::msw_rescale()
@@ -694,7 +676,9 @@ void ParamsPanel::msw_rescale()
((SwitchButton* )m_mode_region)->Rescale();
if (m_mode_icon) m_mode_icon->msw_rescale();
if (m_mode_view)
((SwitchButton* )m_mode_view)->Rescale();
{
m_mode_view->Rescale();
}
for (auto tab : {m_tab_print, m_tab_print_plate, m_tab_print_object, m_tab_print_part, m_tab_print_layer, m_tab_filament, m_tab_printer}) {
if (tab) dynamic_cast<Tab*>(tab)->msw_rescale();
}
@@ -790,7 +774,6 @@ void ParamsPanel::delete_subwindows()
if (m_mode_view)
{
delete m_mode_view;
m_mode_view = nullptr;
}
+2 -1
View File
@@ -31,6 +31,7 @@
#include "GUI_Utils.hpp"
#include "Widgets/Button.hpp"
class ModeSwitchButton;
class SwitchButton;
class StaticBox;
@@ -85,7 +86,7 @@ class ParamsPanel : public wxPanel
ScalableButton *m_tips_arrow{nullptr};
bool m_tips_arror_blink{false};
ScalableButton* m_mode_icon { nullptr }; // ORCA
SwitchButton* m_mode_view { nullptr };
ModeSwitchButton* m_mode_view { nullptr };
//wxBitmapButton* m_search_button { nullptr };
wxStaticLine* m_staticline_print { nullptr };
//wxBoxSizer* m_print_sizer { nullptr };
+149 -43
View File
@@ -5110,7 +5110,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
this->q->set_prepare_state(Job::PREPARE_STATE_MENU);
this->q->orient(); });
//BBS
view3D_canvas->Bind(EVT_GLCANVAS_SELECT_CURR_PLATE_ALL, [this](SimpleEvent&) {this->q->select_curr_plate_all(); });
view3D_canvas->Bind(EVT_GLCANVAS_SELECT_CURR_PLATE_ALL, [this](SimpleEvent&) {this->q->select_curr_plate_all(); });
view3D_canvas->Bind(EVT_GLCANVAS_PRINTABLE, [this](SimpleEvent& evt) { this->sidebar->obj_list()->toggle_printable_state(); });
view3D_canvas->Bind(EVT_GLCANVAS_SELECT_ALL, [this](SimpleEvent&) { this->q->select_all(); });
view3D_canvas->Bind(EVT_GLCANVAS_QUESTION_MARK, [](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); });
@@ -5995,11 +5996,24 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
// BBS: version check
Semver app_version = *(Semver::parse(SoftFever_VERSION));
const wxString load_3mf_title = _L("Load 3MF");
const wxString newer_3mf_title = _L("Newer 3MF version");
const wxString bambu_project_title = _L("BambuStudio Project");
const wxString msg_unsupported_geometry = _L("The 3MF is not supported by OrcaSlicer, loading geometry data only.");
const wxString msg_old_orca_geometry = _L("The 3MF file was generated by an old OrcaSlicer version, loading geometry data only.");
const wxString msg_older_geometry = _L("The 3MF file was generated by an older version, loading geometry data only.");
const wxString msg_bambu_geometry = _L("The 3MF file was generated by BambuStudio, loading geometry data only.");
auto log_and_show_3mf_info = [&](const wxString& text, const wxString& title) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__
<< " "
<< boost::format("3MF import message [%1%]: %2% | file: %3%") % into_u8(title) % into_u8(text) % path.string();
show_info(q, text, title);
};
if (en_3mf_file_type == En3mfType::From_Prusa) {
// do not reset the model config
load_config = false;
if(load_type != LoadType::LoadGeometry)
show_info(q, _L("The 3MF is not supported by OrcaSlicer, loading geometry data only."), _L("Load 3MF"));
if (load_type != LoadType::LoadGeometry)
log_and_show_3mf_info(msg_unsupported_geometry, load_3mf_title);
}
else if (en_3mf_file_type == En3mfType::From_Orca) {
// OrcaSlicer file (has OrcaSlicer tag) - compare file_version with SoftFever_VERSION
@@ -6035,7 +6049,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
wxString append = _L("You'd better upgrade your software.\n");
context += "\n\n";
context += append;
show_info(q, context, _L("Newer 3MF version"));
log_and_show_3mf_info(context, newer_3mf_title);
}
else {
//if the minor version is not matched
@@ -6043,13 +6057,13 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
wxString text = wxString::Format(_L("The 3MF file version %s is newer than %s's version %s, we suggest to upgrade your software."),
file_version.to_string_sf(), std::string(SLIC3R_APP_FULL_NAME), app_version.to_string_sf());
text += "\n";
show_info(q, text, _L("Newer 3MF version"));
log_and_show_3mf_info(text, newer_3mf_title);
}
}
}
else if (load_config && config_loaded.empty()) {
load_config = false;
show_info(q, _L("The 3MF file was generated by an old OrcaSlicer version, loading geometry data only."), _L("Load 3MF"));
log_and_show_3mf_info(msg_old_orca_geometry, load_3mf_title);
}
}
else if (en_3mf_file_type == En3mfType::From_BBS) {
@@ -6084,7 +6098,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
else if (load_config && config_loaded.empty()) {
load_config = false;
show_info(q, _L("The 3MF file was generated by an older version, loading geometry data only."), _L("Load 3MF"));
log_and_show_3mf_info(msg_older_geometry, load_3mf_title);
}
} else {
// BambuStudio project (version > 2.3.2 without OrcaSlicer tag)
@@ -6092,7 +6106,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
Semver slic3r_version = *(Semver::parse(SLIC3R_VERSION));
if (load_config && config_loaded.empty()) {
load_config = false;
show_info(q, _L("The 3MF file was generated by BambuStudio, loading geometry data only."), _L("Load 3MF"));
log_and_show_3mf_info(msg_bambu_geometry, load_3mf_title);
}
else if (load_config && (file_version > slic3r_version)) {
// BambuStudio file version is newer than our compatible SLIC3R_VERSION
@@ -6104,23 +6118,32 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
wxString append = _L("You'd better upgrade your software.\n");
context += "\n\n";
context += append;
show_info(q, context, _L("BambuStudio Project"));
log_and_show_3mf_info(context, bambu_project_title);
} else {
wxString text = wxString::Format(_L("The 3MF was created by BambuStudio (version %s), which is newer than the compatible version %s. Some settings may not be fully compatible."),
file_version.to_string(), slic3r_version.to_string());
text += "\n";
show_info(q, text, _L("BambuStudio Project"));
log_and_show_3mf_info(text, bambu_project_title);
}
} else if (load_config) {
// BambuStudio version is older or same as our SLIC3R_VERSION
wxString text = _L("The 3MF was created by BambuStudio. Some settings may differ from OrcaSlicer.");
show_info(q, text, _L("BambuStudio Project"));
log_and_show_3mf_info(text, bambu_project_title);
}
}
}
else if (en_3mf_file_type == En3mfType::From_Other) {
// Generic CAD/other 3MF without slicer metadata: import geometry silently.
if (load_config && config_loaded.empty()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__
<< " "
<< boost::format("3MF has no slicer metadata/project config, importing geometry only: %1%") % path.string();
load_config = false;
}
}
else if (load_config && config_loaded.empty()) {
load_config = false;
show_info(q, _L("The 3MF file was generated by an old OrcaSlicer version, loading geometry data only."), _L("Load 3MF"));
log_and_show_3mf_info(msg_old_orca_geometry, load_3mf_title);
}
else if (!load_config) {
// reset config except color
@@ -6210,9 +6233,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
config += std::move(config_loaded);
std::map<std::string, std::string> validity = config.validate();
if (!validity.empty()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("Param values in 3mf error: ");
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << " " << boost::format("Param values in 3mf error: ");
for (std::map<std::string, std::string>::iterator it=validity.begin(); it!=validity.end(); ++it)
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("%1%: %2%")%it->first %it->second;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << " " << boost::format("%1%: %2%")%it->first %it->second;
//
NotificationManager *notify_manager = q->get_notification_manager();
std::string error_message = L("Invalid values found in the 3MF:");
@@ -6694,7 +6717,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
model_object->center_around_origin(false);
// BBS
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_LOAD_MODEL_OBJECTS \n");
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << " " << boost::format("import 3mf IMPORT_LOAD_MODEL_OBJECTS \n");
wxString msg = wxString::Format("Loading file: %s", from_path(real_filename));
model_idx++;
dlg_cont = dlg.Update(progress_percent, msg);
@@ -7777,22 +7800,72 @@ void Plater::priv::process_validation_warning(StringObjectException const &warni
std::string text = warning.string;
auto po = dynamic_cast<PrintObjectBase const *>(warning.object);
auto mo = po ? po->model_object() : dynamic_cast<ModelObject const *>(warning.object);
auto action_fn = (mo || !warning.opt_key.empty()) ? [id = mo ? mo->id() : 0, opt = warning.opt_key](wxEvtHandler *) {
//ORCA: Update process_validation_warning to handle ModelInstance selection and include fallback
auto mi = dynamic_cast<ModelInstance const *>(warning.object);
auto action_fn = (mo || mi || !warning.opt_key.empty()) ? [id = mo ? mo->id() : (mi ? mi->id() : 0),
parent_id = mi ? mi->get_object()->id() : 0,
is_inst = (mi != nullptr),
opt = warning.opt_key](wxEvtHandler *) {
auto & objects = wxGetApp().model().objects;
auto iter = id.id ? std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; }) : objects.end();
if (iter != objects.end()) {
wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor);
wxGetApp().obj_list()->select_items({{*iter, nullptr}});
if (is_inst) {
bool selected = false;
auto iter = std::find_if(objects.begin(), objects.end(), [parent_id](auto o) { return o->id() == parent_id; });
if (iter != objects.end()) {
ModelObject* obj = *iter;
int inst_idx = -1;
for(size_t i=0; i<obj->instances.size(); ++i) {
if (obj->instances[i]->id() == id) {
inst_idx = i;
break;
}
}
wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor);
if (inst_idx != -1) {
auto* model = wxGetApp().obj_list()->GetModel();
wxDataViewItem item;
wxDataViewItem objItem = model->GetObjectItem(obj);
if (objItem.IsOk()) {
int vm_obj_idx = model->GetIdByItem(objItem);
if (vm_obj_idx != -1) {
item = model->GetItemByInstanceId(vm_obj_idx, inst_idx);
}
}
if (item.IsOk()) {
wxDataViewItemArray sel_items;
sel_items.Add(item);
wxGetApp().obj_list()->select_items(sel_items);
wxGetApp().obj_list()->update_selections_on_canvas();
selected = true;
}
}
if (!selected) {
wxGetApp().obj_list()->select_items({ {obj, nullptr} });
wxGetApp().obj_list()->update_selections_on_canvas();
}
}
} else {
auto iter = id.id ? std::find_if(objects.begin(), objects.end(), [id](auto o) { return o->id() == id; }) : objects.end();
if (iter != objects.end()) {
wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor);
wxGetApp().obj_list()->select_items({{*iter, nullptr}});
wxGetApp().obj_list()->update_selections_on_canvas();
}
}
if (!opt.empty()) {
if (iter != objects.end())
if ((!is_inst && id.id) || (is_inst && parent_id.id))
wxGetApp().params_panel()->switch_to_object();
wxGetApp().sidebar().jump_to_option(opt, Preset::TYPE_PRINT, L"");
}
return false;
} : std::function<bool(wxEvtHandler *)>();
auto hypertext = (mo || !warning.opt_key.empty()) ? _u8L("Jump to") : "";
auto hypertext = (mo || mi || !warning.opt_key.empty()) ? _u8L("Jump to") : "";
if (mo) hypertext += std::string(" [") + mo->name + "]";
if (mi) hypertext += std::string(" [") + mi->get_object()->name + "]";
if (!warning.opt_key.empty()) hypertext += std::string(" (") + warning.opt_key + ")";
// BBS disable support enforcer
@@ -12633,6 +12706,7 @@ void Plater::calib_pa(const Calib_Params& params)
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
print_config->set_key_value("overhang_reverse", new ConfigOptionBool(false));
print_config->set_key_value("precise_z_height", new ConfigOptionBool(false));
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
switch (params.mode) {
case CalibMode::Calib_PA_Line:
@@ -13141,6 +13215,10 @@ void Plater::calib_flowrate(bool is_linear, int pass, InfillPattern pattern) {
void Plater::calib_temp(const Calib_Params& params) {
constexpr double base_temp_tower_nozzle_diameter = 0.4;
constexpr double base_temp_tower_block_height = 10.0;
constexpr int base_temp_tower_temp_step = 5;
const auto calib_temp_name = wxString::Format(L"Nozzle temperature test");
new_project(false, false, calib_temp_name);
wxGetApp().mainframe->select_tab(size_t(MainFrame::tp3DEditor));
@@ -13151,18 +13229,61 @@ void Plater::calib_temp(const Calib_Params& params) {
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
auto start_temp = lround(params.start);
const ConfigOptionFloats* nozzle_diameter_config = printer_config->option<ConfigOptionFloats>("nozzle_diameter");
size_t nozzle_id = static_cast<size_t>(std::max(params.extruder_id, 0));
double nozzle_diameter = base_temp_tower_nozzle_diameter;
if (nozzle_diameter_config && !nozzle_diameter_config->values.empty()) {
nozzle_id = std::min(nozzle_id, nozzle_diameter_config->values.size() - 1);
nozzle_diameter = nozzle_diameter_config->values[nozzle_id];
}
if (nozzle_diameter <= 0.0)
nozzle_diameter = base_temp_tower_nozzle_diameter;
const double nozzle_scale = nozzle_diameter / base_temp_tower_nozzle_diameter;
const double block_height = base_temp_tower_block_height;
// cut upper
auto obj_bb = model().objects[0]->bounding_box_exact();
auto block_count = lround((500 - params.end) / base_temp_tower_temp_step + 1);
if (block_count > 0) {
// subtract EPSILON offset to avoid cutting at the exact location where the flat surface is
auto new_height = block_count * block_height - EPSILON;
if (new_height < obj_bb.size().z()) {
cut_horizontal(0, 0, new_height, ModelObjectCutAttribute::KeepLower);
}
}
// cut bottom
obj_bb = model().objects[0]->bounding_box_exact();
block_count = lround((500 - params.start) / base_temp_tower_temp_step);
if (block_count > 0) {
auto new_height = block_count * block_height + EPSILON;
if (new_height < obj_bb.size().z()) {
cut_horizontal(0, 0, new_height, ModelObjectCutAttribute::KeepUpper);
}
}
if (std::abs(nozzle_scale - 1.0) > EPSILON)
model().objects[0]->scale(nozzle_scale, nozzle_scale, nozzle_scale);
model().objects[0]->ensure_on_bed();
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
filament_config->set_key_value("nozzle_temperature_initial_layer", new ConfigOptionInts(1,(int)start_temp));
filament_config->set_key_value("nozzle_temperature", new ConfigOptionInts(1,(int)start_temp));
model().objects[0]->config.set_key_value("layer_height", new ConfigOptionFloat(nozzle_diameter/2));
model().objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum<BrimType>(btOuterOnly));
model().objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(5.0));
model().objects[0]->config.set_key_value("brim_object_gap", new ConfigOptionFloat(0.0));
model().objects[0]->config.set_key_value("alternate_extra_wall", new ConfigOptionBool(false));
model().objects[0]->config.set_key_value("seam_slope_type", new ConfigOptionEnum<SeamScarfType>(SeamScarfType::None));
model().objects[0]->config.set_key_value("overhang_reverse", new ConfigOptionBool(false));
model().objects[0]->config.set_key_value("precise_z_height", new ConfigOptionBool(false));
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
print_config->set_key_value("enable_wrapping_detection", new ConfigOptionBool(false));
print_config->set_key_value("initial_layer_print_height", new ConfigOptionFloat(nozzle_diameter/2));
changed_objects({ 0 });
wxGetApp().get_tab(Preset::TYPE_PRINT)->update_dirty();
@@ -13170,27 +13291,6 @@ void Plater::calib_temp(const Calib_Params& params) {
wxGetApp().get_tab(Preset::TYPE_PRINT)->reload_config();
wxGetApp().get_tab(Preset::TYPE_FILAMENT)->reload_config();
// cut upper
auto obj_bb = model().objects[0]->bounding_box_exact();
auto block_count = lround((500 - params.end) / 5 + 1);
if(block_count > 0){
// subtract EPSILON offset to avoid cutting at the exact location where the flat surface is
auto new_height = block_count * 10.0 - EPSILON;
if (new_height < obj_bb.size().z()) {
cut_horizontal(0, 0, new_height, ModelObjectCutAttribute::KeepLower);
}
}
// cut bottom
obj_bb = model().objects[0]->bounding_box_exact();
block_count = lround((500 - params.start) / 5);
if(block_count > 0){
auto new_height = block_count * 10.0 + EPSILON;
if (new_height < obj_bb.size().z()) {
cut_horizontal(0, 0, new_height, ModelObjectCutAttribute::KeepUpper);
}
}
p->background_process.fff_print()->set_calib_params(params);
}
@@ -13239,6 +13339,7 @@ void Plater::calib_max_vol_speed(const Calib_Params& params)
obj_cfg.set_key_value("brim_type", new ConfigOptionEnum<BrimType>(btOuterAndInner));
obj_cfg.set_key_value("brim_width", new ConfigOptionFloat(5.0));
obj_cfg.set_key_value("brim_object_gap", new ConfigOptionFloat(0.0));
obj_cfg.set_key_value("precise_z_height", new ConfigOptionBool(false));
print_config->set_key_value("timelapse_type", new ConfigOptionEnum<TimelapseType>(tlTraditional));
print_config->set_key_value("spiral_mode", new ConfigOptionBool(true));
print_config->set_key_value("max_volumetric_extrusion_rate_slope", new ConfigOptionFloat(0));
@@ -13311,6 +13412,7 @@ void Plater::calib_retraction(const Calib_Params& params)
obj->config.set_key_value("seam_position", new ConfigOptionEnum<SeamPosition>(spAligned));
obj->config.set_key_value("wall_sequence", new ConfigOptionEnum<WallSequence>(WallSequence::InnerOuter));
obj->config.set_key_value("overhang_reverse", new ConfigOptionBool(false));
obj->config.set_key_value("precise_z_height", new ConfigOptionBool(false));
changed_objects({ 0 });
@@ -13349,6 +13451,7 @@ void Plater::calib_VFA(const Calib_Params& params)
print_config->set_key_value("detect_thin_wall", new ConfigOptionBool(false));
print_config->set_key_value("spiral_mode", new ConfigOptionBool(true));
print_config->set_key_value("enable_wrapping_detection", new ConfigOptionBool(false));
print_config->set_key_value("precise_z_height", new ConfigOptionBool(false));
model().objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum<BrimType>(btOuterOnly));
model().objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0));
model().objects[0]->config.set_key_value("brim_object_gap", new ConfigOptionFloat(0.0));
@@ -13416,6 +13519,7 @@ void Plater::calib_input_shaping_freq(const Calib_Params& params)
print_config->set_key_value("outer_wall_speed", new ConfigOptionFloat(200));
print_config->set_key_value("default_acceleration", new ConfigOptionFloat(20000));
print_config->set_key_value("outer_wall_acceleration", new ConfigOptionFloat(20000));
print_config->set_key_value("precise_z_height", new ConfigOptionBool(false));
model().objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum<BrimType>(btOuterOnly));
model().objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0));
model().objects[0]->config.set_key_value("brim_object_gap", new ConfigOptionFloat(0.0));
@@ -13476,6 +13580,7 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params)
print_config->set_key_value("outer_wall_speed", new ConfigOptionFloat(200));
print_config->set_key_value("default_acceleration", new ConfigOptionFloat(20000));
print_config->set_key_value("outer_wall_acceleration", new ConfigOptionFloat(20000));
print_config->set_key_value("precise_z_height", new ConfigOptionBool(false));
model().objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum<BrimType>(btOuterOnly));
model().objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0));
model().objects[0]->config.set_key_value("brim_object_gap", new ConfigOptionFloat(0.0));
@@ -13538,6 +13643,7 @@ void Plater::Calib_Cornering(const Calib_Params& params)
print_config->set_key_value("outer_wall_speed", new ConfigOptionFloat(200));
print_config->set_key_value("default_acceleration", new ConfigOptionFloat(2000));
print_config->set_key_value("outer_wall_acceleration", new ConfigOptionFloat(2000));
print_config->set_key_value("precise_z_height", new ConfigOptionBool(false));
model().objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum<BrimType>(btOuterOnly));
model().objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0));
model().objects[0]->config.set_key_value("brim_object_gap", new ConfigOptionFloat(0.0));
+1 -5
View File
@@ -1004,11 +1004,7 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too
if (param == "developer_mode") {
m_developer_mode_def = app_config->get("developer_mode");
if (m_developer_mode_def == "true") {
Slic3r::GUI::wxGetApp().save_mode(comDevelop);
} else {
Slic3r::GUI::wxGetApp().save_mode(comAdvanced);
}
Slic3r::GUI::wxGetApp().update_mode();
}
// webview dump_vedio
+81 -25
View File
@@ -299,34 +299,86 @@ gst_bambusrc_create (GstPushSrc * psrc, GstBuffer ** outbuf)
#endif
*outbuf = gst_buffer_new_wrapped_full(0, sbuf, sample.size, 0, sample.size, sbuf, g_free);
/* The NAL data already contains a timestamp (I think?), but we seem to
* need to feed this in too -- otherwise the GStreamer pipeline gets upset
* and starts triggering QoS events.
/* Synthesize monotonic timestamps at the announced frame rate, anchored
* to the first frame's arrival time. The X1C's RTSPS server emits
* unreliable decode timestamps (wildly non-monotonic jumps, or sometimes
* none at all); forwarding them directly froze the pipeline after a few
* seconds. Pacing on a synthesized clock — the same trick mpv uses when
* it reports "No video PTS! Making something up." — gives smooth
* playback regardless of network jitter, and only drops late frames if
* the printer can't keep up. A snap-back resets the anchor if real
* arrival drifts more than two frame periods from the synthesized
* timeline (e.g. announced framerate was wrong).
*/
if (src->video_type == AVC1) {
if (!src->sttime) {
src->sttime = sample.decode_time * 100ULL;
}
GST_BUFFER_DTS(*outbuf) = sample.decode_time * 100ULL - src->sttime;
GST_BUFFER_PTS(*outbuf) = GST_CLOCK_TIME_NONE;
GST_BUFFER_DURATION(*outbuf) = GST_CLOCK_TIME_NONE;
GstClock *clock = GST_ELEMENT_CLOCK(psrc);
GstClockTime base_time = gst_element_get_base_time((GstElement *)psrc);
GstClockTime running_now = GST_CLOCK_TIME_NONE;
if (clock) {
GstClockTime now = gst_clock_get_time(clock);
if (now != GST_CLOCK_TIME_NONE && now >= base_time)
running_now = now - base_time;
}
else {
if (!src->sttime) {
//only available from 1.18
//src->sttime = gst_element_get_current_clock_time((GstElement *)psrc);
src->sttime = gst_clock_get_time(((GstElement *)psrc)->clock);
//if (GST_CLOCK_TIME_NONE == src->sttime)
// src->sttime
GST_DEBUG_OBJECT(src,
"sttime init to %lu.",
src->sttime);
}
//GST_BUFFER_DTS(*outbuf) = gst_element_get_current_clock_time((GstElement *)psrc) - src->sttime;
GST_BUFFER_DTS(*outbuf) = gst_clock_get_time(((GstElement *)psrc)->clock) - src->sttime;
GST_BUFFER_PTS(*outbuf) = GST_CLOCK_TIME_NONE;
GST_BUFFER_DURATION(*outbuf) = GST_CLOCK_TIME_NONE;
/* Adapt the period to actual inter-arrival time via EWMA. The announced
* frame_rate is unreliable on Bambu printers (X1C announces 30 but
* delivers ~28), so trusting it causes the synthesized timeline to drift
* relative to real time, which makes the sink consider frames late and
* skip pacing entirely. Measuring the real rate keeps PTS in step with
* arrival on average, so the sink can pace inside bursts while still
* tracking the printer's actual frame cadence.
*/
if (src->avg_period == 0) {
int fps = src->frame_rate > 0 ? src->frame_rate : 30;
src->avg_period = GST_SECOND / fps;
}
if (running_now != GST_CLOCK_TIME_NONE && src->last_arrival != 0) {
GstClockTimeDiff delta = GST_CLOCK_DIFF(src->last_arrival, running_now);
/* clamp to plausible video frame periods (5..200 ms) so a one-off
* burst-of-zero or long stall doesn't poison the average */
if (delta > 5 * GST_MSECOND && delta < 200 * GST_MSECOND) {
src->avg_period = (src->avg_period * 15 + (GstClockTime)delta) / 16;
}
}
src->last_arrival = (running_now != GST_CLOCK_TIME_NONE) ? running_now : src->last_arrival;
GstClockTime period = src->avg_period;
/* Lead time: schedule frames a few periods in the future of their
* arrival, so the sink has a small jitter buffer. Without this, frames
* arriving slightly later than expected land behind the running clock
* and the sink renders them immediately, producing visible stutter.
* 100ms is invisible for a live print-monitor view.
*/
const GstClockTime LEAD = 100 * GST_MSECOND;
if (!src->sttime) {
src->sttime = (running_now != GST_CLOCK_TIME_NONE) ? running_now + LEAD : LEAD;
src->frame_count = 0;
}
GstClockTime pts = src->sttime + src->frame_count * period;
/* Safety net: with the lead applied, expected drift is roughly -LEAD
* (pts sits LEAD ns ahead of running_now). Re-anchor only if the
* synthesized timeline diverges from that expectation by several frame
* periods, which indicates a real disturbance (printer paused, stream
* resumed, large fps change) rather than ordinary jitter.
*/
if (running_now != GST_CLOCK_TIME_NONE) {
GstClockTimeDiff drift = GST_CLOCK_DIFF(pts, running_now);
GstClockTimeDiff expected = -(GstClockTimeDiff)LEAD;
GstClockTimeDiff slack = (GstClockTimeDiff)(4 * period);
if (drift > expected + slack || drift < expected - slack) {
GST_DEBUG_OBJECT(src, "ts drift %" G_GINT64_FORMAT " ns; re-anchoring", drift);
src->sttime = running_now + LEAD;
src->frame_count = 0;
pts = src->sttime;
}
}
GST_BUFFER_PTS(*outbuf) = pts;
GST_BUFFER_DTS(*outbuf) = pts;
GST_BUFFER_DURATION(*outbuf) = period;
src->frame_count++;
GST_DEBUG_OBJECT(src,
"sttime:%lu, DTS:%lu, PTS: %lu~",
src->sttime, GST_BUFFER_DTS(*outbuf), GST_BUFFER_PTS(*outbuf));
@@ -396,12 +448,16 @@ gst_bambusrc_start (GstBaseSrc * bsrc)
GST_INFO_OBJECT (src, "stream %d type=%d, sub_type=%d", i, info.type, info.sub_type);
if (info.type == VIDE) {
src->video_type = info.sub_type;
src->frame_rate = info.format.video.frame_rate;
GST_INFO_OBJECT (src, " width %d height=%d, frame_rate=%d",
info.format.video.width, info.format.video.height, info.format.video.frame_rate);
}
}
src->sttime = 0;
src->frame_count = 0;
src->last_arrival = 0;
src->avg_period = 0;
return TRUE;
}
+4
View File
@@ -65,6 +65,10 @@ struct _GstBambuSrc
Bambu_Tunnel tnl;
GstClockTime sttime;
int video_type;
int frame_rate;
guint64 frame_count;
GstClockTime last_arrival;
GstClockTime avg_period;
};
extern void gstbambusrc_register();
+27 -63
View File
@@ -44,7 +44,9 @@
#include "MsgDialog.hpp"
#include "Notebook.hpp"
#include "Widgets/ComboBox.hpp"
#include "Widgets/Label.hpp"
#include "Widgets/SwitchButton.hpp"
#include "Widgets/TabCtrl.hpp"
#include "Widgets/ComboBox.hpp"
#include "Widgets/CheckBox.hpp"
@@ -68,6 +70,16 @@ t_config_option_keys deep_diff(const ConfigBase &config_this, const ConfigBase &
namespace GUI {
namespace
{
int mode_to_selection(ConfigOptionMode mode)
{
return mode == comExpert ? 2 :
mode == comAdvanced ? 1 :
0;
}
}
#define DISABLE_UNDO_SYS
// Forward declaration for early use; definitions live later in this translation unit.
@@ -393,17 +405,19 @@ void Tab::create_preset_tab()
if (dynamic_cast<TabPrint*>(this) == nullptr) {
m_mode_icon = new ScalableButton(m_top_panel, wxID_ANY, "advanced"); // ORCA
m_mode_icon->SetToolTip(_L("Show/Hide advanced parameters"));
m_mode_icon->SetToolTip(_L("Cycle settings visibility"));
m_mode_icon->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {
if(wxGetApp().get_mode() == comDevelop) return; // prevent change on dev mode
m_mode_view->SetValue(!m_mode_view->GetValue());
wxCommandEvent evt(wxEVT_TOGGLEBUTTON, m_mode_view->GetId()); // ParamsPanel::OnToggled(evt)
evt.SetEventObject(m_mode_view);
m_mode_view->wxEvtHandler::ProcessEvent(evt);
if (wxGetApp().get_mode() == comDevelop || m_mode_view == nullptr)
return; // prevent change on dev mode
const int selection = m_mode_view->GetSelection();
m_mode_view->SelectAndNotify((selection + 1) % 3);
});
m_top_sizer->Add(m_mode_icon, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(SidebarProps::WideSpacing()));
m_mode_view = new SwitchButton(m_top_panel, wxID_ABOUT);
m_mode_view->SetToolTip(_L("Show/Hide advanced parameters"));
m_mode_view = new ModeSwitchButton(m_top_panel);
m_mode_view->SetSelection(mode_to_selection(wxGetApp().get_saved_mode()));
if (wxGetApp().get_mode() == comDevelop)
m_mode_view->Enable(false);
m_top_sizer->AddSpacer(FromDIP(SidebarProps::ElementSpacing()));
m_top_sizer->Add( m_mode_view, 0, wxALIGN_CENTER_VERTICAL);
}
@@ -417,56 +431,6 @@ void Tab::create_preset_tab()
else
m_top_panel->Hide();
#if 0
#ifdef _MSW_DARK_MODE
// Sizer with buttons for mode changing
if (wxGetApp().tabs_as_menu())
#endif
m_mode_sizer = new ModeSizer(panel, int (0.5*em_unit(this)));
const float scale_factor = /*wxGetApp().*/em_unit(this)*0.1;// GetContentScaleFactor();
m_hsizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_hsizer, 0, wxEXPAND | wxBOTTOM, 3);
m_hsizer->Add(m_presets_choice, 0, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3);
m_hsizer->AddSpacer(int(4*scale_factor));
m_hsizer->Add(m_btn_save_preset, 0, wxALIGN_CENTER_VERTICAL);
m_hsizer->AddSpacer(int(4 * scale_factor));
m_hsizer->Add(m_btn_delete_preset, 0, wxALIGN_CENTER_VERTICAL);
if (m_btn_edit_ph_printer) {
m_hsizer->AddSpacer(int(4 * scale_factor));
m_hsizer->Add(m_btn_edit_ph_printer, 0, wxALIGN_CENTER_VERTICAL);
}
m_hsizer->AddSpacer(int(/*16*/8 * scale_factor));
m_hsizer->Add(m_btn_hide_incompatible_presets, 0, wxALIGN_CENTER_VERTICAL);
m_hsizer->AddSpacer(int(8 * scale_factor));
m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL);
m_hsizer->AddSpacer(int(32 * scale_factor));
m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL);
m_hsizer->AddSpacer(int(32 * scale_factor));
m_hsizer->Add(m_search_btn, 0, wxALIGN_CENTER_VERTICAL);
m_hsizer->AddSpacer(int(8*scale_factor));
m_hsizer->Add(m_btn_compare_preset, 0, wxALIGN_CENTER_VERTICAL);
m_hsizer->AddSpacer(int(16*scale_factor));
// m_hsizer->AddStretchSpacer(32);
// StretchSpacer has a strange behavior under OSX, so
// There is used just additional sizer for m_mode_sizer with right alignment
if (m_mode_sizer) {
auto mode_sizer = new wxBoxSizer(wxVERTICAL);
// Don't set the 2nd parameter to 1, making the sizer rubbery scalable in Y axis may lead
// to wrong vertical size assigned to wxBitmapComboBoxes, see GH issue #7176.
mode_sizer->Add(m_mode_sizer, 0, wxALIGN_RIGHT);
m_hsizer->Add(mode_sizer, 1, wxALIGN_CENTER_VERTICAL | wxRIGHT, wxOSX ? 15 : 10);
}
//Horizontal sizer to hold the tree and the selected page.
m_hsizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_hsizer, 1, wxEXPAND, 0);
//left vertical sizer
m_left_sizer = new wxBoxSizer(wxVERTICAL);
m_hsizer->Add(m_left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 3);
#endif
// tree
m_tabctrl = new TabCtrl(panel, wxID_ANY, wxDefaultPosition, wxSize(20 * m_em_unit, -1),
wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_NONE | wxWANTS_CHARS | wxTR_FULL_ROW_HIGHLIGHT);
@@ -1277,11 +1241,6 @@ void Tab::update_mode()
{
m_mode = wxGetApp().get_mode();
//BBS: GUI refactor
// update mode for ModeSizer
//if (m_mode_sizer)
// m_mode_sizer->SetMode(m_mode);
update_visibility();
update_changed_tree_ui();
@@ -1325,7 +1284,9 @@ void Tab::msw_rescale()
bmp->msw_rescale();
if (m_mode_view)
{
m_mode_view->Rescale();
}
if (m_detach_preset_btn)
m_detach_preset_btn->msw_rescale();
@@ -2330,6 +2291,7 @@ void TabPrint::build()
optgroup->append_single_option_line("xy_hole_compensation", "quality_settings_precision#x-y-compensation");
optgroup->append_single_option_line("xy_contour_compensation", "quality_settings_precision#x-y-compensation");
optgroup->append_single_option_line("elefant_foot_compensation", "quality_settings_precision#elephant-foot-compensation");
optgroup->append_single_option_line("elefant_foot_layers_density", "quality_settings_precision#elephant-foot-compensation");
optgroup->append_single_option_line("elefant_foot_compensation_layers", "quality_settings_precision#elephant-foot-compensation");
optgroup->append_single_option_line("precise_outer_wall", "quality_settings_precision#precise-wall");
optgroup->append_single_option_line("precise_z_height", "quality_settings_precision#precise-z-height");
@@ -2356,6 +2318,8 @@ void TabPrint::build()
optgroup->append_single_option_line("min_bead_width", "quality_settings_wall_generator#arachne");
optgroup->append_single_option_line("min_feature_size", "quality_settings_wall_generator#arachne");
optgroup->append_single_option_line("min_length_factor", "quality_settings_wall_generator#arachne");
optgroup->append_single_option_line("wall_maximum_resolution", "quality_settings_wall_generator#arachne");
optgroup->append_single_option_line("wall_maximum_deviation", "quality_settings_wall_generator#arachne");
optgroup = page->new_optgroup(L("Walls and surfaces"), L"param_wall_surface");
optgroup->append_single_option_line("wall_sequence", "quality_settings_wall_and_surfaces#walls-printing-order");
+3 -3
View File
@@ -44,6 +44,8 @@
#include "Widgets/ComboBox.hpp"
class TabCtrl;
class ModeSwitchButton;
class SwitchButton;
namespace Slic3r {
@@ -163,8 +165,6 @@ protected:
wxScrolledWindow* m_page_view {nullptr};
//wxBoxSizer* m_page_sizer {nullptr};
//ModeSizer* m_mode_sizer {nullptr};
struct PresetDependencies {
Preset::Type type = Preset::TYPE_INVALID;
::CheckBox* checkbox = nullptr;
@@ -304,7 +304,7 @@ public:
// 3. propagate changed configuration to the Plater when (m_update_cnt == 0) only
int m_update_cnt = 0;
SwitchButton *m_mode_view = nullptr;
ModeSwitchButton *m_mode_view = nullptr;
SwitchButton *m_extruder_switch = nullptr;
public:
+1 -1
View File
@@ -84,7 +84,7 @@ ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_AN
BOOST_LOG_TRIVIAL(info) << "login url = " << TargetUrl.ToStdString();
m_bbl_user_agent = wxString::Format("BBL-Slicer/v%s", SLIC3R_VERSION);
m_bbl_user_agent = wxString::Format("BBL-Slicer/v%s", wxGetApp().get_bbl_client_version());
// set the frame icon
+187 -17
View File
@@ -3,6 +3,8 @@
#include "StaticBox.hpp"
#include "../wxExtensions.hpp"
#include "../GUI_App.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "../Utils/MacDarkMode.hpp"
#include "../Utils/WxFontUtils.hpp"
#ifdef __APPLE__
@@ -156,10 +158,10 @@ void SwitchButton::Rescale()
#endif
dc2.SetBrush(wxBrush(track_color.colorForStates(state)));
dc2.SetPen(wxPen(track_color.colorForStates(state)));
dc2.DrawRoundedRectangle(wxRect({0, 0}, trackSize), trackSize.y / 2);
dc2.DrawRoundedRectangle(wxRect({0, 0}, trackSize), trackSize.y / 2.0);
dc2.SetBrush(wxBrush(thumb_color.colorForStates(StateColor::Checked | StateColor::Enabled)));
dc2.SetPen(wxPen(thumb_color.colorForStates(StateColor::Checked | StateColor::Enabled)));
dc2.DrawRoundedRectangle(wxRect({ i == 0 ? BS : (trackSize.x - thumbSize.x - BS), BS}, thumbSize), thumbSize.y / 2);
dc2.DrawRoundedRectangle(wxRect({ i == 0 ? BS : (trackSize.x - thumbSize.x - BS), BS}, thumbSize), thumbSize.y / 2.0);
}
memdc.SetTextForeground(text_color.colorForStates(state ^ StateColor::Checked));
auto text_y = BS + (thumbSize.y - textSize[0].y) / 2;
@@ -202,6 +204,184 @@ void SwitchButton::update()
SetBitmap((GetValue() ? m_on : m_off).bmp());
}
ModeSwitchButton::ModeSwitchButton(wxWindow* parent, wxWindowID id)
{
background_color = StateColor(
std::make_pair(wxColour(0xF1, 0xF1, 0xF1), (int) StateColor::Disabled),
std::make_pair(wxColour(0xE3, 0xE3, 0xE3), (int) StateColor::Pressed),
std::make_pair(wxColour(0xD9, 0xD9, 0xD9), (int) StateColor::Normal));
border_color = StateColor(
std::make_pair(wxColour(0xEA, 0xEA, 0xEA), (int) StateColor::Disabled),
std::make_pair(wxColour(0xBC, 0xBC, 0xBC), (int) StateColor::Hovered),
std::make_pair(wxColour(0xC8, 0xC8, 0xC8), (int) StateColor::Focused),
std::make_pair(wxColour(0xCE, 0xCE, 0xCE), (int) StateColor::Normal));
StaticBox::Create(parent, id, wxDefaultPosition, wxDefaultSize, 0);
SetBackgroundColour(StaticBox::GetParentBackgroundColor(parent));
SetCursor(wxCursor(wxCURSOR_HAND));
m_tooltips[0] = _L("Simple settings");
m_tooltips[1] = _L("Advanced settings");
m_tooltips[2] = _L("Expert settings");
Bind(wxEVT_LEFT_DOWN, &ModeSwitchButton::mouseDown, this);
Bind(wxEVT_LEFT_UP, &ModeSwitchButton::mouseReleased, this);
Bind(wxEVT_LEFT_DCLICK, &ModeSwitchButton::mouseDown, this);
Bind(wxEVT_MOUSE_CAPTURE_LOST, &ModeSwitchButton::mouseCaptureLost, this);
Rescale();
}
void ModeSwitchButton::SetSelection(int selection)
{
m_selection = std::clamp(selection, 0, 2);
update_tooltip();
Refresh();
}
void ModeSwitchButton::SelectAndNotify(int selection)
{
if (!IsEnabled())
return;
SetSelection(selection);
Slic3r::GUI::wxGetApp().save_mode(m_selection);
}
void ModeSwitchButton::Rescale()
{
const wxSize button_size = FromDIP(wxSize(48, 20));
SetMinSize(button_size);
SetMaxSize(button_size);
SetSize(button_size);
SetCornerRadius(button_size.y / 2.0);
Refresh();
}
bool ModeSwitchButton::Enable(bool enable /* = true */)
{
const bool changed = StaticBox::Enable(enable);
if (changed)
Refresh();
return changed;
}
void ModeSwitchButton::doRender(wxDC& dc)
{
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(wxBrush(GetBackgroundColour()));
dc.DrawRectangle(GetClientRect());
const wxRect bounds = GetClientRect().Deflate(1);
if (bounds.width <= 0 || bounds.height <= 0)
return;
const int states = state_handler.states();
const bool hovered = (states & StateHandler::Hovered) != 0;
const bool focused = (states & StateHandler::Focused) != 0;
const bool disabled = !IsEnabled();
const wxColour track_fill = disabled ? wxColour(0xD0, 0xD0, 0xD4) :
m_pressed ? wxColour(0x5A, 0x5D, 0x64) : wxColour(0x66, 0x69, 0x70);
const wxColour track_border = disabled ? wxColour(0xDD, 0xDD, 0xE0) :
focused ? wxColour("#009688") :
hovered ? wxColour(0x7A, 0x7D, 0x84) : wxColour(0x75, 0x78, 0x7F);
const wxColour active_fill = disabled ? wxColour(0x9E, 0xBE, 0xB9) :
m_pressed ? wxColour(0x00877B) : wxColour("#009688");
const wxColour active_dot = disabled ? wxColour(0xEC, 0xF4, 0xF2) : wxColour(0xB7, 0xEB, 0xE3);
const wxColour inactive_dot = disabled ? wxColour(0xF2, 0xF2, 0xF4) : wxColour(0xB5, 0xB7, 0xBD);
const wxColour thumb_fill = disabled ? wxColour(0xFA, 0xFA, 0xFA) : *wxWHITE;
const wxColour thumb_border = disabled ? wxColour(0xE7, 0xE7, 0xEA) : wxColour(0xDD, 0xDF, 0xE3);
dc.SetPen(wxPen(track_border, 1));
dc.SetBrush(wxBrush(track_fill));
dc.DrawRoundedRectangle(bounds, bounds.height / 2.0);
const wxRect thumb = thumb_rect_for(m_selection);
const int fill_right = std::min(bounds.GetRight(), thumb.GetX() + thumb.GetWidth() / 2 + FromDIP(2));
wxRect active(bounds.x, bounds.y, fill_right - bounds.x + 1, bounds.height);
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(wxBrush(active_fill));
dc.DrawRoundedRectangle(active, bounds.height / 2.0);
const int dot_radius = std::max(FromDIP(1), thumb.height / 7);
for (int idx = 0; idx < 3; ++idx) {
if (idx == m_selection)
continue;
const wxRect slot = thumb_rect_for(idx);
const wxPoint center(slot.GetX() + slot.GetWidth() / 2, slot.GetY() + slot.GetHeight() / 2);
dc.SetBrush(wxBrush(idx < m_selection ? active_dot : inactive_dot));
dc.DrawCircle(center, dot_radius);
}
dc.SetPen(wxPen(thumb_border, 1));
dc.SetBrush(wxBrush(thumb_fill));
dc.DrawRoundedRectangle(thumb, thumb.height / 2.0);
}
void ModeSwitchButton::mouseDown(wxMouseEvent& event)
{
if (!IsEnabled()) {
event.Skip();
return;
}
m_pressed = true;
if (!HasCapture())
CaptureMouse();
Refresh();
event.Skip();
}
void ModeSwitchButton::mouseReleased(wxMouseEvent& event)
{
if (m_pressed) {
m_pressed = false;
if (HasCapture())
ReleaseMouse();
if (GetClientRect().Contains(event.GetPosition()))
SelectAndNotify(hit_test_selection(event.GetPosition()));
Refresh();
}
event.Skip();
}
void ModeSwitchButton::mouseCaptureLost(wxMouseCaptureLostEvent& event)
{
m_pressed = false;
Refresh();
event.Skip();
}
int ModeSwitchButton::hit_test_selection(const wxPoint& point) const
{
const int width = std::max(1, GetClientSize().x);
const int x = std::clamp(point.x, 0, width - 1);
return std::clamp((x * 3) / width, 0, 2);
}
wxRect ModeSwitchButton::thumb_rect_for(int selection) const
{
const wxRect bounds = GetClientRect().Deflate(3);
const int thumb_diameter = std::max(FromDIP(10), bounds.height - FromDIP(2));
const int y = bounds.y + (bounds.height - thumb_diameter) / 2;
const int travel = std::max(0, bounds.width - thumb_diameter);
const int x = bounds.x + (travel * std::clamp(selection, 0, 2)) / 2;
return wxRect(x, y, thumb_diameter, thumb_diameter);
}
void ModeSwitchButton::update_tooltip()
{
SetToolTip(m_tooltips[m_selection]);
}
SwitchBoard::SwitchBoard(wxWindow *parent, wxString leftL, wxString right, wxSize size)
: wxWindow(parent, wxID_ANY, wxDefaultPosition, size)
{
@@ -355,24 +535,14 @@ void SwitchBoard::on_left_down(wxMouseEvent &evt)
wxPostEvent(this, event);
}
void SwitchBoard::Enable()
bool SwitchBoard::Enable(bool enable /* = true */)
{
if (is_enable == true)
if (is_enable == enable)
{
return;
return false;
}
is_enable = true;
Refresh();
}
void SwitchBoard::Disable()
{
if (is_enable == false)
{
return;
}
is_enable = false;
is_enable = enable;
Refresh();
return true;
}
+34 -4
View File
@@ -3,10 +3,9 @@
#include "../wxExtensions.hpp"
#include "StateColor.hpp"
#include "StaticBox.hpp"
#include <wx/tglbtn.h>
#include "Label.hpp"
#include "Button.hpp"
wxDECLARE_EVENT(wxCUSTOMEVT_SWITCH_POS, wxCommandEvent);
@@ -46,6 +45,37 @@ private:
StateColor thumb_color;
};
class ModeSwitchButton : public StaticBox
{
public:
ModeSwitchButton(wxWindow* parent = nullptr, wxWindowID id = wxID_ANY);
int GetSelection() const { return m_selection; }
void SetSelection(int selection);
void SelectAndNotify(int selection);
void Rescale();
void msw_rescale() { Rescale(); }
bool Enable(bool enable = true) override;
protected:
void doRender(wxDC& dc) override;
private:
void mouseDown(wxMouseEvent& event);
void mouseReleased(wxMouseEvent& event);
void mouseCaptureLost(wxMouseCaptureLostEvent& event);
int hit_test_selection(const wxPoint& point) const;
wxRect thumb_rect_for(int selection) const;
void update_tooltip();
private:
int m_selection { 0 };
bool m_pressed { false };
wxString m_tooltips[3];
};
class SwitchBoard : public wxWindow
{
public:
@@ -62,8 +92,8 @@ public:
void* client_data = nullptr;/*MachineObject* in StatusPanel*/
public:
void Enable();
void Disable();
bool Enable(bool enable = true) override;
bool Disable() { return Enable(false); }
bool IsEnabled(){return is_enable;};
void SetClientData(void* data) { client_data = data; };
+3 -3
View File
@@ -276,7 +276,7 @@ wxWebView* WebView::CreateWebView(wxWindow * parent, wxString const & url)
#ifdef __WIN32__
webView->SetUserAgent(wxString::Format("Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.52 BBL-Slicer/v%s (%s) BBL-Language/%s",
SLIC3R_VERSION, Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light", language_code.mb_str()));
Slic3r::GUI::wxGetApp().get_bbl_client_version(), Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light", language_code.mb_str()));
webView->Create(parent, wxID_ANY, url2, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
// We register the wxfs:// protocol for testing purposes
webView->RegisterHandler(wxSharedPtr<wxWebViewHandler>(new wxWebViewArchiveHandler("bbl")));
@@ -294,7 +294,7 @@ wxWebView* WebView::CreateWebView(wxWindow * parent, wxString const & url)
}
webView->Create(parent, wxID_ANY, url2, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
webView->SetUserAgent(wxString::Format("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) BBL-Slicer/v%s (%s) BBL-Language/%s",
SLIC3R_VERSION, Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light", language_code.mb_str()));
Slic3r::GUI::wxGetApp().get_bbl_client_version(), Slic3r::GUI::wxGetApp().dark_mode() ? "dark" : "light", language_code.mb_str()));
#endif
#ifdef __WXMAC__
WKWebView * wkWebView = (WKWebView *) webView->GetNativeBackend();
@@ -399,7 +399,7 @@ void WebView::RecreateAll()
language_code = language_code.ToStdString();
for (auto webView : g_webviews) {
webView->SetUserAgent(wxString::Format("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) BBL-Slicer/v%s (%s) BBL-Language/%s",
SLIC3R_VERSION, dark ? "dark" : "light", language_code.mb_str()));
Slic3r::GUI::wxGetApp().get_bbl_client_version(), dark ? "dark" : "light", language_code.mb_str()));
webView->Reload();
}
}
-144
View File
@@ -413,15 +413,6 @@ int em_unit(wxWindow* win)
return Slic3r::GUI::wxGetApp().em_unit();
}
int mode_icon_px_size()
{
#ifdef __APPLE__
return 10;
#else
return 12;
#endif
}
wxBitmap create_menu_bitmap(const std::string& bmp_name)
{
return create_scaled_bitmap(bmp_name, nullptr, 16, false, "", true);
@@ -895,141 +886,6 @@ void LockButton::update_button_bitmaps()
// ----------------------------------------------------------------------------
// ModeButton
// ----------------------------------------------------------------------------
ModeButton::ModeButton( wxWindow * parent,
wxWindowID id,
const std::string& icon_name /* = ""*/,
const wxString& mode /* = wxEmptyString*/,
const wxSize& size /* = wxDefaultSize*/,
const wxPoint& pos /* = wxDefaultPosition*/) :
ScalableButton(parent, id, icon_name, mode, size, pos, wxBU_EXACTFIT)
{
Init(mode);
}
ModeButton::ModeButton( wxWindow* parent,
const wxString& mode/* = wxEmptyString*/,
const std::string& icon_name/* = ""*/,
int px_cnt/* = 16*/) :
ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, icon_name, px_cnt), mode, wxBU_EXACTFIT)
{
Init(mode);
}
void ModeButton::Init(const wxString &mode)
{
std::string mode_str = std::string(mode.ToUTF8());
//m_tt_focused = Slic3r::GUI::from_u8((boost::format(_utf8(L("Switch to the %s mode"))) % mode_str).str());
//m_tt_selected = Slic3r::GUI::from_u8((boost::format(_utf8(L("Current mode is %s"))) % mode_str).str());
SetBitmapMargins(3, 0);
//button events
Bind(wxEVT_BUTTON, &ModeButton::OnButton, this);
Bind(wxEVT_ENTER_WINDOW, &ModeButton::OnEnterBtn, this);
Bind(wxEVT_LEAVE_WINDOW, &ModeButton::OnLeaveBtn, this);
}
void ModeButton::OnButton(wxCommandEvent& event)
{
m_is_selected = true;
focus_button(m_is_selected);
event.Skip();
}
void ModeButton::SetState(const bool state)
{
m_is_selected = state;
focus_button(m_is_selected);
SetToolTip(state ? m_tt_selected : m_tt_focused);
}
void ModeButton::focus_button(const bool focus)
{
const wxFont& new_font = focus ?
Slic3r::GUI::wxGetApp().bold_font() :
Slic3r::GUI::wxGetApp().normal_font();
SetFont(new_font);
#ifdef _WIN32
GetParent()->Refresh(); // force redraw a background of the selected mode button
#else
SetForegroundColour(wxSystemSettings::GetColour(focus ? wxSYS_COLOUR_BTNTEXT :
#if defined (__linux__) && defined (__WXGTK3__)
wxSYS_COLOUR_GRAYTEXT
#elif defined (__linux__) && defined (__WXGTK2__)
wxSYS_COLOUR_BTNTEXT
#else
wxSYS_COLOUR_BTNSHADOW
#endif
));
#endif /* no _WIN32 */
Refresh();
Update();
}
// ----------------------------------------------------------------------------
// ModeSizer
// ----------------------------------------------------------------------------
ModeSizer::ModeSizer(wxWindow *parent, int hgap/* = 0*/) :
wxFlexGridSizer(3, 0, hgap),
m_parent(parent),
m_hgap_unscaled((double)(hgap)/em_unit(parent))
{
SetFlexibleDirection(wxHORIZONTAL);
std::vector < std::pair < wxString, std::string >> buttons = {
//{_(L("Simple")), "mode_simple"},
//{_(L("Advanced")), "mode_advanced"},
//{_CTX(L_CONTEXT("Advanced", "Mode"), "Mode"), "mode_advanced"}
};
auto modebtnfn = [](wxCommandEvent &event, int mode_id) {
Slic3r::GUI::wxGetApp().save_mode(mode_id);
event.Skip();
};
m_mode_btns.reserve(3);
for (const auto& button : buttons) {
m_mode_btns.push_back(new ModeButton(parent, button.first, button.second, mode_icon_px_size()));
m_mode_btns.back()->Bind(wxEVT_BUTTON, std::bind(modebtnfn, std::placeholders::_1, int(m_mode_btns.size() - 1)));
Add(m_mode_btns.back());
}
}
void ModeSizer::SetMode(const int mode)
{
for (size_t m = 0; m < m_mode_btns.size(); m++)
m_mode_btns[m]->SetState(int(m) == mode);
}
void ModeSizer::set_items_flag(int flag)
{
for (wxSizerItem* item : this->GetChildren())
item->SetFlag(flag);
}
void ModeSizer::set_items_border(int border)
{
for (wxSizerItem* item : this->GetChildren())
item->SetBorder(border);
}
void ModeSizer::msw_rescale()
{
this->SetHGap(std::lround(m_hgap_unscaled * em_unit(m_parent)));
for (size_t m = 0; m < m_mode_btns.size(); m++)
m_mode_btns[m]->msw_rescale();
}
// ----------------------------------------------------------------------------
// MenuWithSeparators
// ----------------------------------------------------------------------------
-71
View File
@@ -52,7 +52,6 @@ class wxDialog;
void edit_tooltip(wxString& tooltip);
void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector<int>& btn_ids);
int em_unit(wxWindow* win);
int mode_icon_px_size();
wxBitmap create_menu_bitmap(const std::string& bmp_name);
@@ -285,76 +284,6 @@ private:
};
// ----------------------------------------------------------------------------
// ModeButton
// ----------------------------------------------------------------------------
class ModeButton : public ScalableButton
{
public:
ModeButton(
wxWindow* parent,
wxWindowID id,
const std::string& icon_name = "",
const wxString& mode = wxEmptyString,
const wxSize& size = wxDefaultSize,
const wxPoint& pos = wxDefaultPosition);
ModeButton(
wxWindow* parent,
const wxString& mode = wxEmptyString,
const std::string& icon_name = "",
int px_cnt = 16);
~ModeButton() {}
void Init(const wxString& mode);
void OnButton(wxCommandEvent& event);
void OnEnterBtn(wxMouseEvent& event) { focus_button(true); event.Skip(); }
void OnLeaveBtn(wxMouseEvent& event) { focus_button(m_is_selected); event.Skip(); }
void SetState(const bool state);
bool is_selected() { return m_is_selected; }
protected:
void focus_button(const bool focus);
private:
bool m_is_selected = false;
wxString m_tt_selected;
wxString m_tt_focused;
};
// ----------------------------------------------------------------------------
// ModeSizer
// ----------------------------------------------------------------------------
class ModeSizer : public wxFlexGridSizer
{
public:
ModeSizer( wxWindow *parent, int hgap = 0);
~ModeSizer() {}
void SetMode(const /*ConfigOptionMode*/int mode);
void set_items_flag(int flag);
void set_items_border(int border);
void msw_rescale();
const std::vector<ModeButton*>& get_btns() { return m_mode_btns; }
private:
std::vector<ModeButton*> m_mode_btns;
wxWindow* m_parent {nullptr};
double m_hgap_unscaled;
};
// ----------------------------------------------------------------------------
// MenuWithSeparators
// ----------------------------------------------------------------------------
+11
View File
@@ -628,6 +628,17 @@ int BBLCloudServiceAgent::get_my_profile(std::string token, unsigned int* http_c
return -1;
}
int BBLCloudServiceAgent::get_my_token(std::string ticket, unsigned int* http_code, std::string* http_body)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
auto func = plugin.get_get_my_token();
if (func && agent) {
return func(agent, ticket, http_code, http_body);
}
return -1;
}
// ============================================================================
// Analytics & Tracking
// ============================================================================
@@ -98,6 +98,7 @@ public:
int get_model_mall_home_url(std::string* url) override;
int get_model_mall_detail_url(std::string* url, std::string id) override;
int get_my_profile(std::string token, unsigned int* http_code, std::string* http_body) override;
int get_my_token(std::string ticket, unsigned int* http_code, std::string* http_body) override;
// Analytics & Tracking
int track_enable(bool enable) override;
+4 -1
View File
@@ -176,7 +176,8 @@ int BBLNetworkPlugin::initialize(bool using_backup, const std::string& version)
<< ", version=" << (loaded_version.empty() ? "unknown" : loaded_version)
<< ", send_message=" << (m_send_message ? "loaded" : "null")
<< ", start_print=" << (m_start_print ? "loaded" : "null")
<< ", start_local_print=" << (m_start_local_print ? "loaded" : "null");
<< ", start_local_print=" << (m_start_local_print ? "loaded" : "null")
<< ", get_my_token=" << (m_get_my_token ? "loaded" : "null");
return 0;
}
@@ -622,6 +623,7 @@ void BBLNetworkPlugin::load_all_function_pointers()
m_get_model_mall_home_url = reinterpret_cast<func_get_model_mall_home_url>(get_function("bambu_network_get_model_mall_home_url"));
m_get_model_mall_detail_url = reinterpret_cast<func_get_model_mall_detail_url>(get_function("bambu_network_get_model_mall_detail_url"));
m_get_my_profile = reinterpret_cast<func_get_my_profile>(get_function("bambu_network_get_my_profile"));
m_get_my_token = reinterpret_cast<func_get_my_token>(get_function("bambu_network_get_my_token"));
m_track_enable = reinterpret_cast<func_track_enable>(get_function("bambu_network_track_enable"));
m_track_remove_files = reinterpret_cast<func_track_remove_files>(get_function("bambu_network_track_remove_files"));
m_track_event = reinterpret_cast<func_track_event>(get_function("bambu_network_track_event"));
@@ -725,6 +727,7 @@ void BBLNetworkPlugin::clear_all_function_pointers()
m_get_model_mall_home_url = nullptr;
m_get_model_mall_detail_url = nullptr;
m_get_my_profile = nullptr;
m_get_my_token = nullptr;
m_track_enable = nullptr;
m_track_remove_files = nullptr;
m_track_event = nullptr;
+3
View File
@@ -107,6 +107,7 @@ typedef int (*func_get_subtask)(void *agent, BBLModelTask* task, OnGetSubTaskFn
typedef int (*func_get_model_mall_home_url)(void *agent, std::string* url);
typedef int (*func_get_model_mall_detail_url)(void *agent, std::string* url, std::string id);
typedef int (*func_get_my_profile)(void *agent, std::string token, unsigned int *http_code, std::string *http_body);
typedef int (*func_get_my_token)(void *agent, std::string ticket, unsigned int *http_code, std::string *http_body);
typedef int (*func_track_enable)(void *agent, bool enable);
typedef int (*func_track_remove_files)(void *agent);
typedef int (*func_track_event)(void *agent, std::string evt_key, std::string content);
@@ -361,6 +362,7 @@ public:
func_get_model_mall_home_url get_get_model_mall_home_url() const { return m_get_model_mall_home_url; }
func_get_model_mall_detail_url get_get_model_mall_detail_url() const { return m_get_model_mall_detail_url; }
func_get_my_profile get_get_my_profile() const { return m_get_my_profile; }
func_get_my_token get_get_my_token() const { return m_get_my_token; }
func_track_enable get_track_enable() const { return m_track_enable; }
func_track_remove_files get_track_remove_files() const { return m_track_remove_files; }
func_track_event get_track_event() const { return m_track_event; }
@@ -496,6 +498,7 @@ private:
func_get_model_mall_home_url m_get_model_mall_home_url{nullptr};
func_get_model_mall_detail_url m_get_model_mall_detail_url{nullptr};
func_get_my_profile m_get_my_profile{nullptr};
func_get_my_token m_get_my_token{nullptr};
func_track_enable m_track_enable{nullptr};
func_track_remove_files m_track_remove_files{nullptr};
func_track_event m_track_event{nullptr};
+8
View File
@@ -353,6 +353,14 @@ public:
*/
virtual int get_my_profile(std::string token, unsigned int* http_code, std::string* http_body) = 0;
/**
* Exchange a one-time login ticket (from a third-party OAuth callback)
* for token + profile JSON. Used by the localhost callback handler when
* the auth server redirects with ?ticket=...&redirect_url=... instead of
* passing tokens directly in the URL.
*/
virtual int get_my_token(std::string ticket, unsigned int* http_code, std::string* http_body) = 0;
// ========================================================================
// Analytics & Tracking
// ========================================================================
+6
View File
@@ -781,6 +781,12 @@ int NetworkAgent::get_my_profile(std::string token, unsigned int* http_code, std
return -1;
}
int NetworkAgent::get_my_token(std::string ticket, unsigned int* http_code, std::string* http_body)
{
if (m_cloud_agent) return m_cloud_agent->get_my_token(ticket, http_code, http_body);
return -1;
}
int NetworkAgent::track_enable(bool enable)
{
this->enable_track = enable;
+1
View File
@@ -144,6 +144,7 @@ public:
int get_model_mall_home_url(std::string* url);
int get_model_mall_detail_url(std::string* url, std::string id);
int get_my_profile(std::string token, unsigned int* http_code, std::string* http_body);
int get_my_token(std::string ticket, unsigned int* http_code, std::string* http_body);
int track_enable(bool enable);
int track_remove_files();
int track_event(std::string evt_key, std::string content);
@@ -2474,6 +2474,14 @@ int OrcaCloudServiceAgent::get_my_profile(std::string token, unsigned int* http_
return BAMBU_NETWORK_SUCCESS;
}
int OrcaCloudServiceAgent::get_my_token(std::string ticket, unsigned int* http_code, std::string* http_body)
{
BOOST_LOG_TRIVIAL(debug) << "OrcaCloudServiceAgent: get_my_token (stub) - Orca cloud uses code-based OAuth, not tickets";
if (http_code) *http_code = 0;
if (http_body) *http_body = "";
return -1;
}
int OrcaCloudServiceAgent::track_enable(bool enable)
{
std::lock_guard<std::recursive_mutex> lock(state_mutex);
@@ -199,6 +199,7 @@ public:
int get_model_mall_home_url(std::string* url) override;
int get_model_mall_detail_url(std::string* url, std::string id) override;
int get_my_profile(std::string token, unsigned int* http_code, std::string* http_body) override;
int get_my_token(std::string ticket, unsigned int* http_code, std::string* http_body) override;
// ========================================================================
// ICloudServiceAgent Interface Implementation - Analytics & Tracking