mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-06 07:11:10 +00:00
Merge branch 'main' into claude/inspiring-knuth-7cp6pk-upstream
This commit is contained in:
@@ -127,7 +127,7 @@ jobs:
|
||||
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
|
||||
uses: ./.github/workflows/build_check_cache.yml
|
||||
with:
|
||||
os: ${{ vars.SELF_HOSTED && 'orca-macos-arm64' || 'macos-14' }}
|
||||
os: ${{ vars.SELF_HOSTED && 'orca-macos-arm64' || 'macos-15' }}
|
||||
arch: ${{ matrix.arch }}
|
||||
build-deps-only: ${{ inputs.build-deps-only || false }}
|
||||
force-build: ${{ github.event_name == 'schedule' }}
|
||||
@@ -138,7 +138,7 @@ jobs:
|
||||
if: ${{ !cancelled() && needs.build_macos_arch.result == 'success' && !inputs.build-deps-only && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
|
||||
uses: ./.github/workflows/build_orca.yml
|
||||
with:
|
||||
os: ${{ vars.SELF_HOSTED && 'orca-macos-arm64' || 'macos-14' }}
|
||||
os: ${{ vars.SELF_HOSTED && 'orca-macos-arm64' || 'macos-15' }}
|
||||
arch: universal
|
||||
macos-combine-only: true
|
||||
secrets: inherit
|
||||
@@ -182,7 +182,7 @@ jobs:
|
||||
if: ${{ !cancelled() && success() }}
|
||||
uses: ./.github/workflows/unit_tests.yml
|
||||
with:
|
||||
os: ${{ vars.SELF_HOSTED && 'orca-macos-arm64' || 'macos-14' }}
|
||||
os: ${{ vars.SELF_HOSTED && 'orca-macos-arm64' || 'macos-15' }}
|
||||
artifact: ${{ github.sha }}-tests-macos-arm64
|
||||
test-dir: build/arm64/tests
|
||||
# Slice a two-colour cube through every shipped printer, and through every
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
# Anything that changes how the tree is built belongs in the key, or a job
|
||||
# restores one it cannot use. Linux amd64 passes no arch deliberately, so
|
||||
# 'linux-clang' keeps the cache it already has.
|
||||
cache-os: ${{ runner.os == 'macOS' && format('macos-{0}', inputs.arch) || (runner.os == 'Windows' && format('windows-{0}-{1}', inputs.arch, inputs.compiler) || format('linux-clang{0}', inputs.arch && format('-{0}', inputs.arch) || '')) }}
|
||||
cache-os: ${{ runner.os == 'macOS' && format('{0}-{1}', inputs.os, inputs.arch) || (runner.os == 'Windows' && format('windows-{0}-{1}', inputs.arch, inputs.compiler) || format('linux-clang{0}', inputs.arch && format('-{0}', inputs.arch) || '')) }}
|
||||
# The Windows ARM64 deps build in build-arm64, all others under build;
|
||||
# build_deps.yml and build_orca.yml pass the Windows directory to build_win.bat.
|
||||
dep-folder-name: ${{ runner.os == 'macOS' && format('/{0}', inputs.arch) || (runner.os == 'Windows' && inputs.arch == 'arm64') && '-arm64/OrcaSlicer_dep' || '/OrcaSlicer_dep' }}
|
||||
|
||||
@@ -183,10 +183,12 @@ jobs:
|
||||
- name: Free disk space
|
||||
if: runner.os == 'macOS' && !inputs.macos-combine-only && !vars.SELF_HOSTED
|
||||
run: |
|
||||
df -hI /dev/disk3s1s1
|
||||
sudo find /Applications -maxdepth 1 -type d -name "Xcode_*.app" ! -name "Xcode_15.4.app" -exec rm -rf {} +
|
||||
df -hI /
|
||||
# Keep only the selected Xcode
|
||||
xcode=$(basename "$(cd "$(xcode-select -p)/../.." && pwd -P)")
|
||||
sudo find /Applications -maxdepth 1 -type d -name "Xcode_*.app" ! -name "$xcode" -exec rm -rf {} +
|
||||
sudo rm -rf ~/Library/Developer/CoreSimulator/Caches/*
|
||||
df -hI /dev/disk3s1s1
|
||||
df -hI /
|
||||
|
||||
- name: Build slicer mac
|
||||
if: runner.os == 'macOS' && !inputs.macos-combine-only
|
||||
|
||||
@@ -75,6 +75,19 @@ static FILE *stl_open_count_facets(stl_file *stl, const char *file, unsigned int
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Zero normals and coordinates like 10 or 15 have no byte above 127, so the test above can miss a binary file.
|
||||
// Its size still matches its facet count; text read as that count would need a file of gigabytes.
|
||||
if (stl->stats.type == ascii) {
|
||||
uint32_t header_num_facets;
|
||||
fseek(fp, custom_header_length, SEEK_SET);
|
||||
if (fread(&header_num_facets, sizeof(uint32_t), 1, fp) == 1) {
|
||||
#if BOOST_ENDIAN_BIG_BYTE
|
||||
stl_internal_reverse_quads((char*)&header_num_facets, 4);
|
||||
#endif /* BOOST_ENDIAN_BIG_BYTE */
|
||||
if (header_size + uint64_t(header_num_facets) * SIZEOF_STL_FACET == file_size)
|
||||
stl->stats.type = binary;
|
||||
}
|
||||
}
|
||||
rewind(fp);
|
||||
|
||||
uint32_t num_facets = 0;
|
||||
|
||||
@@ -326,17 +326,13 @@ ConfigOption* ConfigOptionDef::create_default_option() const
|
||||
return new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt());
|
||||
|
||||
if (type == coEnums) {
|
||||
auto dft = this->default_value->clone();
|
||||
if (dft->nullable()) {
|
||||
ConfigOptionEnumsGenericNullable *opt = dynamic_cast<ConfigOptionEnumsGenericNullable *>(this->default_value->clone());
|
||||
opt->keys_map = this->enum_keys_map;
|
||||
return opt;
|
||||
} else {
|
||||
ConfigOptionEnumsGeneric *opt = dynamic_cast<ConfigOptionEnumsGeneric *>(this->default_value->clone());
|
||||
opt->keys_map = this->enum_keys_map;
|
||||
return opt;
|
||||
}
|
||||
delete dft;
|
||||
// Enum list defaults are built without a keys map, which the copy needs to deserialize and serialize names.
|
||||
ConfigOption *opt = this->default_value->clone();
|
||||
if (auto *nullable_enums = dynamic_cast<ConfigOptionEnumsGenericNullable *>(opt))
|
||||
nullable_enums->keys_map = this->enum_keys_map;
|
||||
else if (auto *enums = dynamic_cast<ConfigOptionEnumsGeneric *>(opt))
|
||||
enums->keys_map = this->enum_keys_map;
|
||||
return opt;
|
||||
}
|
||||
|
||||
return this->default_value->clone();
|
||||
|
||||
@@ -264,10 +264,7 @@ void Fill::_create_gap_fill(const Surface* surface, const FillParams& params, Ex
|
||||
return p.length() < scale_(params.config->filter_out_gap_fill.value);
|
||||
}), polylines.end());
|
||||
|
||||
ExtrusionEntityCollection gap_fill;
|
||||
variable_width(polylines, erGapFill, params.flow, gap_fill.entities);
|
||||
auto gap = std::move(gap_fill.entities);
|
||||
out->append(gap);
|
||||
variable_width(polylines, erGapFill, params.flow, out->entities);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3087,6 +3087,18 @@ bool GUI_App::on_init_inner()
|
||||
d->EndModal(wxID_ABORT);
|
||||
});
|
||||
|
||||
#ifdef __APPLE__
|
||||
// A quit request from the Dock, a logout or a restart ends with AppKit calling exit() right after this event, so
|
||||
// OnExit() and ~GUI_App() never run. Shut the plugins and Python down here as ~GUI_App() does. Left to
|
||||
// PluginManager's static destructor, the shutdown locks hook state that has already been destroyed and aborts.
|
||||
wxGetApp().Bind(wxEVT_END_SESSION, [](wxCloseEvent &e) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "received wxEVT_END_SESSION";
|
||||
Slic3r::PluginManager::instance().shutdown();
|
||||
Slic3r::PythonInterpreter::instance().shutdown();
|
||||
e.Skip();
|
||||
});
|
||||
#endif
|
||||
|
||||
// Verify resources path
|
||||
const wxString resources_dir = from_u8(Slic3r::resources_dir());
|
||||
wxCHECK_MSG(wxDirExists(resources_dir), false,
|
||||
|
||||
@@ -1618,19 +1618,11 @@ wxBoxSizer *StatusBasePanel::create_monitoring_page()
|
||||
|
||||
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_web_media_controller = std::make_unique<WebMediaController>(m_custom_camera_view);
|
||||
|
||||
m_media_play_ctrl = new MediaPlayCtrl(this, m_media_ctrl, wxDefaultPosition, wxSize(-1, FromDIP(40)));
|
||||
m_media_play_ctrl->SetWebMediaController(m_web_media_controller.get());
|
||||
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();
|
||||
// }
|
||||
// });
|
||||
|
||||
sizer->Add(m_media_ctrl, 1, wxEXPAND | wxALL, 0);
|
||||
sizer->Add(m_custom_camera_view, 1, wxEXPAND | wxALL, 0);
|
||||
@@ -1643,12 +1635,6 @@ wxBoxSizer *StatusBasePanel::create_monitoring_page()
|
||||
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);
|
||||
@@ -5121,23 +5107,6 @@ void StatusPanel::on_camera_enter(wxMouseEvent& event)
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -606,8 +606,6 @@ protected:
|
||||
virtual void on_axis_ctrl_e_up_10(wxCommandEvent &event) { event.Skip(); }
|
||||
virtual void on_axis_ctrl_e_down_10(wxCommandEvent &event) { event.Skip(); }
|
||||
virtual void on_nozzle_selected(wxCommandEvent &event) { event.Skip(); }
|
||||
void remove_controls();
|
||||
void on_webview_navigating(wxWebViewEvent& evt);
|
||||
|
||||
public:
|
||||
StatusBasePanel(wxWindow * parent,
|
||||
|
||||
@@ -1477,3 +1477,21 @@ TEST_CASE("A static config applied onto a config of another type falls back to a
|
||||
dynamic.apply(region);
|
||||
CHECK(dynamic.opt_serialize("sparse_infill_pattern") == "gyroid");
|
||||
}
|
||||
|
||||
TEST_CASE("Default options of enum lists get their definition's keys map", "[Config]")
|
||||
{
|
||||
size_t enum_lists = 0;
|
||||
for (const auto &[key, def] : print_config_def.options) {
|
||||
if (def.type != coEnums || !def.default_value)
|
||||
continue;
|
||||
INFO(key);
|
||||
const std::unique_ptr<ConfigOption> opt(def.create_default_option());
|
||||
CHECK(*opt == *def.default_value);
|
||||
const auto *nullable_enums = dynamic_cast<const ConfigOptionEnumsGenericNullable *>(opt.get());
|
||||
const auto *enums = dynamic_cast<const ConfigOptionEnumsGeneric *>(opt.get());
|
||||
REQUIRE((nullable_enums != nullptr) != (enums != nullptr));
|
||||
CHECK((nullable_enums != nullptr ? nullable_enums->keys_map : enums->keys_map) == def.enum_keys_map);
|
||||
++enum_lists;
|
||||
}
|
||||
CHECK(enum_lists > 0);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#include <catch2/catch_all.hpp>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include "libslic3r/Point.hpp"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Format/STL.hpp"
|
||||
#include "test_utils.hpp"
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
@@ -58,3 +61,25 @@ SCENARIO("Reading an STL file", "[stl]") {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("A binary STL whose facet bytes never exceed 127 is read as binary", "[stl]")
|
||||
{
|
||||
const indexed_triangle_set cube = its_make_cube(10., 10., 10.);
|
||||
std::string stl(80, '\0');
|
||||
const auto append = [&stl](const auto &value) { stl.append(reinterpret_cast<const char *>(&value), sizeof(value)); };
|
||||
append(uint32_t(cube.indices.size()));
|
||||
const stl_normal zero_normal = stl_normal::Zero();
|
||||
for (const stl_triangle_vertex_indices &facet : cube.indices) {
|
||||
append(zero_normal);
|
||||
for (int i = 0; i < 3; ++i)
|
||||
append(cube.vertices[facet[i]]);
|
||||
append(uint16_t(0));
|
||||
}
|
||||
REQUIRE(std::none_of(stl.begin() + 84, stl.begin() + 84 + 128, [](unsigned char c) { return c > 127; }));
|
||||
|
||||
ScopedTemporaryFile file(".stl");
|
||||
boost::nowide::ofstream(file.string(), std::ios::binary) << stl;
|
||||
Model model;
|
||||
REQUIRE(load_stl(file.string().c_str(), &model));
|
||||
REQUIRE(is_approx(model.objects.front()->volumes.front()->mesh().size(), Vec3d(10, 10, 10)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user