mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-15 04:57:34 +00:00
Compare commits
7 Commits
hanif/cli-
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efc9f253ee | ||
|
|
292cf0095e | ||
|
|
5c635d5e50 | ||
|
|
5496883493 | ||
|
|
70247ad298 | ||
|
|
ecbe1b1b90 | ||
|
|
ffb4f192c1 |
@@ -587,10 +587,15 @@ if ((NOT MSVC OR IS_CLANG_CL) AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR
|
|||||||
add_compile_options(-Wno-${w})
|
add_compile_options(-Wno-${w})
|
||||||
endforeach ()
|
endforeach ()
|
||||||
|
|
||||||
# Turn everything else into an error. Dependency headers are exempt because the SYSTEM
|
# GCC is not built in CI, so don't throw errors CI won't catch.
|
||||||
# include flag (-imsvc on clang-cl, -isystem elsewhere) keeps their diagnostics out,
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
# apart from GCC's maybe-uninitialized, demoted below.
|
add_compile_options(-Werror=return-type)
|
||||||
add_compile_options(-Werror)
|
else ()
|
||||||
|
# Turn everything else into an error. Dependency headers are exempt because the
|
||||||
|
# SYSTEM include flag (-imsvc on clang-cl, -isystem elsewhere) keeps their
|
||||||
|
# diagnostics out.
|
||||||
|
add_compile_options(-Werror)
|
||||||
|
endif ()
|
||||||
|
|
||||||
# Demoted. Remove a name once its category is cleared on every compiler.
|
# Demoted. Remove a name once its category is cleared on every compiler.
|
||||||
set(warnings_demoted)
|
set(warnings_demoted)
|
||||||
@@ -612,20 +617,6 @@ if ((NOT MSVC OR IS_CLANG_CL) AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR
|
|||||||
cast-function-type-mismatch
|
cast-function-type-mismatch
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
||||||
list(APPEND warnings_demoted
|
|
||||||
# maybe-uninitialized runs after inlining and reports inside boost/variant,
|
|
||||||
# boost/tuple and the bundled clipper header even with -isystem.
|
|
||||||
maybe-uninitialized
|
|
||||||
|
|
||||||
# array-bounds is reported once, where ConfigOptionVector::set_at inlines
|
|
||||||
# into OrcaSlicer.cpp on a branch the preceding type test rules out.
|
|
||||||
array-bounds
|
|
||||||
|
|
||||||
# template-id-cdtor is a GCC 14+ warning in the bundled Clipper2 headers.
|
|
||||||
template-id-cdtor
|
|
||||||
)
|
|
||||||
endif ()
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
list(APPEND warnings_demoted
|
list(APPEND warnings_demoted
|
||||||
# enum-constexpr-conversion is a Clang warning that defaults to an error,
|
# enum-constexpr-conversion is a Clang warning that defaults to an error,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Snapmaker",
|
"name": "Snapmaker",
|
||||||
"version": "02.04.00.12",
|
"version": "02.04.00.13",
|
||||||
"force_update": "0",
|
"force_update": "0",
|
||||||
"description": "Snapmaker configurations",
|
"description": "Snapmaker configurations",
|
||||||
"machine_model_list": [
|
"machine_model_list": [
|
||||||
|
|||||||
@@ -15,13 +15,13 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"cool_plate_temp": [
|
"cool_plate_temp": [
|
||||||
"105"
|
"100"
|
||||||
],
|
],
|
||||||
"cool_plate_temp_initial_layer": [
|
"cool_plate_temp_initial_layer": [
|
||||||
"105"
|
"100"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
"105"
|
"100"
|
||||||
],
|
],
|
||||||
"eng_plate_temp_initial_layer": [
|
"eng_plate_temp_initial_layer": [
|
||||||
"100"
|
"100"
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
"Polymaker"
|
"Polymaker"
|
||||||
],
|
],
|
||||||
"hot_plate_temp": [
|
"hot_plate_temp": [
|
||||||
"105"
|
"100"
|
||||||
],
|
],
|
||||||
"hot_plate_temp_initial_layer": [
|
"hot_plate_temp_initial_layer": [
|
||||||
"100"
|
"100"
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
"110.8"
|
"110.8"
|
||||||
],
|
],
|
||||||
"textured_plate_temp": [
|
"textured_plate_temp": [
|
||||||
"105"
|
"100"
|
||||||
],
|
],
|
||||||
"textured_plate_temp_initial_layer": [
|
"textured_plate_temp_initial_layer": [
|
||||||
"100"
|
"100"
|
||||||
|
|||||||
@@ -15,16 +15,16 @@
|
|||||||
"1"
|
"1"
|
||||||
],
|
],
|
||||||
"cool_plate_temp": [
|
"cool_plate_temp": [
|
||||||
"105"
|
"100"
|
||||||
],
|
],
|
||||||
"cool_plate_temp_initial_layer": [
|
"cool_plate_temp_initial_layer": [
|
||||||
"105"
|
"100"
|
||||||
],
|
],
|
||||||
"eng_plate_temp": [
|
"eng_plate_temp": [
|
||||||
"105"
|
"100"
|
||||||
],
|
],
|
||||||
"eng_plate_temp_initial_layer": [
|
"eng_plate_temp_initial_layer": [
|
||||||
"105"
|
"100"
|
||||||
],
|
],
|
||||||
"fan_cooling_layer_time": [
|
"fan_cooling_layer_time": [
|
||||||
"12"
|
"12"
|
||||||
@@ -51,10 +51,10 @@
|
|||||||
"Polymaker"
|
"Polymaker"
|
||||||
],
|
],
|
||||||
"hot_plate_temp": [
|
"hot_plate_temp": [
|
||||||
"105"
|
"100"
|
||||||
],
|
],
|
||||||
"hot_plate_temp_initial_layer": [
|
"hot_plate_temp_initial_layer": [
|
||||||
"105"
|
"100"
|
||||||
],
|
],
|
||||||
"nozzle_temperature": [
|
"nozzle_temperature": [
|
||||||
"300"
|
"300"
|
||||||
@@ -81,10 +81,10 @@
|
|||||||
"110"
|
"110"
|
||||||
],
|
],
|
||||||
"textured_plate_temp": [
|
"textured_plate_temp": [
|
||||||
"105"
|
"100"
|
||||||
],
|
],
|
||||||
"textured_plate_temp_initial_layer": [
|
"textured_plate_temp_initial_layer": [
|
||||||
"105"
|
"100"
|
||||||
],
|
],
|
||||||
"filament_type": [
|
"filament_type": [
|
||||||
"ABS"
|
"ABS"
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
""
|
""
|
||||||
],
|
],
|
||||||
"hot_plate_temp": [
|
"hot_plate_temp": [
|
||||||
"110"
|
"100"
|
||||||
],
|
],
|
||||||
"hot_plate_temp_initial_layer": [
|
"hot_plate_temp_initial_layer": [
|
||||||
"105"
|
"100"
|
||||||
],
|
],
|
||||||
"overhang_fan_speed": [
|
"overhang_fan_speed": [
|
||||||
"20"
|
"20"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
""
|
""
|
||||||
],
|
],
|
||||||
"hot_plate_temp": [
|
"hot_plate_temp": [
|
||||||
"110"
|
"100"
|
||||||
],
|
],
|
||||||
"hot_plate_temp_initial_layer": [
|
"hot_plate_temp_initial_layer": [
|
||||||
"100"
|
"100"
|
||||||
|
|||||||
@@ -7715,6 +7715,13 @@ bool CLI::setup(int argc, char **argv)
|
|||||||
this->print_help();
|
this->print_help();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Orca: resolve here, while the process is still in the directory the user invoked it from.
|
||||||
|
// GUI_App's constructor moves the working directory to <data_dir>/log, long before the GUI
|
||||||
|
// opens these files in post_init(), and a relative path would then resolve against that.
|
||||||
|
for (std::string &input_file : m_input_files)
|
||||||
|
input_file = resolve_cli_input_path(input_file);
|
||||||
|
|
||||||
// Parse actions and transform options.
|
// Parse actions and transform options.
|
||||||
for (auto const &opt_key : opt_order) {
|
for (auto const &opt_key : opt_order) {
|
||||||
if (cli_actions_config_def.has(opt_key))
|
if (cli_actions_config_def.has(opt_key))
|
||||||
|
|||||||
@@ -1595,6 +1595,25 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
|
|||||||
return sparse_infill_polylines;
|
return sparse_infill_polylines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the filament id (1-based) the region is ironed with, or -1 when the
|
||||||
|
// region is not ironed. AllSolid always irons. TopSurfaces and TopmostOnly need
|
||||||
|
// either some top shells or, in spiral mode, more than one bottom shell, and
|
||||||
|
// TopmostOnly additionally needs the layer to be the topmost one.
|
||||||
|
int Layer::choose_ironing_extruder(const PrintRegionConfig &cfg,
|
||||||
|
bool spiral_mode,
|
||||||
|
bool is_topmost_layer)
|
||||||
|
{
|
||||||
|
if (cfg.ironing_type == IroningType::NoIroning)
|
||||||
|
return -1;
|
||||||
|
const bool gate = (cfg.ironing_type == IroningType::AllSolid)
|
||||||
|
|| ((cfg.top_shell_layers > 0 || (spiral_mode && cfg.bottom_shell_layers > 1))
|
||||||
|
&& (cfg.ironing_type == IroningType::TopSurfaces
|
||||||
|
|| (cfg.ironing_type == IroningType::TopmostOnly && is_topmost_layer)));
|
||||||
|
if (!gate)
|
||||||
|
return -1;
|
||||||
|
return cfg.top_surface_filament_id;
|
||||||
|
}
|
||||||
|
|
||||||
// Create ironing extrusions over top surfaces.
|
// Create ironing extrusions over top surfaces.
|
||||||
void Layer::make_ironing()
|
void Layer::make_ironing()
|
||||||
{
|
{
|
||||||
@@ -1664,19 +1683,10 @@ void Layer::make_ironing()
|
|||||||
if (! layerm->slices.empty()) {
|
if (! layerm->slices.empty()) {
|
||||||
IroningParams ironing_params;
|
IroningParams ironing_params;
|
||||||
const PrintRegionConfig &config = layerm->region().config();
|
const PrintRegionConfig &config = layerm->region().config();
|
||||||
if (config.ironing_type != IroningType::NoIroning &&
|
ironing_params.extruder = Layer::choose_ironing_extruder(
|
||||||
(config.ironing_type == IroningType::AllSolid ||
|
config,
|
||||||
((config.top_shell_layers > 0 || (this->object()->print()->config().spiral_mode && config.bottom_shell_layers > 1)) &&
|
/*spiral_mode=*/this->object()->print()->config().spiral_mode,
|
||||||
(config.ironing_type == IroningType::TopSurfaces ||
|
/*is_topmost_layer=*/layerm->layer()->upper_layer == nullptr);
|
||||||
(config.ironing_type == IroningType::TopmostOnly && layerm->layer()->upper_layer == nullptr))))) {
|
|
||||||
if (config.outer_wall_filament_id == config.top_surface_filament_id || config.wall_loops == 0) {
|
|
||||||
// Iron the whole face.
|
|
||||||
ironing_params.extruder = config.top_surface_filament_id;
|
|
||||||
} else {
|
|
||||||
// Iron just the infill.
|
|
||||||
ironing_params.extruder = config.top_surface_filament_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ironing_params.extruder != -1) {
|
if (ironing_params.extruder != -1) {
|
||||||
//TODO just_infill is currently not used.
|
//TODO just_infill is currently not used.
|
||||||
ironing_params.just_infill = false;
|
ironing_params.just_infill = false;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ using LayerPtrs = std::vector<Layer*>;
|
|||||||
class LayerRegion;
|
class LayerRegion;
|
||||||
using LayerRegionPtrs = std::vector<LayerRegion*>;
|
using LayerRegionPtrs = std::vector<LayerRegion*>;
|
||||||
class PrintRegion;
|
class PrintRegion;
|
||||||
|
class PrintRegionConfig;
|
||||||
class PrintObject;
|
class PrintObject;
|
||||||
class Print;
|
class Print;
|
||||||
|
|
||||||
@@ -200,6 +201,11 @@ public:
|
|||||||
FillAdaptive::Octree *support_fill_octree,
|
FillAdaptive::Octree *support_fill_octree,
|
||||||
FillLightning::Generator* lightning_generator) const;
|
FillLightning::Generator* lightning_generator) const;
|
||||||
void make_ironing();
|
void make_ironing();
|
||||||
|
// Returns the filament id (1-based) the region is ironed with, or -1 when the
|
||||||
|
// region is not ironed.
|
||||||
|
static int choose_ironing_extruder(const PrintRegionConfig &cfg,
|
||||||
|
bool spiral_mode,
|
||||||
|
bool is_topmost_layer);
|
||||||
void make_contour_z(const sla::IndexedMesh &mesh);
|
void make_contour_z(const sla::IndexedMesh &mesh);
|
||||||
|
|
||||||
void export_region_slices_to_svg(const char *path) const;
|
void export_region_slices_to_svg(const char *path) const;
|
||||||
|
|||||||
@@ -314,6 +314,9 @@ extern unsigned get_current_pid();
|
|||||||
std::string per_user_temp_id();
|
std::string per_user_temp_id();
|
||||||
// Per-user temp root under `base`; an empty `user_id` returns `base` unchanged.
|
// Per-user temp root under `base`; an empty `user_id` returns `base` unchanged.
|
||||||
std::string per_user_temp_dir(const std::string &base, const std::string &user_id);
|
std::string per_user_temp_dir(const std::string &base, const std::string &user_id);
|
||||||
|
// Completes a relative command line input path against the current working directory. Absolute
|
||||||
|
// paths and custom open protocol URLs are returned unchanged.
|
||||||
|
std::string resolve_cli_input_path(const std::string &path);
|
||||||
// BBS: backup & restore
|
// BBS: backup & restore
|
||||||
std::string get_process_name(int pid);
|
std::string get_process_name(int pid);
|
||||||
|
|
||||||
|
|||||||
@@ -1339,6 +1339,19 @@ std::string per_user_temp_dir(const std::string &base, const std::string &user_i
|
|||||||
return base + "/orcaslicer_" + user_id;
|
return base + "/orcaslicer_" + user_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string resolve_cli_input_path(const std::string &path)
|
||||||
|
{
|
||||||
|
const boost::filesystem::path input(path);
|
||||||
|
if (path.empty() || is_supported_open_protocol(path) || input.is_absolute())
|
||||||
|
return path;
|
||||||
|
|
||||||
|
boost::system::error_code ec;
|
||||||
|
const boost::filesystem::path resolved = boost::filesystem::system_complete(input, ec);
|
||||||
|
if (ec)
|
||||||
|
return path;
|
||||||
|
return resolved.lexically_normal().make_preferred().string();
|
||||||
|
}
|
||||||
|
|
||||||
// BBS: backup & restore
|
// BBS: backup & restore
|
||||||
std::string get_process_name(int pid)
|
std::string get_process_name(int pid)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2098,12 +2098,6 @@ void GLCanvas3D::render(bool only_init)
|
|||||||
_render_selection_center();
|
_render_selection_center();
|
||||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
|
||||||
// we need to set the mouse's scene position here because the depth buffer
|
|
||||||
// could be invalidated by the following gizmo render methods
|
|
||||||
// this position is used later into on_mouse() to drag the objects
|
|
||||||
if (m_picking_enabled)
|
|
||||||
m_mouse.scene_position = _mouse_to_3d(m_mouse.position.cast<coord_t>());
|
|
||||||
|
|
||||||
// sidebar hints need to be rendered before the gizmos because the depth buffer
|
// sidebar hints need to be rendered before the gizmos because the depth buffer
|
||||||
// could be invalidated by the following gizmo render methods
|
// could be invalidated by the following gizmo render methods
|
||||||
_render_selection_sidebar_hints();
|
_render_selection_sidebar_hints();
|
||||||
@@ -4491,12 +4485,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||||||
BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box();
|
BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box();
|
||||||
volume_bbox.offset(1.0);
|
volume_bbox.offset(1.0);
|
||||||
const bool is_cut_connector_selected = m_selection.is_any_connector();
|
const bool is_cut_connector_selected = m_selection.is_any_connector();
|
||||||
if ((!any_gizmo_active || !evt.CmdDown()) && volume_bbox.contains(m_mouse.scene_position) && !is_cut_connector_selected) {
|
const Vec3d scene_position = _mouse_to_3d(pos);
|
||||||
|
if ((!any_gizmo_active || !evt.CmdDown()) && volume_bbox.contains(scene_position) && !is_cut_connector_selected) {
|
||||||
m_volumes.volumes[volume_idx]->hover = GLVolume::HS_None;
|
m_volumes.volumes[volume_idx]->hover = GLVolume::HS_None;
|
||||||
// The dragging operation is initiated.
|
// The dragging operation is initiated.
|
||||||
m_mouse.drag.move_volume_idx = volume_idx;
|
m_mouse.drag.move_volume_idx = volume_idx;
|
||||||
m_selection.setup_cache();
|
m_selection.setup_cache();
|
||||||
m_mouse.drag.start_position_3D = m_mouse.scene_position;
|
m_mouse.drag.start_position_3D = scene_position;
|
||||||
m_sequential_print_clearance_first_displacement = true;
|
m_sequential_print_clearance_first_displacement = true;
|
||||||
m_moving = true;
|
m_moving = true;
|
||||||
|
|
||||||
|
|||||||
@@ -337,7 +337,6 @@ class GLCanvas3D
|
|||||||
|
|
||||||
bool dragging{ false };
|
bool dragging{ false };
|
||||||
Vec2d position{ DBL_MAX, DBL_MAX };
|
Vec2d position{ DBL_MAX, DBL_MAX };
|
||||||
Vec3d scene_position{ DBL_MAX, DBL_MAX, DBL_MAX };
|
|
||||||
bool ignore_left_up{ false };
|
bool ignore_left_up{ false };
|
||||||
Drag drag;
|
Drag drag;
|
||||||
bool ignore_right_up;
|
bool ignore_right_up;
|
||||||
|
|||||||
@@ -1037,9 +1037,6 @@ size_t PublishSettingsDialog::section_group_for(Section kind)
|
|||||||
section.mixed_tabs = new TabCtrl(section.page, wxID_ANY, wxDefaultPosition, wxDefaultSize, s_tab_style);
|
section.mixed_tabs = new TabCtrl(section.page, wxID_ANY, wxDefaultPosition, wxDefaultSize, s_tab_style);
|
||||||
section.mixed_tabs->SetFont(Label::Body_14);
|
section.mixed_tabs->SetFont(Label::Body_14);
|
||||||
section.mixed_tabs->SetBackgroundColour(GetBackgroundColour());
|
section.mixed_tabs->SetBackgroundColour(GetBackgroundColour());
|
||||||
// The mixed tabs carry full swatch compositions: give them a touch more room than the
|
|
||||||
// filament tabs so neighbouring compositions stay distinguishable (must precede AppendItem).
|
|
||||||
section.mixed_tabs->SetItemSpace(FromDIP(3));
|
|
||||||
page_sizer->Add(section.mixed_tabs, 0, wxEXPAND | wxTOP, FromDIP(2));
|
page_sizer->Add(section.mixed_tabs, 0, wxEXPAND | wxTOP, FromDIP(2));
|
||||||
section.mixed_tabs->Hide();
|
section.mixed_tabs->Hide();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,8 +311,11 @@ void Button::render(wxDC& dc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto szContent = textSize;
|
auto szContent = textSize;
|
||||||
|
// Whether the measured content reserved the text/icon gap. macOS measures an empty label
|
||||||
|
// as 0-high, so the gap is skipped there; the dot must not advance past it in that case.
|
||||||
|
const bool gap_reserved = szContent.y > 0;
|
||||||
if (icon.bmp().IsOk()) {
|
if (icon.bmp().IsOk()) {
|
||||||
if (szContent.y > 0) {
|
if (gap_reserved) {
|
||||||
//BBS norrow size between text and icon
|
//BBS norrow size between text and icon
|
||||||
if (vertical)
|
if (vertical)
|
||||||
szContent.y += spacing;
|
szContent.y += spacing;
|
||||||
@@ -357,10 +360,10 @@ void Button::render(wxDC& dc)
|
|||||||
dc.DrawBitmap(icon.bmp(), pt);
|
dc.DrawBitmap(icon.bmp(), pt);
|
||||||
//BBS norrow size between text and icon
|
//BBS norrow size between text and icon
|
||||||
if (vertical) {
|
if (vertical) {
|
||||||
pt.y += szIcon.y + spacing;
|
pt.y += szIcon.y + (gap_reserved ? spacing : 0);
|
||||||
pt.x = rcContent.x;
|
pt.x = rcContent.x;
|
||||||
} else {
|
} else {
|
||||||
pt.x += szIcon.x + spacing;
|
pt.x += szIcon.x + (gap_reserved ? spacing : 0);
|
||||||
pt.y = rcContent.y;
|
pt.y = rcContent.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ int TabCtrl::AppendItem(const wxString& item, int image, int selImage, void* cli
|
|||||||
btns.push_back(btn);
|
btns.push_back(btn);
|
||||||
if (btns.size() > 1)
|
if (btns.size() > 1)
|
||||||
sizer->GetItem(sizer->GetItemCount() - 1)->SetMinSize({0, 0});
|
sizer->GetItem(sizer->GetItemCount() - 1)->SetMinSize({0, 0});
|
||||||
sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, item_space);
|
sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||||
sizer->AddStretchSpacer(1);
|
sizer->AddStretchSpacer(1);
|
||||||
relayout();
|
relayout();
|
||||||
return btns.size() - 1;
|
return btns.size() - 1;
|
||||||
@@ -256,12 +256,12 @@ void TabCtrl::relayout()
|
|||||||
int item = sel + 1;
|
int item = sel + 1;
|
||||||
int first = 0;
|
int first = 0;
|
||||||
for (int i = 0; i < item; ++i)
|
for (int i = 0; i < item; ++i)
|
||||||
offset += btns[i]->GetMinSize().x + item_space * 2;
|
offset += btns[i]->GetMinSize().x;
|
||||||
if (item < btns.size())
|
if (item < btns.size())
|
||||||
offset += btns[item]->GetMinSize().x + item_space * 2;
|
offset += btns[item]->GetMinSize().x;
|
||||||
int width = GetSize().x;
|
int width = GetSize().x;
|
||||||
for (int i = 0; i < btns.size(); ++i) {
|
for (int i = 0; i < btns.size(); ++i) {
|
||||||
auto size = btns[i]->GetMinSize().x + item_space * 2;
|
auto size = btns[i]->GetMinSize().x;
|
||||||
if (i < sel && offset > width) {
|
if (i < sel && offset > width) {
|
||||||
sizer->Show(i * 2 + 1, false);
|
sizer->Show(i * 2 + 1, false);
|
||||||
sizer->Show(i * 2 + 2, false);
|
sizer->Show(i * 2 + 2, false);
|
||||||
@@ -284,26 +284,17 @@ void TabCtrl::relayout()
|
|||||||
if (item >= btns.size())
|
if (item >= btns.size())
|
||||||
--item;
|
--item;
|
||||||
// Keep spacing 2 ~ 10 TAB_BUTTON_SPACE
|
// Keep spacing 2 ~ 10 TAB_BUTTON_SPACE
|
||||||
int b = GetSize().x - offset - 10 - (item + 1 - first) * item_space * 8;
|
int b = GetSize().x - offset - 10 - (item + 1 - first) * 16;
|
||||||
sizer->GetItem(item * 2 + 2)->SetMinSize({b > 0 ? b : 0, 0});
|
sizer->GetItem(item * 2 + 2)->SetMinSize({b > 0 ? b : 0, 0});
|
||||||
Layout();
|
Layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TabCtrl::SetItemSpace(int space)
|
|
||||||
{
|
|
||||||
if (space < 0 || space == item_space)
|
|
||||||
return;
|
|
||||||
item_space = space;
|
|
||||||
relayout();
|
|
||||||
Refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
int TabCtrl::GetFullSize() const
|
int TabCtrl::GetFullSize() const
|
||||||
{
|
{
|
||||||
// Mirrors relayout(): a 10px leading spacer plus every button's min width and spacing.
|
// Mirrors relayout(): a 10px leading spacer plus every button's min width.
|
||||||
int width = 10;
|
int width = 10;
|
||||||
for (const Button* btn : btns)
|
for (const Button* btn : btns)
|
||||||
width += btn->GetMinSize().x + item_space * 2;
|
width += btn->GetMinSize().x;
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ class TabCtrl : public StaticBox
|
|||||||
|
|
||||||
int sel = -1;
|
int sel = -1;
|
||||||
wxFont bold;
|
wxFont bold;
|
||||||
int item_space = 2; // space around each button, both sides (SetItemSpace)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TabCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0);
|
TabCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0);
|
||||||
@@ -64,10 +63,6 @@ public:
|
|||||||
int GetNextVisible(int item) const;
|
int GetNextVisible(int item) const;
|
||||||
bool IsVisible(unsigned int item) const;
|
bool IsVisible(unsigned int item) const;
|
||||||
|
|
||||||
// Extra space around each tab button (in px on both sides). Defaults to the control-wide
|
|
||||||
// standard; call before appending items so every button picks it up.
|
|
||||||
void SetItemSpace(int space);
|
|
||||||
|
|
||||||
int GetFullSize() const;
|
int GetFullSize() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "libslic3r/Geometry.hpp"
|
#include "libslic3r/Geometry.hpp"
|
||||||
#include "libslic3r/Layer.hpp"
|
#include "libslic3r/Layer.hpp"
|
||||||
#include "libslic3r/Print.hpp"
|
#include "libslic3r/Print.hpp"
|
||||||
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
#include "libslic3r/SVG.hpp"
|
#include "libslic3r/SVG.hpp"
|
||||||
#include "libslic3r/libslic3r.h"
|
#include "libslic3r/libslic3r.h"
|
||||||
|
|
||||||
@@ -676,6 +677,73 @@ TEST_CASE("Ironing follows the solid infill rotation template", "[Fill]")
|
|||||||
REQUIRE(compared > int(ironing.size()) / 2);
|
REQUIRE(compared > int(ironing.size()) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
PrintRegionConfig ironing_config(IroningType type,
|
||||||
|
int top_surface_filament_id = 1,
|
||||||
|
int top_shell_layers = 3,
|
||||||
|
int bottom_shell_layers = 1)
|
||||||
|
{
|
||||||
|
PrintRegionConfig cfg;
|
||||||
|
cfg.ironing_type.value = type;
|
||||||
|
cfg.top_surface_filament_id.value = top_surface_filament_id;
|
||||||
|
cfg.top_shell_layers.value = top_shell_layers;
|
||||||
|
cfg.bottom_shell_layers.value = bottom_shell_layers;
|
||||||
|
cfg.outer_wall_filament_id.value = 1;
|
||||||
|
cfg.wall_loops.value = 2;
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("Ironing an all-solid region uses the top surface filament on every layer", "[Fill]")
|
||||||
|
{
|
||||||
|
const PrintRegionConfig cfg = ironing_config(IroningType::AllSolid, /*top_surface_filament_id=*/2);
|
||||||
|
const bool is_topmost_layer = GENERATE(false, true);
|
||||||
|
CAPTURE(is_topmost_layer);
|
||||||
|
REQUIRE(Layer::choose_ironing_extruder(cfg, /*spiral_mode=*/false, is_topmost_layer) == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Ironing top surfaces uses the top surface filament when the region has top shells", "[Fill]")
|
||||||
|
{
|
||||||
|
const PrintRegionConfig cfg = ironing_config(IroningType::TopSurfaces,
|
||||||
|
/*top_surface_filament_id=*/3,
|
||||||
|
/*top_shell_layers=*/2);
|
||||||
|
REQUIRE(Layer::choose_ironing_extruder(cfg, /*spiral_mode=*/false, /*is_topmost_layer=*/false) == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Ironing top surfaces without top shells needs spiral mode and more than one bottom shell", "[Fill]")
|
||||||
|
{
|
||||||
|
const PrintRegionConfig one_bottom_shell = ironing_config(IroningType::TopSurfaces,
|
||||||
|
/*top_surface_filament_id=*/1,
|
||||||
|
/*top_shell_layers=*/0,
|
||||||
|
/*bottom_shell_layers=*/1);
|
||||||
|
const PrintRegionConfig two_bottom_shells = ironing_config(IroningType::TopSurfaces,
|
||||||
|
/*top_surface_filament_id=*/1,
|
||||||
|
/*top_shell_layers=*/0,
|
||||||
|
/*bottom_shell_layers=*/2);
|
||||||
|
|
||||||
|
REQUIRE(Layer::choose_ironing_extruder(two_bottom_shells, /*spiral_mode=*/true, /*is_topmost_layer=*/false) == 1);
|
||||||
|
REQUIRE(Layer::choose_ironing_extruder(one_bottom_shell, /*spiral_mode=*/true, /*is_topmost_layer=*/false) == -1);
|
||||||
|
REQUIRE(Layer::choose_ironing_extruder(two_bottom_shells, /*spiral_mode=*/false, /*is_topmost_layer=*/false) == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Ironing the topmost surface only applies to the topmost layer", "[Fill]")
|
||||||
|
{
|
||||||
|
const PrintRegionConfig cfg = ironing_config(IroningType::TopmostOnly, /*top_surface_filament_id=*/4);
|
||||||
|
REQUIRE(Layer::choose_ironing_extruder(cfg, /*spiral_mode=*/false, /*is_topmost_layer=*/true) == 4);
|
||||||
|
REQUIRE(Layer::choose_ironing_extruder(cfg, /*spiral_mode=*/false, /*is_topmost_layer=*/false) == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("A region with ironing turned off is never ironed", "[Fill]")
|
||||||
|
{
|
||||||
|
const PrintRegionConfig cfg = ironing_config(IroningType::NoIroning);
|
||||||
|
const bool spiral_mode = GENERATE(false, true);
|
||||||
|
CAPTURE(spiral_mode);
|
||||||
|
REQUIRE(Layer::choose_ironing_extruder(cfg, spiral_mode, /*is_topmost_layer=*/true) == -1);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Solid infill direction offsets every layer when no template is set", "[Fill]")
|
TEST_CASE("Solid infill direction offsets every layer when no template is set", "[Fill]")
|
||||||
{
|
{
|
||||||
auto angles_for = [](int direction) {
|
auto angles_for = [](int direction) {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include "test_utils.hpp"
|
#include "test_utils.hpp"
|
||||||
|
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -88,3 +90,65 @@ TEST_CASE("copy_file reports the OS error when the destination cannot be written
|
|||||||
REQUIRE(std::all_of(code.begin(), code.end(), [](unsigned char c) { return std::isdigit(c) != 0; }));
|
REQUIRE(std::all_of(code.begin(), code.end(), [](unsigned char c) { return std::isdigit(c) != 0; }));
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("A resolved input path still names the same file after the working directory changes", "[utils]") {
|
||||||
|
ScopedTemporaryFile model(".3mf");
|
||||||
|
{ std::ofstream out(model.string()); out << "3mf"; }
|
||||||
|
const std::string name = model.path().filename().string();
|
||||||
|
|
||||||
|
// Resolve the bare name from the directory holding the file, then move away from it. The guard
|
||||||
|
// restores the directory the test started in, wherever this leaves it.
|
||||||
|
ScopedWorkingDirectory cwd(model.path().parent_path());
|
||||||
|
const std::string resolved = resolve_cli_input_path(name);
|
||||||
|
boost::filesystem::current_path(boost::filesystem::path(TEST_DATA_DIR));
|
||||||
|
|
||||||
|
REQUIRE(boost::filesystem::exists(resolved));
|
||||||
|
REQUIRE(boost::filesystem::equivalent(resolved, model.path()));
|
||||||
|
// Control: the bare name finds nothing from here, so resolving it this late would have failed.
|
||||||
|
REQUIRE_FALSE(boost::filesystem::exists(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("resolve_cli_input_path completes a relative path against the working directory", "[utils]") {
|
||||||
|
ScopedWorkingDirectory cwd(boost::filesystem::temp_directory_path());
|
||||||
|
// Read back rather than reusing temp_directory_path(): changing to it resolves any symlink.
|
||||||
|
const boost::filesystem::path here = boost::filesystem::current_path();
|
||||||
|
|
||||||
|
SECTION("a bare name") {
|
||||||
|
REQUIRE(resolve_cli_input_path("model.3mf") == (here / "model.3mf").make_preferred().string());
|
||||||
|
}
|
||||||
|
SECTION("a ./ prefix is dropped") {
|
||||||
|
REQUIRE(resolve_cli_input_path("./model.3mf") == (here / "model.3mf").make_preferred().string());
|
||||||
|
}
|
||||||
|
SECTION("a ../ traversal is collapsed") {
|
||||||
|
REQUIRE(resolve_cli_input_path("../model.3mf") == (here.parent_path() / "model.3mf").make_preferred().string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("resolve_cli_input_path leaves inputs that must not be completed unchanged", "[utils]") {
|
||||||
|
SECTION("an absolute path") {
|
||||||
|
const boost::filesystem::path absolute = (boost::filesystem::temp_directory_path() / "model.3mf").make_preferred();
|
||||||
|
REQUIRE(resolve_cli_input_path(absolute.string()) == absolute.string());
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Every absolute form Windows accepts opens today, so each must come back byte for byte:
|
||||||
|
// normalizing them would rewrite the forward slashes and rebuild the \\?\ and UNC prefixes.
|
||||||
|
SECTION("an absolute Windows path of any form") {
|
||||||
|
for (const std::string absolute : {R"(C:\models\model.3mf)",
|
||||||
|
R"(C:/models/model.3mf)",
|
||||||
|
R"(\\server\share\model.3mf)",
|
||||||
|
R"(\\?\C:\models\model.3mf)"})
|
||||||
|
REQUIRE(resolve_cli_input_path(absolute) == absolute);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// These are downloaded rather than opened, and completing one would produce a path, not a URL.
|
||||||
|
SECTION("a custom open protocol URL") {
|
||||||
|
for (const std::string url : {"orcaslicer://open/?file=https://example.com/model.3mf",
|
||||||
|
"prusaslicer://open/?file=https://example.com/model.3mf",
|
||||||
|
"bambustudio://open/?file=https://example.com/model.3mf",
|
||||||
|
"cura://open/?file=https://example.com/model.3mf"})
|
||||||
|
REQUIRE(resolve_cli_input_path(url) == url);
|
||||||
|
}
|
||||||
|
SECTION("an empty argument") {
|
||||||
|
REQUIRE(resolve_cli_input_path("").empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -176,4 +176,22 @@ inline void write_debug_stream([[maybe_unused]] const std::string &name, [[maybe
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Changes the working directory and restores the previous one on scope exit, including when an
|
||||||
|
// assertion throws. It is process wide state shared with every other test.
|
||||||
|
class ScopedWorkingDirectory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ScopedWorkingDirectory(const boost::filesystem::path &dir)
|
||||||
|
: m_previous(boost::filesystem::current_path())
|
||||||
|
{
|
||||||
|
boost::filesystem::current_path(dir);
|
||||||
|
}
|
||||||
|
~ScopedWorkingDirectory() { boost::system::error_code ec; boost::filesystem::current_path(m_previous, ec); }
|
||||||
|
ScopedWorkingDirectory(const ScopedWorkingDirectory &) = delete;
|
||||||
|
ScopedWorkingDirectory &operator=(const ScopedWorkingDirectory &) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::filesystem::path m_previous;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // SLIC3R_TEST_UTILS
|
#endif // SLIC3R_TEST_UTILS
|
||||||
|
|||||||
Reference in New Issue
Block a user