Merge branch 'main' into enh-port-edit-gcode-dlg
This commit is contained in:
commit
0af1b9b958
14 changed files with 463 additions and 51 deletions
76
resources/images/camera_switch.svg
Normal file
76
resources/images/camera_switch.svg
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="39"
|
||||
height="22"
|
||||
viewBox="0 0 39 22"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
sodipodi:docname="camera_switch.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs5">
|
||||
<clipPath
|
||||
id="clip0_7112_29362">
|
||||
<rect
|
||||
id="svg_1"
|
||||
fill="#ffffff"
|
||||
height="30"
|
||||
width="30"
|
||||
x="0"
|
||||
y="0" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview5"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="16"
|
||||
inkscape:cx="6.84375"
|
||||
inkscape:cy="11.21875"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1369"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg5"
|
||||
showguides="false" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="m 21.066182,15.123836 h 1.463398 1.463399 1.463398 1.463399 m 0,0 h 1.463398 c 1.167463,0 2.113821,-0.946358 2.113821,-2.113821 V 2.600884 c 0,-1.1674317 -0.946358,-2.11382107 -2.113821,-2.11382107 H 16.675987 c -1.167411,0 -2.1138,0.94637877 -2.1138,2.11381047 v 1.3011427 1.3011428 1.3011427 1.3011427 h 1.268271 V 6.5043016 5.2031589 3.9020161 2.6008734 c 0,-0.4669642 0.378586,-0.8455179 0.845529,-0.8455179 h 11.707187 c 0.467048,0 0.845528,0.3785537 0.845528,0.8455285 v 10.409131 c 0,0.467049 -0.37848,0.845529 -0.845528,0.845529 h -1.463398 m 0,0 H 25.456377 23.992979 22.52958 21.066182 m 0,0 v 1.268292"
|
||||
fill="#b3b3b5"
|
||||
id="path1"
|
||||
style="fill:#262e30;fill-opacity:1;stroke:none;stroke-width:0;stroke-dasharray:none"
|
||||
sodipodi:nodetypes="ccccccssssssccccccccsssssscccccccc" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="m 31.074174,7.804414 4.093731,3.449544 V 4.3548482 Z M 30.065035,6.9961839 c -0.501081,0.4222886 -0.501081,1.1942138 0,1.6164496 l 4.633179,3.9041215 c 0.687309,0.579187 1.737984,0.09058 1.737984,-0.80822 V 3.9003132 c 0,-0.8987967 -1.050675,-1.3873959 -1.737984,-0.80823 z"
|
||||
fill="#b3b3b5"
|
||||
id="path4"
|
||||
style="fill:#262e30;fill-opacity:1;stroke-width:1.05691" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M 19.153511,8.3696736 H 7.4463245 c -0.4669431,0 -0.8455285,0.3785536 -0.8455285,0.8455179 V 19.624333 c 0,0.467049 0.3785854,0.845528 0.8455285,0.845528 H 19.153511 c 0.467048,0 0.845528,-0.378479 0.845528,-0.845528 V 9.215202 c 0,-0.4669748 -0.37848,-0.8455284 -0.845528,-0.8455284 z M 7.4463245,7.1013809 c -1.1674106,0 -2.1138,0.9463789 -2.1138,2.1138106 V 19.624333 c 0,1.167463 0.9463894,2.113821 2.1138,2.113821 H 19.153511 c 1.167463,0 2.113821,-0.946358 2.113821,-2.113821 V 9.215202 c 0,-1.1674317 -0.946358,-2.1138211 -2.113821,-2.1138211 z"
|
||||
fill="#b3b3b5"
|
||||
id="path1-5"
|
||||
style="stroke:none;stroke-width:0;stroke-dasharray:none;fill:#262e30;fill-opacity:1" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="m 21.844511,14.418731 4.093731,3.449545 v -6.89911 z m -1.009139,-0.80823 c -0.501081,0.422289 -0.501081,1.194214 0,1.61645 l 4.633179,3.904122 c 0.687309,0.579187 1.737984,0.09058 1.737984,-0.80822 v -7.808222 c 0,-0.8987971 -1.050675,-1.3873956 -1.737984,-0.8082304 z"
|
||||
fill="#b3b3b5"
|
||||
id="path4-1"
|
||||
style="stroke-width:1.05691;fill:#262e30;fill-opacity:1" />
|
||||
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
76
resources/images/camera_switch_dark.svg
Normal file
76
resources/images/camera_switch_dark.svg
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="39"
|
||||
height="22"
|
||||
viewBox="0 0 39 22"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
sodipodi:docname="camera_switch_dark.svg"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs5">
|
||||
<clipPath
|
||||
id="clip0_7112_29362">
|
||||
<rect
|
||||
id="svg_1"
|
||||
fill="#ffffff"
|
||||
height="30"
|
||||
width="30"
|
||||
x="0"
|
||||
y="0" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview5"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="16"
|
||||
inkscape:cx="6.84375"
|
||||
inkscape:cy="16.21875"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1369"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg5"
|
||||
showguides="false" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="m 21.066182,15.123836 h 1.463398 1.463399 1.463398 1.463399 m 0,0 h 1.463398 c 1.167463,0 2.113821,-0.946358 2.113821,-2.113821 V 2.600884 c 0,-1.1674317 -0.946358,-2.11382107 -2.113821,-2.11382107 H 16.675987 c -1.167411,0 -2.1138,0.94637877 -2.1138,2.11381047 v 1.3011427 1.3011428 1.3011427 1.3011427 h 1.268271 V 6.5043016 5.2031589 3.9020161 2.6008734 c 0,-0.4669642 0.378586,-0.8455179 0.845529,-0.8455179 h 11.707187 c 0.467048,0 0.845528,0.3785537 0.845528,0.8455285 v 10.409131 c 0,0.467049 -0.37848,0.845529 -0.845528,0.845529 h -1.463398 m 0,0 H 25.456377 23.992979 22.52958 21.066182 m 0,0 v 1.268292"
|
||||
fill="#b3b3b5"
|
||||
id="path1"
|
||||
style="fill:#b3b3b5;fill-opacity:1;stroke:none;stroke-width:0;stroke-dasharray:none"
|
||||
sodipodi:nodetypes="ccccccssssssccccccccsssssscccccccc" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="m 31.074174,7.804414 4.093731,3.449544 V 4.3548482 Z M 30.065035,6.9961839 c -0.501081,0.4222886 -0.501081,1.1942138 0,1.6164496 l 4.633179,3.9041215 c 0.687309,0.579187 1.737984,0.09058 1.737984,-0.80822 V 3.9003132 c 0,-0.8987967 -1.050675,-1.3873959 -1.737984,-0.80823 z"
|
||||
fill="#b3b3b5"
|
||||
id="path4"
|
||||
style="fill:#b3b3b5;fill-opacity:1;stroke-width:1.05691" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M 19.153511,8.3696736 H 7.4463245 c -0.4669431,0 -0.8455285,0.3785536 -0.8455285,0.8455179 V 19.624333 c 0,0.467049 0.3785854,0.845528 0.8455285,0.845528 H 19.153511 c 0.467048,0 0.845528,-0.378479 0.845528,-0.845528 V 9.215202 c 0,-0.4669748 -0.37848,-0.8455284 -0.845528,-0.8455284 z M 7.4463245,7.1013809 c -1.1674106,0 -2.1138,0.9463789 -2.1138,2.1138106 V 19.624333 c 0,1.167463 0.9463894,2.113821 2.1138,2.113821 H 19.153511 c 1.167463,0 2.113821,-0.946358 2.113821,-2.113821 V 9.215202 c 0,-1.1674317 -0.946358,-2.1138211 -2.113821,-2.1138211 z"
|
||||
fill="#b3b3b5"
|
||||
id="path1-5"
|
||||
style="stroke:none;stroke-width:0;stroke-dasharray:none;fill:#b3b3b5;fill-opacity:1" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="m 21.844511,14.418731 4.093731,3.449545 v -6.89911 z m -1.009139,-0.80823 c -0.501081,0.422289 -0.501081,1.194214 0,1.61645 l 4.633179,3.904122 c 0.687309,0.579187 1.737984,0.09058 1.737984,-0.80822 v -7.808222 c 0,-0.8987971 -1.050675,-1.3873956 -1.737984,-0.8082304 z"
|
||||
fill="#b3b3b5"
|
||||
id="path4-1"
|
||||
style="stroke-width:1.05691;fill:#b3b3b5;fill-opacity:1" />
|
||||
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
|
@ -2478,9 +2478,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
|
||||
CalibPressureAdvanceLine pa_test(this);
|
||||
|
||||
auto fast_speed = CalibPressureAdvance::find_optimal_PA_speed(print.full_print_config(), pa_test.line_width(), 0.2);
|
||||
auto slow_speed = std::max(20.0, fast_speed / 10.0);
|
||||
|
||||
auto fast_speed = CalibPressureAdvance::find_optimal_PA_speed(print.full_print_config(), pa_test.line_width(), pa_test.height_layer());
|
||||
auto slow_speed = std::max(10.0, fast_speed / 10.0);
|
||||
if (fast_speed < slow_speed + 5)
|
||||
fast_speed = slow_speed + 5;
|
||||
|
||||
pa_test.set_speed(fast_speed, slow_speed);
|
||||
pa_test.draw_numbers() = print.calib_params().print_numbers;
|
||||
gcode += pa_test.generate_test(params.start, params.step, std::llround(std::ceil((params.end - params.start) / params.step)) + 1);
|
||||
|
|
|
@ -786,7 +786,7 @@ static std::vector<std::string> s_Preset_print_options {
|
|||
"independent_support_layer_height",
|
||||
"support_angle", "support_interface_top_layers", "support_interface_bottom_layers",
|
||||
"support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern",
|
||||
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges", "max_bridge_length", "print_sequence", "support_remove_small_overhang",
|
||||
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges", "max_bridge_length", "print_sequence", "support_remove_small_overhang",
|
||||
"filename_format", "wall_filament", "support_bottom_z_distance",
|
||||
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body",
|
||||
"ooze_prevention", "standby_temperature_delta", "interface_shells", "line_width", "initial_layer_line_width",
|
||||
|
|
|
@ -248,6 +248,13 @@ static t_config_enum_values s_keys_map_SeamPosition {
|
|||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SeamPosition)
|
||||
|
||||
static t_config_enum_values s_keys_map_InternalBridgeFilter {
|
||||
{ "disabled", ibfDisabled },
|
||||
{ "limited", ibfLimited },
|
||||
{ "nofilter", ibfNofilter },
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InternalBridgeFilter)
|
||||
|
||||
static const t_config_enum_values s_keys_map_SLADisplayOrientation = {
|
||||
{ "landscape", sladoLandscape},
|
||||
{ "portrait", sladoPortrait}
|
||||
|
@ -1220,6 +1227,32 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
def = this->add("dont_filter_internal_bridges", coEnum);
|
||||
def->label = L("Don't filter out small internal bridges (experimental)");
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("This option can help reducing pillowing on top surfaces in heavily slanted or curved models.\n\n"
|
||||
"By default, small internal bridges are filtered out and the internal solid infill is printed directly"
|
||||
" over the sparse infill. This works well in most cases, speeding up printing without too much compromise"
|
||||
" on top surface quality. \n\nHowever, in heavily slanted or curved models especially where too low sparse"
|
||||
" infill density is used, this may result in curling of the unsupported solid infill, causing pillowing.\n\n"
|
||||
"Enabling this option will print internal bridge layer over slightly unsupported internal"
|
||||
" solid infill. The options below control the amount of filtering, i.e. the amount of internal bridges "
|
||||
"created.\n\n"
|
||||
"Disabled - Disables this option. This is the default behaviour and works well in most cases.\n\n"
|
||||
"Limited filtering - Creates internal bridges on heavily slanted surfaces, while avoiding creating "
|
||||
"uncessesary interal bridges. This works well for most difficult models.\n\n"
|
||||
"No filtering - Creates internal bridges on every potential internal overhang. This option is useful "
|
||||
"for heavily slanted top surface models. However, in most cases it creates too many unecessary bridges.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<InternalBridgeFilter>::get_enum_values();
|
||||
def->enum_values.push_back("disabled");
|
||||
def->enum_values.push_back("limited");
|
||||
def->enum_values.push_back("nofilter");
|
||||
def->enum_labels.push_back(L("Disabled"));
|
||||
def->enum_labels.push_back(L("Limited filtering"));
|
||||
def->enum_labels.push_back(L("No filtering"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnum<InternalBridgeFilter>(ibfDisabled));
|
||||
|
||||
|
||||
def = this->add("max_bridge_length", coFloat);
|
||||
def->label = L("Max bridge length");
|
||||
|
|
|
@ -152,6 +152,10 @@ enum SeamPosition {
|
|||
spNearest, spAligned, spRear, spRandom
|
||||
};
|
||||
|
||||
enum InternalBridgeFilter {
|
||||
ibfDisabled, ibfLimited, ibfNofilter
|
||||
};
|
||||
|
||||
enum LiftType {
|
||||
NormalLift,
|
||||
SpiralLift,
|
||||
|
@ -744,6 +748,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
// Orca internal thick bridge
|
||||
((ConfigOptionBool, thick_bridges))
|
||||
((ConfigOptionBool, thick_internal_bridges))
|
||||
((ConfigOptionEnum<InternalBridgeFilter>, dont_filter_internal_bridges))
|
||||
// Overhang angle threshold.
|
||||
((ConfigOptionInt, support_threshold_angle))
|
||||
((ConfigOptionFloat, support_object_xy_distance))
|
||||
|
|
|
@ -1969,7 +1969,6 @@ template<typename T> void debug_draw(std::string name, const T& a, const T& b, c
|
|||
void PrintObject::bridge_over_infill()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Start" << log_memory_info();
|
||||
|
||||
struct CandidateSurface
|
||||
{
|
||||
CandidateSurface(const Surface *original_surface,
|
||||
|
@ -1991,12 +1990,20 @@ void PrintObject::bridge_over_infill()
|
|||
};
|
||||
|
||||
std::map<size_t, std::vector<CandidateSurface>> surfaces_by_layer;
|
||||
// Orca:
|
||||
// Detect use of lightning infill. Moved earlier in the function to pass to the gather and filter surfaces threads.
|
||||
bool has_lightning_infill = false;
|
||||
for (size_t i = 0; i < this->num_printing_regions(); i++) {
|
||||
if (this->printing_region(i).config().sparse_infill_pattern == ipLightning) {
|
||||
has_lightning_infill = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// SECTION to gather and filter surfaces for expanding, and then cluster them by layer
|
||||
{
|
||||
tbb::concurrent_vector<CandidateSurface> candidate_surfaces;
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = static_cast<const PrintObject *>(this),
|
||||
&candidate_surfaces](tbb::blocked_range<size_t> r) {
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = static_cast<const PrintObject *>(this), &candidate_surfaces, has_lightning_infill](tbb::blocked_range<size_t> r) {
|
||||
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
|
||||
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
|
||||
const Layer *layer = po->get_layer(lidx);
|
||||
|
@ -2019,44 +2026,60 @@ void PrintObject::bridge_over_infill()
|
|||
}
|
||||
}
|
||||
unsupported_area = closing(unsupported_area, float(SCALED_EPSILON));
|
||||
|
||||
// Orca:
|
||||
// Lightning infill benefits from always having a bridge layer so don't filter out small unsupported areas. Also, don't filter small internal unsupported areas if the user has requested so.
|
||||
double expansion_multiplier = 3;
|
||||
if(has_lightning_infill || po->config().dont_filter_internal_bridges.value !=ibfDisabled){
|
||||
expansion_multiplier = 1;
|
||||
}
|
||||
// By expanding the lower layer solids, we avoid making bridges from the tiny internal overhangs that are (very likely) supported by previous layer solids
|
||||
// NOTE that we cannot filter out polygons worth bridging by their area, because sometimes there is a very small internal island that will grow into large hole
|
||||
lower_layer_solids = shrink(lower_layer_solids, 1 * spacing); // first remove thin regions that will not support anything
|
||||
lower_layer_solids = expand(lower_layer_solids, (1 + 3) * spacing); // then expand back (opening), and further for parts supported by internal solids
|
||||
lower_layer_solids = expand(lower_layer_solids, (1 + expansion_multiplier) * spacing); // then expand back (opening), and further for parts supported by internal solids
|
||||
// By shrinking the unsupported area, we avoid making bridges from narrow ensuring region along perimeters.
|
||||
unsupported_area = shrink(unsupported_area, 3 * spacing);
|
||||
unsupported_area = shrink(unsupported_area, expansion_multiplier * spacing);
|
||||
unsupported_area = diff(unsupported_area, lower_layer_solids);
|
||||
|
||||
|
||||
for (const LayerRegion *region : layer->regions()) {
|
||||
SurfacesPtr region_internal_solids = region->fill_surfaces.filter_by_type(stInternalSolid);
|
||||
for (const Surface *s : region_internal_solids) {
|
||||
Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area);
|
||||
// The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported.
|
||||
// These regions can be filtered by area, because they for sure are touching solids on lower layers, and it does not make sense to bridge their tiny overhangs
|
||||
bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON;
|
||||
if (!unsupported.empty() && (!partially_supported || area(unsupported) > 3 * 3 * spacing * spacing)) {
|
||||
Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 4 * spacing));
|
||||
// after we extracted the part worth briding, we go over the leftovers and merge the tiny ones back, to not brake the surface too much
|
||||
for (const Polygon& p : diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))) {
|
||||
double area = p.area();
|
||||
if (area < spacing * scale_(12.0) && area > spacing * spacing) {
|
||||
worth_bridging.push_back(p);
|
||||
|
||||
// Orca: If the user has selected to always support internal overhanging regions, no matter how small
|
||||
// skip the filtering
|
||||
if (po->config().dont_filter_internal_bridges.value == ibfNofilter){
|
||||
// expand the unsupported area by 4x spacing to trigger internal bridging
|
||||
unsupported = expand(unsupported, 4 * spacing);
|
||||
candidate_surfaces.push_back(CandidateSurface(s, lidx, unsupported, region, 0));
|
||||
}else{
|
||||
// The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported.
|
||||
// These regions can be filtered by area, because they for sure are touching solids on lower layers, and it does not make sense to bridge their tiny overhangs
|
||||
bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON;
|
||||
if (!unsupported.empty() && (!partially_supported || area(unsupported) > 3 * 3 * spacing * spacing)) {
|
||||
Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 4 * spacing));
|
||||
// after we extracted the part worth briding, we go over the leftovers and merge the tiny ones back, to not brake the surface too much
|
||||
for (const Polygon& p : diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))) {
|
||||
double area = p.area();
|
||||
if (area < spacing * scale_(12.0) && area > spacing * spacing) {
|
||||
worth_bridging.push_back(p);
|
||||
}
|
||||
}
|
||||
worth_bridging = intersection(closing(worth_bridging, float(SCALED_EPSILON)), s->expolygon);
|
||||
candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0));
|
||||
|
||||
#ifdef DEBUG_BRIDGE_OVER_INFILL
|
||||
debug_draw(std::to_string(lidx) + "_candidate_surface_" + std::to_string(area(s->expolygon)),
|
||||
to_lines(region->layer()->lslices), to_lines(s->expolygon), to_lines(worth_bridging),
|
||||
to_lines(unsupported_area));
|
||||
#endif
|
||||
#ifdef DEBUG_BRIDGE_OVER_INFILL
|
||||
debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(unsupported)),
|
||||
to_lines(unsupported), to_lines(intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing))),
|
||||
to_lines(diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))),
|
||||
to_lines(unsupported_area));
|
||||
#endif
|
||||
}
|
||||
worth_bridging = intersection(closing(worth_bridging, float(SCALED_EPSILON)), s->expolygon);
|
||||
candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0));
|
||||
|
||||
#ifdef DEBUG_BRIDGE_OVER_INFILL
|
||||
debug_draw(std::to_string(lidx) + "_candidate_surface_" + std::to_string(area(s->expolygon)),
|
||||
to_lines(region->layer()->lslices), to_lines(s->expolygon), to_lines(worth_bridging),
|
||||
to_lines(unsupported_area));
|
||||
#endif
|
||||
#ifdef DEBUG_BRIDGE_OVER_INFILL
|
||||
debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(unsupported)),
|
||||
to_lines(unsupported), to_lines(intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing))),
|
||||
to_lines(diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))),
|
||||
to_lines(unsupported_area));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2071,13 +2094,6 @@ void PrintObject::bridge_over_infill()
|
|||
// LIGHTNING INFILL SECTION - If lightning infill is used somewhere, we check the areas that are going to be bridges, and those that rely on the
|
||||
// lightning infill under them get expanded. This somewhat helps to ensure that most of the extrusions are anchored to the lightning infill at the ends.
|
||||
// It requires modifying this instance of print object in a specific way, so that we do not invalidate the pointers in our surfaces_by_layer structure.
|
||||
bool has_lightning_infill = false;
|
||||
for (size_t i = 0; i < this->num_printing_regions(); i++) {
|
||||
if (this->printing_region(i).config().sparse_infill_pattern == ipLightning) {
|
||||
has_lightning_infill = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (has_lightning_infill) {
|
||||
// Prepare backup data for the Layer Region infills. Before modfiyng the layer region, we backup its fill surfaces by moving! them into this map.
|
||||
// then a copy is created, modifiyed and passed to lightning infill generator. After generator is created, we restore the original state of the fills
|
||||
|
|
|
@ -424,6 +424,13 @@ std::string CalibPressureAdvance::draw_box(GCodeWriter &writer, double min_x, do
|
|||
|
||||
return gcode.str();
|
||||
}
|
||||
CalibPressureAdvanceLine::CalibPressureAdvanceLine(GCode* gcodegen)
|
||||
: CalibPressureAdvance(gcodegen->config()), mp_gcodegen(gcodegen), m_nozzle_diameter(gcodegen->config().nozzle_diameter.get_at(0))
|
||||
{
|
||||
m_line_width = m_nozzle_diameter < 0.51 ? m_nozzle_diameter * 1.5 : m_nozzle_diameter * 1.05;
|
||||
m_height_layer = gcodegen->config().initial_layer_print_height;
|
||||
m_number_line_width = m_thin_line_width = m_nozzle_diameter;
|
||||
};
|
||||
|
||||
std::string CalibPressureAdvanceLine::generate_test(double start_pa /*= 0*/, double step_pa /*= 0.002*/, int count /*= 10*/)
|
||||
{
|
||||
|
@ -496,14 +503,15 @@ std::string CalibPressureAdvanceLine::print_pa_lines(double start_x, double star
|
|||
// gcode << move_to(Vec2d(start_x + m_length_short + m_length_long, y_pos + (num - 1) * m_space_y + 7), writer);
|
||||
// gcode << writer.extrude_to_xy(Vec2d(start_x + m_length_short + m_length_long, y_pos + (num - 1) * m_space_y + 2), thin_e_per_mm * 7);
|
||||
|
||||
DrawBoxOptArgs default_box_opt_args(2, m_height_layer, 0.6, fast);
|
||||
const auto box_start_x = start_x + m_length_short + m_length_long + m_length_short;
|
||||
DrawBoxOptArgs default_box_opt_args(2, m_height_layer, m_line_width, fast);
|
||||
default_box_opt_args.is_filled = true;
|
||||
gcode << draw_box(writer, start_x + m_length_short + m_length_long + m_length_short, start_y - m_space_y, number_spacing() * 8,
|
||||
(num + 1) * m_space_y, default_box_opt_args);
|
||||
gcode << draw_box(writer, box_start_x, start_y - m_space_y,
|
||||
number_spacing() * 8, (num + 1) * m_space_y, default_box_opt_args);
|
||||
gcode << writer.travel_to_z(m_height_layer*2);
|
||||
for (int i = 0; i < num; i += 2) {
|
||||
gcode << draw_number(start_x + m_length_short + m_length_long + m_length_short + 3, y_pos + i * m_space_y + m_space_y / 2,
|
||||
start_pa + i * step_pa, m_draw_digit_mode, m_number_line_width, number_e_per_mm, 3600, writer);
|
||||
gcode << draw_number(box_start_x + 3 + m_line_width, y_pos + i * m_space_y + m_space_y / 2, start_pa + i * step_pa, m_draw_digit_mode,
|
||||
m_number_line_width, number_e_per_mm, 3600, writer);
|
||||
}
|
||||
}
|
||||
return gcode.str();
|
||||
|
|
|
@ -187,7 +187,7 @@ protected:
|
|||
class CalibPressureAdvanceLine : public CalibPressureAdvance
|
||||
{
|
||||
public:
|
||||
CalibPressureAdvanceLine(GCode *gcodegen) : CalibPressureAdvance(gcodegen->config()), mp_gcodegen(gcodegen),m_nozzle_diameter(gcodegen->config().nozzle_diameter.get_at(0)){};
|
||||
CalibPressureAdvanceLine(GCode* gcodegen);
|
||||
~CalibPressureAdvanceLine(){};
|
||||
|
||||
std::string generate_test(double start_pa = 0, double step_pa = 0.002, int count = 50);
|
||||
|
@ -199,6 +199,7 @@ public:
|
|||
}
|
||||
|
||||
const double &line_width() { return m_line_width; };
|
||||
const double &height_layer() { return m_height_layer; };
|
||||
bool is_delta() const;
|
||||
bool &draw_numbers() { return m_draw_numbers; }
|
||||
|
||||
|
@ -212,10 +213,10 @@ private:
|
|||
double m_nozzle_diameter;
|
||||
double m_slow_speed, m_fast_speed;
|
||||
|
||||
const double m_height_layer{0.2};
|
||||
const double m_line_width{0.6};
|
||||
const double m_thin_line_width{0.44};
|
||||
const double m_number_line_width{0.48};
|
||||
double m_height_layer{0.2};
|
||||
double m_line_width{0.6};
|
||||
double m_thin_line_width{0.44};
|
||||
double m_number_line_width{0.48};
|
||||
const double m_space_y{3.5};
|
||||
|
||||
double m_length_short{20.0}, m_length_long{40.0};
|
||||
|
|
|
@ -23,6 +23,7 @@ wxEND_EVENT_TABLE()
|
|||
|
||||
wxDEFINE_EVENT(EVT_VCAMERA_SWITCH, wxMouseEvent);
|
||||
wxDEFINE_EVENT(EVT_SDCARD_ABSENT_HINT, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_CAM_SOURCE_CHANGE, wxCommandEvent);
|
||||
|
||||
#define CAMERAPOPUP_CLICK_INTERVAL 20
|
||||
|
||||
|
@ -78,6 +79,34 @@ CameraPopup::CameraPopup(wxWindow *parent)
|
|||
top_sizer->Add(0, 0, wxALL, 0);
|
||||
}
|
||||
|
||||
// custom IP camera
|
||||
m_custom_camera_input_confirm = new Button(m_panel, _L("Enable"));
|
||||
m_custom_camera_input_confirm->SetBackgroundColor(wxColour(38, 166, 154));
|
||||
m_custom_camera_input_confirm->SetBorderColor(wxColour(38, 166, 154));
|
||||
m_custom_camera_input_confirm->SetTextColor(wxColour(0xFFFFFE));
|
||||
m_custom_camera_input_confirm->SetFont(Label::Body_14);
|
||||
m_custom_camera_input_confirm->SetMinSize(wxSize(FromDIP(90), FromDIP(30)));
|
||||
m_custom_camera_input_confirm->SetPosition(wxDefaultPosition);
|
||||
m_custom_camera_input_confirm->SetCornerRadius(FromDIP(12));
|
||||
m_custom_camera_input = new TextInput(m_panel, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, wxDefaultSize);
|
||||
m_custom_camera_input->GetTextCtrl()->SetHint(_L("Hostname or IP"));
|
||||
m_custom_camera_input->GetTextCtrl()->SetFont(Label::Body_14);
|
||||
m_custom_camera_hint = new wxStaticText(m_panel, wxID_ANY, _L("Custom camera source"));
|
||||
m_custom_camera_hint->Wrap(-1);
|
||||
m_custom_camera_hint->SetFont(Label::Head_14);
|
||||
m_custom_camera_hint->SetForegroundColour(TEXT_COL);
|
||||
|
||||
m_custom_camera_input_confirm->Bind(wxEVT_BUTTON, &CameraPopup::on_camera_source_changed, this);
|
||||
|
||||
if (!wxGetApp().app_config->get("camera", "custom_source").empty()) {
|
||||
m_custom_camera_input->GetTextCtrl()->SetValue(wxGetApp().app_config->get("camera", "custom_source"));
|
||||
set_custom_cam_button_state(wxGetApp().app_config->get("camera", "enable_custom_source") == "true");
|
||||
}
|
||||
|
||||
top_sizer->Add(m_custom_camera_hint, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, FromDIP(5));
|
||||
top_sizer->Add(0, 0, wxALL, 0);
|
||||
top_sizer->Add(m_custom_camera_input, 2, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxEXPAND | wxALL, FromDIP(5));
|
||||
top_sizer->Add(m_custom_camera_input_confirm, 1, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, FromDIP(5));
|
||||
main_sizer->Add(top_sizer, 0, wxALL, FromDIP(10));
|
||||
|
||||
auto url = wxString::Format(L"https://wiki.bambulab.com/%s/software/bambu-studio/virtual-camera", L"en");
|
||||
|
@ -132,6 +161,37 @@ void CameraPopup::sdcard_absent_hint()
|
|||
GetEventHandler()->ProcessEvent(evt);
|
||||
}
|
||||
|
||||
void CameraPopup::on_camera_source_changed(wxCommandEvent &event)
|
||||
{
|
||||
if (m_obj && !m_custom_camera_input->GetTextCtrl()->IsEmpty()) {
|
||||
handle_camera_source_change();
|
||||
}
|
||||
}
|
||||
|
||||
void CameraPopup::handle_camera_source_change()
|
||||
{
|
||||
m_custom_camera_enabled = !m_custom_camera_enabled;
|
||||
|
||||
set_custom_cam_button_state(m_custom_camera_enabled);
|
||||
|
||||
wxGetApp().app_config->set("camera", "custom_source", m_custom_camera_input->GetTextCtrl()->GetValue().ToStdString());
|
||||
wxGetApp().app_config->set("camera", "enable_custom_source", m_custom_camera_enabled);
|
||||
|
||||
wxCommandEvent evt(EVT_CAM_SOURCE_CHANGE);
|
||||
evt.SetEventObject(this);
|
||||
GetEventHandler()->ProcessEvent(evt);
|
||||
}
|
||||
|
||||
void CameraPopup::set_custom_cam_button_state(bool state)
|
||||
{
|
||||
m_custom_camera_enabled = state;
|
||||
auto stateColour = state ? wxColour(170, 0, 0) : wxColour(38, 166, 154);
|
||||
auto stateText = state ? "Disable" : "Enable";
|
||||
m_custom_camera_input_confirm->SetBackgroundColor(stateColour);
|
||||
m_custom_camera_input_confirm->SetBorderColor(stateColour);
|
||||
m_custom_camera_input_confirm->SetLabel(_L(stateText));
|
||||
}
|
||||
|
||||
void CameraPopup::on_switch_recording(wxCommandEvent& event)
|
||||
{
|
||||
if (!m_obj) return;
|
||||
|
|
|
@ -14,12 +14,14 @@
|
|||
#include "Widgets/SwitchButton.hpp"
|
||||
#include "Widgets/RadioBox.hpp"
|
||||
#include "Widgets/PopupWindow.hpp"
|
||||
#include "Widgets/TextInput.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
wxDECLARE_EVENT(EVT_VCAMERA_SWITCH, wxMouseEvent);
|
||||
wxDECLARE_EVENT(EVT_SDCARD_ABSENT_HINT, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_CAM_SOURCE_CHANGE, wxCommandEvent);
|
||||
|
||||
class CameraPopup : public PopupWindow
|
||||
{
|
||||
|
@ -50,6 +52,9 @@ protected:
|
|||
void on_switch_recording(wxCommandEvent& event);
|
||||
void on_set_resolution();
|
||||
void sdcard_absent_hint();
|
||||
void on_camera_source_changed(wxCommandEvent& event);
|
||||
void handle_camera_source_change();
|
||||
void set_custom_cam_button_state(bool state);
|
||||
|
||||
wxWindow * create_item_radiobox(wxString title, wxWindow *parent, wxString tooltip, int padding_left);
|
||||
void select_curr_radiobox(int btn_idx);
|
||||
|
@ -66,6 +71,10 @@ private:
|
|||
SwitchButton* m_switch_recording;
|
||||
wxStaticText* m_text_vcamera;
|
||||
SwitchButton* m_switch_vcamera;
|
||||
wxStaticText* m_custom_camera_hint;
|
||||
TextInput* m_custom_camera_input;
|
||||
Button* m_custom_camera_input_confirm;
|
||||
bool m_custom_camera_enabled{ false };
|
||||
wxStaticText* m_text_resolution;
|
||||
wxWindow* m_resolution_options[RESOLUTION_OPTIONS_NUM];
|
||||
wxScrolledWindow *m_panel;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/StepCtrl.hpp"
|
||||
#include "Widgets/SideTools.hpp"
|
||||
#include "Widgets/WebView.hpp"
|
||||
|
||||
#include "BitmapCache.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
@ -889,6 +890,11 @@ StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint
|
|||
StatusBasePanel::~StatusBasePanel()
|
||||
{
|
||||
delete m_media_play_ctrl;
|
||||
|
||||
if (m_custom_camera_view) {
|
||||
delete m_custom_camera_view;
|
||||
m_custom_camera_view = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void StatusBasePanel::init_bitmaps()
|
||||
|
@ -922,6 +928,7 @@ void StatusBasePanel::init_bitmaps()
|
|||
m_bitmap_timelapse_off = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_timelapse_off_dark" : "monitor_timelapse_off", 20);
|
||||
m_bitmap_vcamera_on = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_vcamera_on_dark" : "monitor_vcamera_on", 20);
|
||||
m_bitmap_vcamera_off = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_vcamera_off_dark" : "monitor_vcamera_off", 20);
|
||||
m_bitmap_switch_camera = ScalableBitmap(this, wxGetApp().dark_mode() ? "camera_switch_dark" : "camera_switch", 20);
|
||||
|
||||
}
|
||||
|
||||
|
@ -989,12 +996,27 @@ wxBoxSizer *StatusBasePanel::create_monitoring_page()
|
|||
m_setting_button->SetMinSize(wxSize(FromDIP(38), FromDIP(24)));
|
||||
m_setting_button->SetBackgroundColour(STATUS_TITLE_BG);
|
||||
|
||||
m_camera_switch_button = new wxStaticBitmap(m_panel_monitoring_title, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(FromDIP(38), FromDIP(24)), 0);
|
||||
m_camera_switch_button->SetMinSize(wxSize(FromDIP(38), FromDIP(24)));
|
||||
m_camera_switch_button->SetBackgroundColour(STATUS_TITLE_BG);
|
||||
m_camera_switch_button->SetBitmap(m_bitmap_switch_camera.bmp());
|
||||
m_camera_switch_button->Bind(wxEVT_LEFT_DOWN, &StatusBasePanel::on_camera_switch_toggled, this);
|
||||
m_camera_switch_button->Bind(wxEVT_RIGHT_DOWN, [this](auto& e) {
|
||||
const std::string js_request_pip = R"(
|
||||
document.querySelector('video').requestPictureInPicture();
|
||||
)";
|
||||
m_custom_camera_view->RunScript(js_request_pip);
|
||||
});
|
||||
m_camera_switch_button->Hide();
|
||||
|
||||
m_bitmap_sdcard_img->SetToolTip(_L("SD Card"));
|
||||
m_bitmap_timelapse_img->SetToolTip(_L("Timelapse"));
|
||||
m_bitmap_recording_img->SetToolTip(_L("Video"));
|
||||
m_bitmap_vcamera_img->SetToolTip(_L("Go Live"));
|
||||
m_setting_button->SetToolTip(_L("Camera Setting"));
|
||||
m_camera_switch_button->SetToolTip(_L("Switch Camera View"));
|
||||
|
||||
bSizer_monitoring_title->Add(m_camera_switch_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5));
|
||||
bSizer_monitoring_title->Add(m_bitmap_sdcard_img, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5));
|
||||
bSizer_monitoring_title->Add(m_bitmap_timelapse_img, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5));
|
||||
bSizer_monitoring_title->Add(m_bitmap_recording_img, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5));
|
||||
|
@ -1014,17 +1036,44 @@ wxBoxSizer *StatusBasePanel::create_monitoring_page()
|
|||
m_media_ctrl = new wxMediaCtrl2(this);
|
||||
m_media_ctrl->SetMinSize(wxSize(PAGE_MIN_WIDTH, FromDIP(288)));
|
||||
|
||||
m_custom_camera_view = WebView::CreateWebView(this, wxEmptyString);
|
||||
m_custom_camera_view->EnableContextMenu(false);
|
||||
Bind(wxEVT_WEBVIEW_NAVIGATING, &StatusBasePanel::on_webview_navigating, this, m_custom_camera_view->GetId());
|
||||
|
||||
m_media_play_ctrl = new MediaPlayCtrl(this, m_media_ctrl, wxDefaultPosition, wxSize(-1, FromDIP(40)));
|
||||
m_custom_camera_view->Hide();
|
||||
m_custom_camera_view->Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, [this](wxWebViewEvent& evt) {
|
||||
if (evt.GetString() == "leavepictureinpicture") {
|
||||
// When leaving PiP, video gets paused in some cases and toggling play
|
||||
// programmatically does not work.
|
||||
m_custom_camera_view->Reload();
|
||||
}
|
||||
else if (evt.GetString() == "enterpictureinpicture") {
|
||||
toggle_builtin_camera();
|
||||
}
|
||||
});
|
||||
|
||||
sizer->Add(m_media_ctrl, 1, wxEXPAND | wxALL, 0);
|
||||
sizer->Add(m_custom_camera_view, 1, wxEXPAND | wxALL, 0);
|
||||
sizer->Add(m_media_play_ctrl, 0, wxEXPAND | wxALL, 0);
|
||||
// media_ctrl_panel->SetSizer(bSizer_monitoring);
|
||||
// media_ctrl_panel->Layout();
|
||||
//
|
||||
// sizer->Add(media_ctrl_panel, 1, wxEXPAND | wxALL, 1);
|
||||
|
||||
if (wxGetApp().app_config->get("camera", "enable_custom_source") == "true") {
|
||||
handle_camera_source_change();
|
||||
}
|
||||
|
||||
return sizer;
|
||||
}
|
||||
|
||||
void StatusBasePanel::on_webview_navigating(wxWebViewEvent& evt) {
|
||||
wxGetApp().CallAfter([this] {
|
||||
remove_controls();
|
||||
});
|
||||
}
|
||||
|
||||
wxBoxSizer *StatusBasePanel::create_machine_control_page(wxWindow *parent)
|
||||
{
|
||||
wxBoxSizer *bSizer_right = new wxBoxSizer(wxVERTICAL);
|
||||
|
@ -3863,6 +3912,7 @@ void StatusPanel::on_camera_enter(wxMouseEvent& event)
|
|||
}
|
||||
sdcard_hint_dlg->on_show();
|
||||
});
|
||||
m_camera_popup->Bind(EVT_CAM_SOURCE_CHANGE, &StatusPanel::on_camera_source_change, this);
|
||||
wxWindow* ctrl = (wxWindow*)event.GetEventObject();
|
||||
wxPoint pos = ctrl->ClientToScreen(wxPoint(0, 0));
|
||||
wxSize sz = ctrl->GetSize();
|
||||
|
@ -3874,6 +3924,71 @@ void StatusPanel::on_camera_enter(wxMouseEvent& event)
|
|||
}
|
||||
}
|
||||
|
||||
void StatusBasePanel::on_camera_source_change(wxCommandEvent& event)
|
||||
{
|
||||
handle_camera_source_change();
|
||||
}
|
||||
|
||||
void StatusBasePanel::handle_camera_source_change()
|
||||
{
|
||||
const auto new_cam_url = wxGetApp().app_config->get("camera", "custom_source");
|
||||
const auto enabled = wxGetApp().app_config->get("camera", "enable_custom_source") == "true";
|
||||
|
||||
if (enabled && !new_cam_url.empty()) {
|
||||
m_custom_camera_view->LoadURL(new_cam_url);
|
||||
toggle_custom_camera();
|
||||
m_camera_switch_button->Show();
|
||||
} else {
|
||||
toggle_builtin_camera();
|
||||
m_camera_switch_button->Hide();
|
||||
}
|
||||
}
|
||||
|
||||
void StatusBasePanel::toggle_builtin_camera()
|
||||
{
|
||||
m_custom_camera_view->Hide();
|
||||
m_media_ctrl->Show();
|
||||
m_media_play_ctrl->Show();
|
||||
}
|
||||
|
||||
void StatusBasePanel::toggle_custom_camera()
|
||||
{
|
||||
const auto enabled = wxGetApp().app_config->get("camera", "enable_custom_source") == "true";
|
||||
|
||||
if (enabled) {
|
||||
m_custom_camera_view->Show();
|
||||
m_media_ctrl->Hide();
|
||||
m_media_play_ctrl->Hide();
|
||||
}
|
||||
}
|
||||
|
||||
void StatusBasePanel::on_camera_switch_toggled(wxMouseEvent& event)
|
||||
{
|
||||
const auto enabled = wxGetApp().app_config->get("camera", "enable_custom_source") == "true";
|
||||
if (enabled && m_media_ctrl->IsShown()) {
|
||||
toggle_custom_camera();
|
||||
} else {
|
||||
toggle_builtin_camera();
|
||||
}
|
||||
}
|
||||
|
||||
void StatusBasePanel::remove_controls()
|
||||
{
|
||||
const std::string js_cleanup_video_element = R"(
|
||||
document.body.style.overflow='hidden';
|
||||
const video = document.querySelector('video');
|
||||
video.setAttribute('style', 'width: 100% !important;');
|
||||
video.removeAttribute('controls');
|
||||
video.addEventListener('leavepictureinpicture', () => {
|
||||
window.wx.postMessage('leavepictureinpicture');
|
||||
});
|
||||
video.addEventListener('enterpictureinpicture', () => {
|
||||
window.wx.postMessage('enterpictureinpicture');
|
||||
});
|
||||
)";
|
||||
m_custom_camera_view->RunScript(js_cleanup_video_element);
|
||||
}
|
||||
|
||||
void StatusPanel::on_camera_leave(wxMouseEvent& event)
|
||||
{
|
||||
if (obj && m_camera_popup) {
|
||||
|
|
|
@ -287,6 +287,7 @@ protected:
|
|||
ScalableBitmap m_bitmap_timelapse_off;
|
||||
ScalableBitmap m_bitmap_vcamera_on;
|
||||
ScalableBitmap m_bitmap_vcamera_off;
|
||||
ScalableBitmap m_bitmap_switch_camera;
|
||||
|
||||
/* title panel */
|
||||
wxPanel * media_ctrl_panel;
|
||||
|
@ -307,6 +308,7 @@ protected:
|
|||
wxStaticBitmap *m_bitmap_sdcard_img;
|
||||
wxStaticBitmap *m_bitmap_static_use_time;
|
||||
wxStaticBitmap *m_bitmap_static_use_weight;
|
||||
wxStaticBitmap* m_camera_switch_button;
|
||||
|
||||
|
||||
wxMediaCtrl2 * m_media_ctrl;
|
||||
|
@ -326,6 +328,7 @@ protected:
|
|||
ScalableButton *m_button_pause_resume;
|
||||
ScalableButton *m_button_abort;
|
||||
Button * m_button_clean;
|
||||
wxWebView * m_custom_camera_view{nullptr};
|
||||
|
||||
wxStaticText * m_text_tasklist_caption;
|
||||
|
||||
|
@ -410,6 +413,13 @@ protected:
|
|||
virtual void on_axis_ctrl_z_down_10(wxCommandEvent &event) { event.Skip(); }
|
||||
virtual void on_axis_ctrl_e_up_10(wxCommandEvent &event) { event.Skip(); }
|
||||
virtual void on_axis_ctrl_e_down_10(wxCommandEvent &event) { event.Skip(); }
|
||||
void on_camera_source_change(wxCommandEvent& event);
|
||||
void handle_camera_source_change();
|
||||
void remove_controls();
|
||||
void on_webview_navigating(wxWebViewEvent& evt);
|
||||
void on_camera_switch_toggled(wxMouseEvent& event);
|
||||
void toggle_custom_camera();
|
||||
void toggle_builtin_camera();
|
||||
|
||||
public:
|
||||
StatusBasePanel(wxWindow * parent,
|
||||
|
|
|
@ -1995,6 +1995,7 @@ void TabPrint::build()
|
|||
optgroup->append_single_option_line("bridge_density");
|
||||
optgroup->append_single_option_line("thick_bridges");
|
||||
optgroup->append_single_option_line("thick_internal_bridges");
|
||||
optgroup->append_single_option_line("dont_filter_internal_bridges");
|
||||
|
||||
optgroup = page->new_optgroup(L("Overhangs"), L"param_advanced");
|
||||
optgroup->append_single_option_line("detect_overhang_wall");
|
||||
|
|
Loading…
Reference in a new issue