mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-11 18:01:14 +00:00
Merge remote-tracking branch 'upstream/main' into feedback
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -199,6 +199,7 @@ enum ConfigOptionType {
|
||||
enum ConfigOptionMode {
|
||||
comSimple = 0,
|
||||
comAdvanced,
|
||||
comExpert,
|
||||
comDevelop,
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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") },
|
||||
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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 += "] ";
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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");
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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; };
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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
|
||||
// ========================================================================
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user