Compare commits

..
89 changed files with 1154 additions and 515 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "Elegoo",
"version": "02.04.00.11",
"version": "02.04.00.12",
"force_update": "0",
"description": "Elegoo configurations",
"machine_model_list": [
@@ -15,6 +15,6 @@
"Elegoo"
],
"filament_start_gcode": [
"; filament start gcode\n"
"; Elegoo PLA filament start gcode\n"
]
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "Orca Arena Printer",
"url": "",
"version": "02.04.00.07",
"version": "02.04.00.08",
"force_update": "0",
"description": "Orca Arena configuration files",
"machine_model_list": [
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "OrcaFilamentLibrary",
"version": "02.04.00.15",
"version": "02.04.00.19",
"force_update": "0",
"description": "Orca Filament Library",
"filament_list": [
@@ -39,7 +39,7 @@
"60"
],
"filament_start_gcode": [
"; filament start gcode\n"
"; Elegoo PLA filament start gcode\n"
],
"filament_end_gcode": [
"; filament end gcode \n"
-2
View File
@@ -4094,8 +4094,6 @@ int CLI::run(int argc, char **argv)
flush_and_exit(CLI_MIXED_FILAMENT_INVALID);
}
}
if (filament_count > 0)
resize_mixed_filament_metadata(m_print_config, size_t(filament_count), size_t(filament_count));
m_print_config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology", true)->value = printer_technology;
+1
View File
@@ -84,6 +84,7 @@
#include <cmath>
#include <cctype>
#include <cstdio>
#include <functional>
#include <gp_XY.hxx>
#include <initializer_list>
#include <math.h>
+3
View File
@@ -261,6 +261,8 @@ set(lisbslic3r_sources
GCode/PreciseSeam.cpp
GCode/PreciseSeam.hpp
GCode/PreciseSeamInternal.hpp
#GCodeSender.cpp
#GCodeSender.hpp
GCode/SmallAreaInfillFlowCompensator.cpp
GCode/SmallAreaInfillFlowCompensator.hpp
GCode/SpiralVase.cpp
@@ -445,6 +447,7 @@ set(lisbslic3r_sources
SLA/SupportTreeBuildsteps.hpp
SLA/SupportTree.cpp
SLA/SupportTree.hpp
#SLA/SupportTreeIGL.cpp
SLA/SupportTreeMesher.cpp
SLA/SupportTreeMesher.hpp
SlicesToTriangleMesh.cpp
+1
View File
@@ -2589,6 +2589,7 @@ void priv::create_face_types(FaceTypeMap &map,
}
#include <CGAL/Polygon_mesh_processing/clip.h>
#include <CGAL/Polygon_mesh_processing/corefinement.h>
bool priv::clip_cut(SurfacePatch &cut, CutMesh clipper)
{
CutMesh& tm = cut.mesh;
+1
View File
@@ -15,6 +15,7 @@
#include "Emboss.hpp"
#include <optional>
#include <stdio.h>
#include <numeric>
#include <cstdlib>
#include <boost/nowide/convert.hpp>
#include <boost/log/trivial.hpp>
+1
View File
@@ -55,6 +55,7 @@
#include "TDataStd_Name.hxx"
#include "BRepBuilderAPI_Transform.hxx"
#include "TopExp_Explorer.hxx"
#include "TopExp_Explorer.hxx"
#include "BRep_Tool.hxx"
#include "BRepTools.hxx"
#include <IMeshTools_Parameters.hxx>
+1
View File
@@ -43,6 +43,7 @@
#include "Print.hpp"
#include "Utils.hpp"
#include "ClipperUtils.hpp"
#include "libslic3r.h"
#include "LocalesUtils.hpp"
#include "libslic3r/format.hpp"
#include "Time.hpp"
+2
View File
@@ -148,6 +148,8 @@ struct ConflictComputeResult
using ConflictComputeOpt = std::optional<ConflictComputeResult>;
using ConflictObjName = std::optional<std::pair<std::string, std::string>>;
struct ConflictChecker
{
static ConflictResultOpt find_inter_of_lines_in_diff_objs(PrintObjectPtrs objs, std::optional<const FakeWipeTower *> wtdptr);
+2
View File
@@ -27,6 +27,8 @@
#include "../PrintConfig.hpp"
#include "../Utils.hpp"
#include "Print.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/libslic3r.h"
#include <boost/log/trivial.hpp>
#include <string>
+4 -1
View File
@@ -22,6 +22,9 @@
namespace Slic3r {
namespace PreciseSeam {
// Import EnforcedBlockedSeamPoint from SeamPlacerImpl namespace for convenience
using SeamPlacerImpl::EnforcedBlockedSeamPoint;
// Geometry and its exterior bounds are prepared together, then treated as read-only.
struct ModifierRegion {
ExPolygon polygon;
@@ -134,7 +137,7 @@ SegmentExtraction extract_perimeter_segments(const PreparedPerimeter &prepared,
// Result of weak modifier segment processing
struct WeakModifierSegment {
SeamPlacerImpl::EnforcedBlockedSeamPoint type; // Enforced/Blocked/Neutral
EnforcedBlockedSeamPoint type; // Enforced/Blocked/Neutral
Point left_point; // Coordinates of left (first) point of segment
PerimeterPosition left_position; // Position on the source perimeter before insertion/refinement.
Point right_point; // Coordinates of right (last) point of segment
+2 -1
View File
@@ -40,7 +40,8 @@ std::string get_error_string(const ThumbnailErrors& errors);
typedef std::vector<std::pair<GCodeThumbnailsFormat, Vec2d>> GCodeThumbnailDefinitionsList;
std::pair<GCodeThumbnailDefinitionsList, ThumbnailErrors> make_and_check_thumbnail_list(const std::string& thumbnails_string, const std::string_view def_ext = "PNG");
using namespace std::literals;
std::pair<GCodeThumbnailDefinitionsList, ThumbnailErrors> make_and_check_thumbnail_list(const std::string& thumbnails_string, const std::string_view def_ext = "PNG"sv);
std::pair<GCodeThumbnailDefinitionsList, ThumbnailErrors> make_and_check_thumbnail_list(const ConfigBase &config);
+580
View File
@@ -0,0 +1,580 @@
#include "GCodeSender.hpp"
#include <iostream>
#include <istream>
#include <string>
#include <thread>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/lexical_cast.hpp>
#if defined(__APPLE__) || defined(__OpenBSD__)
#include <termios.h>
#endif
#ifdef __APPLE__
#include <sys/ioctl.h>
#include <IOKit/serial/ioss.h>
#endif
#ifdef __linux__
#include <sys/ioctl.h>
#include <fcntl.h>
#include "/usr/include/asm-generic/ioctls.h"
/* The following definitions are kindly borrowed from:
/usr/include/asm-generic/termbits.h
Unfortunately we cannot just include that one because
it would redefine the "struct termios" already defined
the <termios.h> already included by Boost.ASIO. */
#define K_NCCS 19
struct termios2 {
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_line;
cc_t c_cc[K_NCCS];
speed_t c_ispeed;
speed_t c_ospeed;
};
#define BOTHER CBAUDEX
#endif
//#define DEBUG_SERIAL
#ifdef DEBUG_SERIAL
#include <cstdlib>
#include <fstream>
std::fstream fs;
#endif
#define KEEP_SENT 20
namespace Slic3r {
GCodeSender::GCodeSender()
: io(), serial(io), can_send(false), sent(0), open(false), error(false),
connected(false), queue_paused(false)
{
#ifdef DEBUG_SERIAL
std::srand(std::time(nullptr));
#endif
}
GCodeSender::~GCodeSender()
{
this->disconnect();
}
bool
GCodeSender::connect(std::string devname, unsigned int baud_rate)
{
this->disconnect();
this->set_error_status(false);
try {
this->serial.open(devname);
this->serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::odd));
this->serial.set_option(boost::asio::serial_port_base::character_size(boost::asio::serial_port_base::character_size(8)));
this->serial.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
this->serial.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
this->set_baud_rate(baud_rate);
this->serial.close();
this->serial.open(devname);
this->serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
// set baud rate again because set_option overwrote it
this->set_baud_rate(baud_rate);
this->open = true;
this->reset();
} catch (boost::system::system_error &) {
this->set_error_status(true);
return false;
}
// a reset firmware expect line numbers to start again from 1
this->sent = 0;
this->last_sent.clear();
/* Initialize debugger */
#ifdef DEBUG_SERIAL
fs.open("serial.txt", std::fstream::out | std::fstream::trunc);
#endif
// this gives some work to the io_service before it is started
// (post() runs the supplied function in its thread)
this->io.post(boost::bind(&GCodeSender::do_read, this));
// start reading in the background thread
boost::thread t(boost::bind(&boost::asio::io_service::run, &this->io));
this->background_thread.swap(t);
// always send a M105 to check for connection because firmware might be silent on connect
//FIXME Vojtech: This is being sent too early, leading to line number synchronization issues,
// from which the GCodeSender never recovers.
// this->send("M105", true);
return true;
}
void
GCodeSender::set_baud_rate(unsigned int baud_rate)
{
try {
// This does not support speeds > 115200
this->serial.set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
} catch (boost::system::system_error &) {
boost::asio::serial_port::native_handle_type handle = this->serial.native_handle();
#if __APPLE__
termios ios;
::tcgetattr(handle, &ios);
::cfsetspeed(&ios, baud_rate);
speed_t newSpeed = baud_rate;
ioctl(handle, IOSSIOSPEED, &newSpeed);
::tcsetattr(handle, TCSANOW, &ios);
#elif __linux__
termios2 ios;
if (ioctl(handle, TCGETS2, &ios))
printf("Error in TCGETS2: %s\n", strerror(errno));
ios.c_ispeed = ios.c_ospeed = baud_rate;
ios.c_cflag &= ~CBAUD;
ios.c_cflag |= BOTHER | CLOCAL | CREAD;
ios.c_cc[VMIN] = 1; // Minimum of characters to read, prevents eof errors when 0 bytes are read
ios.c_cc[VTIME] = 1;
if (ioctl(handle, TCSETS2, &ios))
printf("Error in TCSETS2: %s\n", strerror(errno));
#elif __OpenBSD__
struct termios ios;
::tcgetattr(handle, &ios);
::cfsetspeed(&ios, baud_rate);
if (::tcsetattr(handle, TCSAFLUSH, &ios) != 0)
printf("Failed to set baud rate: %s\n", strerror(errno));
#else
//throw Slic3r::InvalidArgument("OS does not currently support custom bauds");
#endif
}
}
void
GCodeSender::disconnect()
{
if (!this->open) return;
this->open = false;
this->connected = false;
this->io.post(boost::bind(&GCodeSender::do_close, this));
this->background_thread.join();
this->io.reset();
/*
if (this->error_status()) {
throw(boost::system::system_error(boost::system::error_code(),
"Error while closing the device"));
}
*/
#ifdef DEBUG_SERIAL
fs << "DISCONNECTED" << std::endl << std::flush;
fs.close();
#endif
}
bool
GCodeSender::is_connected() const
{
return this->connected;
}
bool
GCodeSender::wait_connected(unsigned int timeout) const
{
using namespace boost::posix_time;
ptime t0 = second_clock::local_time() + seconds(timeout);
while (!this->connected) {
if (second_clock::local_time() > t0) return false;
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
}
return true;
}
size_t
GCodeSender::queue_size() const
{
boost::lock_guard<boost::mutex> l(this->queue_mutex);
return this->queue.size();
}
void
GCodeSender::pause_queue()
{
boost::lock_guard<boost::mutex> l(this->queue_mutex);
this->queue_paused = true;
}
void
GCodeSender::resume_queue()
{
{
boost::lock_guard<boost::mutex> l(this->queue_mutex);
this->queue_paused = false;
}
this->send();
}
void
GCodeSender::purge_queue(bool priority)
{
boost::lock_guard<boost::mutex> l(this->queue_mutex);
if (priority) {
// clear priority queue
std::list<std::string> empty;
std::swap(this->priqueue, empty);
} else {
// clear queue
std::queue<std::string> empty;
std::swap(this->queue, empty);
this->queue_paused = false;
}
}
// purge log and return its contents
std::vector<std::string>
GCodeSender::purge_log()
{
boost::lock_guard<boost::mutex> l(this->log_mutex);
std::vector<std::string> retval;
retval.reserve(this->log.size());
while (!this->log.empty()) {
retval.push_back(this->log.front());
this->log.pop();
}
return retval;
}
std::string
GCodeSender::getT() const
{
boost::lock_guard<boost::mutex> l(this->log_mutex);
return this->T;
}
std::string
GCodeSender::getB() const
{
boost::lock_guard<boost::mutex> l(this->log_mutex);
return this->B;
}
void
GCodeSender::do_close()
{
this->set_error_status(false);
boost::system::error_code ec;
this->serial.cancel(ec);
if (ec) this->set_error_status(true);
this->serial.close(ec);
if (ec) this->set_error_status(true);
}
void
GCodeSender::set_error_status(bool e)
{
boost::lock_guard<boost::mutex> l(this->error_mutex);
this->error = e;
}
bool
GCodeSender::error_status() const
{
boost::lock_guard<boost::mutex> l(this->error_mutex);
return this->error;
}
void
GCodeSender::do_read()
{
// read one line
boost::asio::async_read_until(
this->serial,
this->read_buffer,
'\n',
boost::bind(
&GCodeSender::on_read,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
}
void
GCodeSender::on_read(const boost::system::error_code& error,
size_t bytes_transferred)
{
this->set_error_status(false);
if (error) {
#ifdef __APPLE__
if (error.value() == 45) {
// OS X bug: http://osdir.com/ml/lib.boost.asio.user/2008-08/msg00004.html
this->do_read();
return;
}
#endif
// printf("ERROR: [%d] %s\n", error.value(), error.message().c_str());
// error can be true even because the serial port was closed.
// In this case it is not a real error, so ignore.
if (this->open) {
this->do_close();
this->set_error_status(true);
}
return;
}
std::istream is(&this->read_buffer);
std::string line;
std::getline(is, line);
if (!line.empty()) {
#ifdef DEBUG_SERIAL
fs << "<< " << line << std::endl << std::flush;
#endif
// note that line might contain \r at its end
// parse incoming line
if (!this->connected
&& (boost::starts_with(line, "start")
|| boost::starts_with(line, "Grbl ")
|| boost::starts_with(line, "ok")
|| boost::contains(line, "T:"))) {
this->connected = true;
{
boost::lock_guard<boost::mutex> l(this->queue_mutex);
this->can_send = true;
}
this->send();
} else if (boost::starts_with(line, "ok")) {
{
boost::lock_guard<boost::mutex> l(this->queue_mutex);
this->can_send = true;
}
this->send();
} else if (boost::istarts_with(line, "resend") // Marlin uses "Resend: "
|| boost::istarts_with(line, "rs")) {
// extract the first number from line
boost::algorithm::trim_left_if(line, !boost::algorithm::is_digit());
size_t toresend = boost::lexical_cast<size_t>(line.substr(0, line.find_first_not_of("0123456789")));
#ifdef DEBUG_SERIAL
fs << "!! line num out of sync: toresend = " << toresend << ", sent = " << sent << ", last_sent.size = " << last_sent.size() << std::endl;
#endif
if (toresend > this->sent - this->last_sent.size() && toresend <= this->sent) {
{
boost::lock_guard<boost::mutex> l(this->queue_mutex);
const auto lines_to_resend = this->sent - toresend + 1;
#ifdef DEBUG_SERIAL
fs << "!! resending " << lines_to_resend << " lines" << std::endl;
#endif
// move the unsent lines to priqueue
this->priqueue.insert(
this->priqueue.begin(), // insert at the beginning
this->last_sent.begin() + this->last_sent.size() - lines_to_resend,
this->last_sent.end()
);
// we can empty last_sent because it's not useful anymore
this->last_sent.clear();
// start resending with the requested line number
this->sent = toresend - 1;
this->can_send = true;
}
this->send();
} else {
printf("Cannot resend %zu (oldest we have is %zu)\n", toresend, this->sent - this->last_sent.size());
}
} else if (boost::starts_with(line, "wait")) {
// ignore
} else {
// push any other line into the log
boost::lock_guard<boost::mutex> l(this->log_mutex);
this->log.push(line);
}
// parse temperature info
{
size_t pos = line.find("T:");
if (pos != std::string::npos && line.size() > pos + 2) {
// we got temperature info
boost::lock_guard<boost::mutex> l(this->log_mutex);
this->T = line.substr(pos+2, line.find_first_not_of("0123456789.", pos+2) - (pos+2));
pos = line.find("B:");
if (pos != std::string::npos && line.size() > pos + 2) {
// we got bed temperature info
this->B = line.substr(pos+2, line.find_first_not_of("0123456789.", pos+2) - (pos+2));
}
}
}
}
this->do_read();
}
void
GCodeSender::send(const std::vector<std::string> &lines, bool priority)
{
// append lines to queue
{
boost::lock_guard<boost::mutex> l(this->queue_mutex);
for (std::vector<std::string>::const_iterator line = lines.begin(); line != lines.end(); ++line) {
if (priority) {
this->priqueue.push_back(*line);
} else {
this->queue.push(*line);
}
}
}
this->send();
}
void
GCodeSender::send(const std::string &line, bool priority)
{
// append line to queue
{
boost::lock_guard<boost::mutex> l(this->queue_mutex);
if (priority) {
this->priqueue.push_back(line);
} else {
this->queue.push(line);
}
}
this->send();
}
void
GCodeSender::send()
{
this->io.post(boost::bind(&GCodeSender::do_send, this));
}
void
GCodeSender::do_send()
{
boost::lock_guard<boost::mutex> l(this->queue_mutex);
// printer is not connected or we're still waiting for the previous ack
if (!this->can_send) return;
std::string line;
while (!this->priqueue.empty() || (!this->queue.empty() && !this->queue_paused)) {
if (!this->priqueue.empty()) {
line = this->priqueue.front();
this->priqueue.pop_front();
} else {
line = this->queue.front();
this->queue.pop();
}
// strip comments
size_t comment_pos = line.find_first_of(';');
if (comment_pos != std::string::npos)
line.erase(comment_pos, std::string::npos);
boost::algorithm::trim(line);
// if line is not empty, send it
if (!line.empty()) break;
// if line is empty, process next item in queue
}
if (line.empty()) return;
// compute full line
++ this->sent;
#ifndef DEBUG_SERIAL
const auto line_num = this->sent;
#else
// In DEBUG_SERIAL mode, test line re-synchronization by sending bad line number 1/4 of the time
const auto line_num = std::rand() < RAND_MAX/4 ? 0 : this->sent;
#endif
std::string full_line = "N" + boost::lexical_cast<std::string>(line_num) + " " + line;
// calculate checksum
int cs = 0;
for (std::string::const_iterator it = full_line.begin(); it != full_line.end(); ++it)
cs = cs ^ *it;
// write line to device
full_line += "*";
full_line += boost::lexical_cast<std::string>(cs);
full_line += "\n";
#ifdef DEBUG_SERIAL
fs << ">> " << full_line << std::flush;
#endif
this->last_sent.push_back(line);
this->can_send = false;
while (this->last_sent.size() > KEEP_SENT) {
this->last_sent.pop_front();
}
// we can't supply boost::asio::buffer(full_line) to async_write() because full_line is on the
// stack and the buffer would lose its underlying storage causing memory corruption
std::ostream os(&this->write_buffer);
os << full_line;
boost::asio::async_write(this->serial, this->write_buffer, boost::bind(&GCodeSender::on_write, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void
GCodeSender::on_write(const boost::system::error_code& error,
size_t bytes_transferred)
{
this->set_error_status(false);
if (error) {
if (this->open) {
this->do_close();
this->set_error_status(true);
}
return;
}
this->do_send();
}
void
GCodeSender::set_DTR(bool on)
{
#if defined(_WIN32) && !defined(__SYMBIAN32__)
boost::asio::serial_port_service::native_handle_type handle = this->serial.native_handle();
if (on)
EscapeCommFunction(handle, SETDTR);
else
EscapeCommFunction(handle, CLRDTR);
#else
int fd = this->serial.native_handle();
int status;
ioctl(fd, TIOCMGET, &status);
if (on)
status |= TIOCM_DTR;
else
status &= ~TIOCM_DTR;
ioctl(fd, TIOCMSET, &status);
#endif
}
void
GCodeSender::reset()
{
set_DTR(false);
std::this_thread::sleep_for(std::chrono::milliseconds(200));
set_DTR(true);
std::this_thread::sleep_for(std::chrono::milliseconds(200));
set_DTR(false);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
} // namespace Slic3r
+81
View File
@@ -0,0 +1,81 @@
#ifndef slic3r_GCodeSender_hpp_
#define slic3r_GCodeSender_hpp_
#include "libslic3r.h"
#include <queue>
#include <boost/core/noncopyable.hpp>
#include <cstddef>
#include <boost/asio/io_service.hpp>
#include <boost/asio/serial_port.hpp>
#include <boost/asio/streambuf.hpp>
#include <list>
#include <deque>
#include <string>
#include <vector>
#include <boost/asio.hpp>
#include <boost/noncopyable.hpp>
#include <boost/bind/bind.hpp>
#include <boost/thread.hpp>
namespace Slic3r {
namespace asio = boost::asio;
class GCodeSender : private boost::noncopyable {
public:
GCodeSender();
~GCodeSender();
bool connect(std::string devname, unsigned int baud_rate);
void send(const std::vector<std::string> &lines, bool priority = false);
void send(const std::string &s, bool priority = false);
void disconnect();
bool error_status() const;
bool is_connected() const;
bool wait_connected(unsigned int timeout = 3) const;
size_t queue_size() const;
void pause_queue();
void resume_queue();
void purge_queue(bool priority = false);
std::vector<std::string> purge_log();
std::string getT() const;
std::string getB() const;
void set_DTR(bool on);
void reset();
private:
asio::io_service io;
asio::serial_port serial;
boost::thread background_thread;
boost::asio::streambuf read_buffer, write_buffer;
bool open; // whether the serial socket is connected
bool connected; // whether the printer is online
bool error;
mutable boost::mutex error_mutex;
// this mutex guards queue, priqueue, can_send, queue_paused, sent, last_sent
mutable boost::mutex queue_mutex;
std::queue<std::string> queue;
std::list<std::string> priqueue;
bool can_send;
bool queue_paused;
size_t sent;
std::deque<std::string> last_sent;
// this mutex guards log, T, B
mutable boost::mutex log_mutex;
std::queue<std::string> log;
std::string T, B;
void set_baud_rate(unsigned int baud_rate);
void set_error_status(bool e);
void do_send();
void on_write(const boost::system::error_code& error, size_t bytes_transferred);
void do_close();
void do_read();
void on_read(const boost::system::error_code& error, size_t bytes_transferred);
void send();
};
} // namespace Slic3r
#endif /* slic3r_GCodeSender_hpp_ */
+2
View File
@@ -94,6 +94,8 @@ struct Circle {
using Circlef = Circle<Vec2f>;
using Circled = Circle<Vec2d>;
using CircleSqf = CircleSq<Vec2f>;
using CircleSqd = CircleSq<Vec2d>;
/// Find the center of the circle corresponding to the vector of Points as an arc.
Point circle_center_taubin_newton(const Points::const_iterator& input_start, const Points::const_iterator& input_end, size_t cycles = 20);
+1
View File
@@ -1,6 +1,7 @@
#include <boost/log/trivial.hpp>
#include "MedialAxis.hpp"
#include <boost/log/trivial.hpp>
#include <boost/polygon/polygon.hpp>
#include <cassert>
#include <cmath>
+4 -3
View File
@@ -154,12 +154,13 @@ template<class ExecutionPolicy, class Enable = void> struct _Loop
};
// Add Specialization for using ExecutionTBB for parallel loops.
template<> struct _Loop<Slic3r::ExecutionTBB>
using namespace Slic3r;
template<> struct _Loop<ExecutionTBB>
{
template<class It, class Fn> static void for_each_idx(It from, It to, Fn&& fn)
{
Slic3r::execution::for_each(
Slic3r::ex_tbb, size_t(0), size_t(to - from), [&from, &fn](size_t i) { fn(from[i], i); }, Slic3r::execution::max_concurrency(Slic3r::ex_tbb));
execution::for_each(
ex_tbb, size_t(0), size_t(to - from), [&from, &fn](size_t i) { fn(from[i], i); }, execution::max_concurrency(ex_tbb));
}
};
+1
View File
@@ -22,6 +22,7 @@
#include "Format/AssimpImport.hpp"
#include "ClipperUtils.hpp"
#include "Exception.hpp"
#include "Model.hpp"
#include "ModelArrange.hpp"
#include "Arrange.hpp"
#include "Geometry.hpp"
+1
View File
@@ -18,6 +18,7 @@
#include "TriangleMesh.hpp"
#include "CustomGCode.hpp"
#include "calib.hpp"
#include "enum_bitmask.hpp"
#include "TextConfiguration.hpp"
#include "EmbossShape.hpp"
#include "TriangleSelector.hpp"
+34 -8
View File
@@ -3593,6 +3593,24 @@ void PresetBundle::export_selections(AppConfig &config)
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": printer %1%, print %2%, filaments[0] %3% ")%printers.get_selected_preset_name() % prints.get_selected_preset_name() %filament_presets[0];
}
// Preserve metadata only for existing colour slots; new slots get false/empty defaults.
static void resize_mixed_filament_metadata(DynamicPrintConfig &config, size_t old_slot_count, size_t new_slot_count)
{
auto resize = [old_slot_count, new_slot_count](auto *opt) {
if (opt) {
opt->values.resize(std::min(old_slot_count, opt->values.size()));
opt->values.resize(new_slot_count);
}
};
resize(config.option<ConfigOptionBools>("filament_is_mixed"));
resize(config.option<ConfigOptionStrings>("filament_mixed_components"));
resize(config.option<ConfigOptionStrings>("filament_mixed_sublayer_ratios"));
resize(config.option<ConfigOptionBools>("filament_mixed_gradient"));
resize(config.option<ConfigOptionStrings>("filament_mixed_gradient_range"));
resize(config.option<ConfigOptionStrings>("filament_mixed_gradient_curve"));
resize(config.option<ConfigOptionBools>("filament_mixed_gradient_per_part"));
}
void PresetBundle::set_num_filaments(unsigned int n, std::string new_color)
{
unsigned old_filament_count = this->filament_presets.size();
@@ -3876,6 +3894,16 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
maps.erase(j);
}
}
}
if (is_placeholder) {
// Orca: an empty AMS slot is a placeholder, not a filament - never synthesize a
// preset for it. Kept here only to keep index alignment with ams_infos; the sync
// drops it so no filament slot is created or retained for an empty tray.
ams_filament_presets.push_back("");
ams_filament_colors.push_back("");
ams_filament_color_types.push_back("");
ams_multi_color_filment.push_back({});
} else if (use_map) {
ams_filament_presets.push_back("Generic PLA");//for unknow matieral
auto default_unknown_color = "#CECECE";
ams_filament_colors.push_back(default_unknown_color);
@@ -3884,12 +3912,6 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
filament_multi_color.push_back(default_unknown_color);
}
ams_multi_color_filment.push_back(filament_multi_color);
} else if (is_placeholder) {
// Orca: push placeholders to keep index alignment with ams_infos
ams_filament_presets.push_back("");
ams_filament_colors.push_back("");
ams_filament_color_types.push_back("");
ams_multi_color_filment.push_back({});
}
continue;
}
@@ -4253,6 +4275,10 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
result_multi_colors.push_back(
i < ams_multi_color_filment.size() ? ams_multi_color_filment[i]
: std::vector<std::string>{ams_filament_colors[i]});
} else if (i < ams_infos.size() && ams_infos[i].is_placeholder) {
// Orca: an empty AMS slot is a placeholder, not a filament, so drop it instead of
// keeping or filling a slot for it. The device AMS panel still shows the empty tray.
continue;
} else if (i < exist_presets.size()) {
// Empty tray or beyond tray count: keep existing filament
result_colors.push_back(exist_colors[i]);
@@ -4277,8 +4303,8 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
filament_color_type->values = result_color_types;
this->filament_presets = result_presets;
ams_multi_color_filment = result_multi_colors;
filament_map->values.resize(total, 1);
filament_volume_map->values.resize(total, static_cast<int>(NozzleVolumeType::nvtStandard));
filament_map->values.resize(result_presets.size(), 1);
filament_volume_map->values.resize(result_presets.size(), static_cast<int>(NozzleVolumeType::nvtStandard));
} else {
// BBL: existing wholesale replace
filament_color->values = ams_filament_colors;
-15
View File
@@ -10845,21 +10845,6 @@ void set_filament_dev_options(DynamicPrintConfig &config, const std::vector<cons
}
}
void resize_mixed_filament_metadata(DynamicPrintConfig &config, size_t old_slot_count, size_t new_slot_count)
{
auto resize = [old_slot_count, new_slot_count](auto *opt) {
opt->values.resize(std::min(old_slot_count, opt->values.size()));
opt->values.resize(new_slot_count);
};
resize(config.option<ConfigOptionBools>("filament_is_mixed", true));
resize(config.option<ConfigOptionStrings>("filament_mixed_components", true));
resize(config.option<ConfigOptionStrings>("filament_mixed_sublayer_ratios", true));
resize(config.option<ConfigOptionBools>("filament_mixed_gradient", true));
resize(config.option<ConfigOptionStrings>("filament_mixed_gradient_range", true));
resize(config.option<ConfigOptionStrings>("filament_mixed_gradient_curve", true));
resize(config.option<ConfigOptionBools>("filament_mixed_gradient_per_part", true));
}
//used for object/region config
//use the smallest of multiple to single
-4
View File
@@ -933,10 +933,6 @@ extern std::set<std::string> filament_dev_options;
// filament_configs, one config per filament in slot order, as the filaments' values one after another.
void set_filament_dev_options(DynamicPrintConfig &config, const std::vector<const DynamicPrintConfig *> &filament_configs);
// Orca: sizes the per-slot mixed-colour metadata options to new_slot_count, keeping the first
// old_slot_count values; an option the config lacks is created.
void resize_mixed_filament_metadata(DynamicPrintConfig &config, size_t old_slot_count, size_t new_slot_count);
extern void update_static_print_config_from_dynamic(ConfigBase& config, const DynamicPrintConfig& dest_config, std::vector<int> variant_index, std::set<std::string>& key_set1, int stride = 1);
extern void compute_filament_override_value(const std::string& opt_key, const ConfigOption *opt_old_machine, const ConfigOption *opt_new_machine, const ConfigOption *opt_new_filament, const DynamicPrintConfig& new_full_config,
t_config_option_keys& diff_keys, DynamicPrintConfig& filament_overrides, std::vector<int>& f_map_indices);
+1
View File
@@ -49,6 +49,7 @@ public:
using TColor = typename PixelRenderer::color_type;
using TValue = typename TColor::value_type;
using TPixel = typename PixelRenderer::pixel_type;
using TRawBuffer = agg::rendering_buffer;
protected:
+1
View File
@@ -44,6 +44,7 @@
#include "TDataStd_Name.hxx"
#include "BRepBuilderAPI_Transform.hxx"
#include "TopExp_Explorer.hxx"
#include "TopExp_Explorer.hxx"
#include "BRep_Tool.hxx"
#include "Font_BRepFont.hxx"
#include "Font_BRepTextBuilder.hxx"
@@ -17,6 +17,9 @@ typedef std::vector<Color> ColorList;
typedef std::array<double, 3> ColorDouble;
typedef std::array<std::size_t, 3> RGB;
// Function pointer type that points to a specific color-difference function based on the chosen method.
using DistanceFunction = double (*)(const Color&, const Color&);
// Color space used for computing color differences.
enum struct ColorDifferenceMethod : std::size_t {
RGB = 0, // Simplest and fastest
+2 -3
View File
@@ -24,6 +24,8 @@
namespace Slic3r { namespace tex2color {
namespace PMP = CGAL::Polygon_mesh_processing;
// Default upper bound on the number of half-edges in any single boundary cycle
// that CloseBoundariesAndRepairManifoldness will attempt to triangulate. The
// cost of triangulate_hole grows non-linearly with cycle length, so this caps
@@ -59,7 +61,6 @@ struct BoundaryEdgeStats
// any pre-processing (e.g. stitch_borders) needed for the count to be meaningful.
inline BoundaryEdgeStats ComputeBoundaryEdgeStats(const cgalutils::CGALMesh& cgal_mesh)
{
namespace PMP = CGAL::Polygon_mesh_processing;
using CGALMesh = cgalutils::CGALMesh;
using HalfedgeDescriptor = boost::graph_traits<CGALMesh>::halfedge_descriptor;
@@ -86,7 +87,6 @@ inline BoundaryEdgeStats ComputeBoundaryEdgeStats(const cgalutils::CGALMesh& cga
// boundary statistics; entering this function always triggers triangulation.
inline void CloseBoundariesAndRepairManifoldness(cgalutils::CGALMesh& cgal_mesh)
{
namespace PMP = CGAL::Polygon_mesh_processing;
using CGALMesh = cgalutils::CGALMesh;
using HalfedgeDescriptor = boost::graph_traits<CGALMesh>::halfedge_descriptor;
using FaceDescriptor = boost::graph_traits<CGALMesh>::face_descriptor;
@@ -112,7 +112,6 @@ inline bool RepairMesh(const TriMesh& mesh,
AlgoCancelCallback cancel_callback = nullptr,
const RepairSetting& setting = RepairSetting{})
{
namespace PMP = CGAL::Polygon_mesh_processing;
using Clock = std::chrono::steady_clock;
auto elapsed_ms = [](Clock::time_point t0) {
return std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now() - t0).count();
+2
View File
@@ -25,6 +25,8 @@
#include <initializer_list>
#include <string_view>
#include <regex>
#include <string_view>
#include <algorithm>
#include <boost/system/error_code.hpp>
#include <boost/algorithm/string.hpp>
+2
View File
@@ -432,6 +432,8 @@ inline IntegerOnly<I, I> fast_round_up(double a)
return a == 0.49999999999999994 ? I(0) : I(floor(a + 0.5));
}
template<class T> using SamePair = std::pair<T, T>;
} // namespace Slic3r
#endif
+2
View File
@@ -17,6 +17,7 @@
#include <wx/dataview.h>
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/dataview.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
@@ -29,6 +30,7 @@
#include <wx/dialog.h>
#include <wx/popupwin.h>
#include <wx/spinctrl.h>
#include <wx/artprov.h>
#include <wx/wrapsizer.h>
#include "GUI_Utils.hpp"
+3
View File
@@ -19,17 +19,20 @@
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/sizer.h>
#include <wx/grid.h>
#include <wx/dataview.h>
#include <wx/panel.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/bmpbuttn.h>
#include <wx/button.h>
#include <wx/statbox.h>
#include <wx/tglbtn.h>
#include <wx/popupwin.h>
#include <wx/spinctrl.h>
#include <wx/artprov.h>
#include <map>
+3
View File
@@ -28,6 +28,7 @@
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/sizer.h>
#include <wx/grid.h>
#include <wx/dataview.h>
#include <wx/panel.h>
@@ -35,12 +36,14 @@
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/bmpbuttn.h>
#include <wx/button.h>
#include <wx/gbsizer.h>
#include <wx/statbox.h>
#include <wx/tglbtn.h>
#include <wx/popupwin.h>
#include <wx/spinctrl.h>
#include <wx/artprov.h>
#include <wx/webrequest.h>
#include <map>
#include <vector>
+1
View File
@@ -8,6 +8,7 @@
#include <memory>
#include <string>
#include <functional>
#include <string>
#include "Jobs/ProgressIndicator.hpp"
+1
View File
@@ -10,6 +10,7 @@
#include <memory>
#include <string>
#include <functional>
#include <string>
#include <wx/string.h>
#include "Jobs/ProgressIndicator.hpp"
+1
View File
@@ -9,6 +9,7 @@
#include <memory>
#include <string>
#include <functional>
#include <string>
#include <wx/hyperlink.h>
#include "Jobs/ProgressIndicator.hpp"
+1
View File
@@ -9,6 +9,7 @@
#include <memory>
#include <string>
#include <functional>
#include <string>
#include <wx/hyperlink.h>
#include "Jobs/ProgressIndicator.hpp"
@@ -18,6 +18,7 @@
#include <wx/string.h>
#include <wx/toplevel.h>
#include <wx/timer.h>
#include "Widgets/Button.hpp"
#include "CapsuleButton.hpp"
namespace Slic3r::GUI { struct IntEvent; }
+1
View File
@@ -46,6 +46,7 @@
#include "libslic3r/Model.hpp"
#include "libslic3r/Polygon.hpp"
#include "MainFrame.hpp"
#include "GUI_App.hpp"
#include "Plater.hpp"
#include "Jobs/BoostThreadWorker.hpp"
#include "Jobs/PlaterWorker.hpp"
+1
View File
@@ -29,6 +29,7 @@
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/format.hpp"
#include "slic3r/Utils/Bonjour.hpp"
#include "slic3r/GUI/Widgets/DialogButtons.hpp"
+2
View File
@@ -11,6 +11,7 @@
#include <wx/dataview.h>
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/dataview.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
@@ -23,6 +24,7 @@
#include <wx/dialog.h>
#include <wx/popupwin.h>
#include <wx/spinctrl.h>
#include <wx/artprov.h>
#include <wx/wrapsizer.h>
#include "GUI_Utils.hpp"
+1
View File
@@ -1,3 +1,4 @@
// #include "libslic3r/GCodeSender.hpp"
#include "ConfigManipulation.hpp"
#include "I18N.hpp"
#include "GUI_App.hpp"
+3
View File
@@ -198,3 +198,6 @@ template<> struct std::hash<NozzleDef>
return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
};
};
// key(extruder_id) -> { key1(nozzle type info), val1( number of the nozzle type)}
using ExtruderNozzleInfos = std::unordered_map<int, std::unordered_map<NozzleDef, int>>;
@@ -15,6 +15,7 @@
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <wx/string.h>
#include <wx/colour.h>
+1
View File
@@ -11,6 +11,7 @@
#include "GUI_App.hpp"
#include "DeviceErrorDialog.hpp"
#include "Plater.hpp"
#include "GUI_App.hpp"
#include "ReleaseNote.hpp"
#include <string>
#include <boost/log/trivial.hpp>
+5 -9
View File
@@ -2427,15 +2427,11 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size)
if (m_canvas_type == ECanvasType::CanvasView3D) {
// m_show_bed gates the plate list too: hiding the bed but leaving its grid and outline
// floating would read as a rendering fault rather than a deliberate view option.
if (show_bed) {
bool show_axes = m_show_world_axes;
#ifdef SLIC3R_CAD
// Design tab: while its reference planes are up they draw their own axes from the modeling
// origin, where the bed's triad would otherwise sit on top of them.
show_axes = show_axes && !(m_design_sketch_tool != nullptr && m_design_sketch_tool->draws_reference_axes());
#endif
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), show_axes);
}
// Design tab: while its reference planes are up they draw their own axes from the modeling
// origin, where the bed's triad would otherwise sit on top of them.
if (show_bed)
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(),
m_show_world_axes && !(m_design_sketch_tool != nullptr && m_design_sketch_tool->draws_reference_axes()));
m_frame_profiler.mark("bed");
if (show_bed) //BBS: add outline logic
_render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, only_body, hover_id, true, show_grid);
+3
View File
@@ -183,10 +183,13 @@ wxDECLARE_EVENT(EVT_GLCANVAS_PLATE_NAME_CHANGE, SimpleEvent);
//BBS: declare EVT_GLCANVAS_PLATE_SELECT
wxDECLARE_EVENT(EVT_GLCANVAS_PLATE_SELECT, SimpleEvent);
using Vec2dEvent = Event<Vec2d>;
// _bool_ value is used as a indicator of selection in the 3DScene
using RBtnEvent = Event<std::pair<Vec2d, bool>>;
using RBtnPlateEvent = Event<std::pair<Vec2d, int>>;
template <size_t N> using Vec2dsEvent = ArrayEvent<Vec2d, N>;
using Vec3dEvent = Event<Vec3d>;
template <size_t N> using Vec3dsEvent = ArrayEvent<Vec3d, N>;
using HeightProfileSmoothEvent = Event<HeightProfileSmoothingParams>;
+1
View File
@@ -46,6 +46,7 @@
#include "nanosvg/nanosvgrast.h"
#include "libslic3r/Utils.hpp"
#include "GUI_App.hpp"
#include <boost/log/trivial.hpp>
#include <wx/dcgraph.h>
#include <wx/dcmemory.h>
+1
View File
@@ -50,6 +50,7 @@
#include "AboutDialog.hpp"
#include "MsgDialog.hpp"
#include "Plater.hpp"
#include "format.hpp"
#include "WebUserLoginDialog.hpp"
+2
View File
@@ -172,10 +172,12 @@
#include "libslic3r/Utils.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/I18N.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/InstanceLock.hpp"
#include "libslic3r/Thread.hpp"
#include "libslic3r/miniz_extension.hpp"
#include "libslic3r/Utils.hpp"
#include "slic3r/plugin/PluginManager.hpp"
#include "slic3r/plugin/host/PluginHostUi.hpp"
#include "slic3r/plugin/PythonInterpreter.hpp"
@@ -523,15 +523,7 @@ void GLGizmoTextureDisplacement::render_painter_gizmo()
// volume for a shaded pass that then draws nothing is what made the model vanish - most obviously
// with zero layers, but equally with a layer that has no texture picked yet.
const bool use_shaded = m_use_shaded_preview && m_shaded_preview_glmodel.is_initialized() && shaded_preview_ready();
// Checker/Distortion are built from the *base* patch and drawn with a polygon offset, which biases
// depth values - it does not move the geometry. It therefore cannot win against a surface that
// genuinely stands in front, and the displaced preview does exactly that: it rises above the base
// surface by the layer's depth. Drawn underneath a UV-check overlay it simply occludes it, which is
// why those two modes looked like they did nothing. Leave it out and let the undisplaced volume show
// through instead (toggle_model_objects_visibility below) - that one *is* coincident with the
// overlay, which is what the offset assumes, and it is the surface whose mapping is being inspected.
const bool use_true_preview = !use_shaded && m_uv_check_mode == UVCheckMode::None &&
m_preview_glmodel.is_initialized();
const bool use_true_preview = !use_shaded && m_preview_glmodel.is_initialized();
// In Checker/Distortion mode the UV-check overlay *is* the surface visualization the user is
// looking at, so the opaque paint-selection highlight must not be drawn on top of it - same
// reasoning as skipping it for the shaded preview (see bug #12). Without this the painted area
@@ -559,13 +551,7 @@ void GLGizmoTextureDisplacement::render_painter_gizmo()
render_triangles(selection);
glsafe(::glDisable(GL_POLYGON_OFFSET_FILL));
}
} else {
// render_triangles() *is* the model in a painter gizmo (it draws every model-part volume with the
// selector's colours), not an overlay on top of one - so it still has to run under a UV-check
// overlay, or nothing draws the surface at all and the checker floats alone over an empty scene.
// Deliberately without the depth bias the branch above applies: the checker/heatmap is drawn later
// with its own -1 offset and has to win against this. Biasing both by the same amount is what made
// the painted area cover the checker and is why this call used to be skipped outright.
} else if (show_paint_overlay) {
render_triangles(selection);
}
@@ -5756,20 +5742,14 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
const int cur_mode = m_use_shaded_preview ? 1 :
m_uv_check_mode == UVCheckMode::Checker ? 2 :
m_uv_check_mode == UVCheckMode::Distortion ? 3 : 0;
int new_mode = cur_mode;
bool wf_toggle = false;
bool open_uv_editor = false;
// Checker and Distortion both draw *the unwrap* - the first the texture grid laid over it, the second
// its stretch - so they only mean anything for a layer mapped with Unwrap (LSCM). On the default
// triplanar mapping (or cylindrical / spherical / from view) they are faded out with the reason in the
// tooltip, rather than being offered and then showing nothing.
const wxString uv_view_na = active == nullptr ? _L("Add a layer first.") :
active->projection_method != TextureProjectionMethod::LSCM ?
_L("Only for a layer mapped with Unwrap (LSCM) - set the "
"active layer's Mapping to Unwrap to use this view.") :
wxString();
// Either view over a layer that stopped being an unwrap shows nothing at all, so fall back to Normal.
if ((cur_mode == 2 || cur_mode == 3) && !uv_view_na.empty())
int new_mode = cur_mode;
bool wf_toggle = false;
const wxString distortion_na = active == nullptr ? _L("Add a layer first.") :
active->projection_method != TextureProjectionMethod::LSCM ?
_L("Needs the active layer mapped with Unwrap (LSCM).") :
wxString();
// Distortion over a layer that stopped being an unwrap shows nothing at all, so fall back to Normal.
if (cur_mode == 3 && !distortion_na.empty())
new_mode = 0;
const float x0 = ImGui::GetCursorPosX();
@@ -5788,20 +5768,12 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
ImGui::SameLine(0.f, gap_s);
if (icon_toggle(703, "texture_displacement_checker.svg", cur_mode == 2, icon_md, _L("Checker"),
_L("Checker - a test grid instead of the texture. Where the squares stay square the "
"texture is undistorted; where they stretch, it will too. Opens the UV editor if "
"it is closed"),
uv_view_na)) {
new_mode = 2;
open_uv_editor = true;
}
"texture is undistorted; where they stretch, it will too")))
new_mode = 2;
ImGui::SameLine(0.f, gap_s);
if (icon_toggle(704, "texture_displacement_distortion.svg", cur_mode == 3, icon_md, _L("Distortion"),
_L("Distortion - blue-to-red stretch heatmap over the unwrap. Opens the UV editor if "
"it is closed"),
uv_view_na)) {
new_mode = 3;
open_uv_editor = true;
}
_L("Distortion - blue-to-red stretch heatmap over the unwrap"), distortion_na))
new_mode = 3;
vsep(icon_md);
if (icon_toggle(705, "texture_displacement_wireframe.svg", m_wireframe_overlay, icon_md, _L("Wireframe"),
_L("Wireframe - overlay the mesh edges; independent of the view above")))
@@ -5816,13 +5788,6 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
hover_tip(_u8L("Rebuilds the preview as soon as anything changes. Turn it off on a heavy model if painting "
"or dragging a slider starts to stutter - the preview then waits until you let go."));
// Both are views of the unwrap, so picking one brings the UV editor up with it - including when that
// view is already the active one and only the pane is missing.
if (open_uv_editor && !m_show_uv_editor) {
m_show_uv_editor = true;
if (new_mode == cur_mode)
update_uv_editor(); // otherwise apply_view_mode() below does it
}
if (new_mode != cur_mode)
apply_view_mode(new_mode);
if (wf_toggle) {
+1
View File
@@ -11,6 +11,7 @@
#include "libslic3r/Config.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/Preset.hpp"
#include "libslic3r/Config.hpp"
#include "libslic3r/PrintConfig.hpp"
#include <cstdint>
+1
View File
@@ -66,6 +66,7 @@
#include <nanosvg/nanosvg.h>
#include <nanosvg/nanosvgrast.h>
#include "OpenGLManager.hpp"
#include "GUI_App.hpp"
namespace Slic3r {
namespace GUI {
+1
View File
@@ -113,6 +113,7 @@
#include "MsgDialog.hpp"
#include "Notebook.hpp"
#include "GUI_Factories.hpp"
#include "GUI_ObjectList.hpp"
#include "NotificationManager.hpp"
#include "MarkdownTip.hpp"
#include "NetworkTestDialog.hpp"
+1
View File
@@ -21,6 +21,7 @@
#include "wx/evtloop.h"
#include "MainFrame.hpp"
#include "GUI_App.hpp"
#include "Plater.hpp"
using json = nlohmann::json;
+1
View File
@@ -45,6 +45,7 @@
#include "MainFrame.hpp"
#include "MediaPlayCtrl.h"
#include "MediaFilePanel.h"
#include "Plater.hpp"
#include "BindDialog.hpp"
#include "DeviceCore/DevManager.h"
+3
View File
@@ -23,6 +23,7 @@
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/sizer.h>
#include <wx/grid.h>
#include <wx/dataview.h>
#include <wx/panel.h>
@@ -30,12 +31,14 @@
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/bmpbuttn.h>
#include <wx/button.h>
#include <wx/gbsizer.h>
#include <wx/statbox.h>
#include <wx/tglbtn.h>
#include <wx/popupwin.h>
#include <wx/spinctrl.h>
#include <wx/artprov.h>
#include <wx/webrequest.h>
#include <map>
#include <vector>
+1
View File
@@ -4,6 +4,7 @@
#include "libslic3r/Utils.hpp"
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "I18N.hpp"
#include "libslic3r_version.h"
#include "slic3r/Utils/Http.hpp"
#include "libslic3r/AppConfig.hpp"
+1
View File
@@ -54,6 +54,7 @@ struct Option {
Option(const ConfigOptionDef& _opt, t_config_option_key id) :
opt(_opt), opt_id(id) {}
};
using t_option = std::unique_ptr<Option>; //!
/// Represents option lines
class Line : public UndoValueUIManager
+2
View File
@@ -108,6 +108,7 @@ class PlaterPresetComboBox;
class PartPlateList;
class SyncNozzleAndAmsDialog;
class FinishSyncAmsDialog;
using t_optgroups = std::vector <std::shared_ptr<ConfigOptionsGroup>>;
class Plater;
enum class ActionButtonType : int;
@@ -324,6 +325,7 @@ private:
class Plater: public wxPanel
{
public:
using fs_path = boost::filesystem::path;
Plater(wxWindow *parent, MainFrame *main_frame);
Plater(Plater &&) = delete;
+1
View File
@@ -30,6 +30,7 @@
#include "MainFrame.hpp"
#include <miniz.h>
#include <OrcaCloudServiceAgent.hpp>
#include <wx/event.h>
#include <wx/utils.h>
using json = nlohmann::json;
+1
View File
@@ -79,6 +79,7 @@
#include "libslic3r_version.h"
#include "wxExtensions.hpp"
#include <nlohmann/json.hpp>
#include "libslic3r/Preset.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "slic3r/GUI/Widgets/Button.hpp"
+1
View File
@@ -4,6 +4,7 @@
#include <memory>
#include <string>
#include <functional>
#include <string>
#include "Jobs/ProgressIndicator.hpp"
+3
View File
@@ -19,6 +19,7 @@
#include <wx/dataview.h>
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/dataview.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
@@ -31,8 +32,10 @@
#include <wx/dialog.h>
#include <wx/popupwin.h>
#include <wx/spinctrl.h>
#include <wx/artprov.h>
#include <wx/wrapsizer.h>
#include <wx/event.h>
#include <wx/hyperlink.h>
#include <wx/richtext/richtextctrl.h>
#include "AmsMappingPopup.hpp"
+2
View File
@@ -25,6 +25,7 @@
#include <wx/dataview.h>
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/dataview.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
@@ -37,6 +38,7 @@
#include <wx/dialog.h>
#include <wx/popupwin.h>
#include <wx/spinctrl.h>
#include <wx/artprov.h>
#include <wx/wrapsizer.h>
#include <wx/srchctrl.h>
+2
View File
@@ -16,6 +16,7 @@
#include <wx/dataview.h>
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/dataview.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
@@ -28,6 +29,7 @@
#include <wx/dialog.h>
#include <wx/popupwin.h>
#include <wx/spinctrl.h>
#include <wx/artprov.h>
#include <wx/wrapsizer.h>
#include <wx/srchctrl.h>
+2
View File
@@ -20,6 +20,7 @@
#include <wx/dataview.h>
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/dataview.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
@@ -32,6 +33,7 @@
#include <wx/dialog.h>
#include <wx/popupwin.h>
#include <wx/spinctrl.h>
#include <wx/artprov.h>
#include <wx/wrapsizer.h>
#include <wx/srchctrl.h>
+3
View File
@@ -1,3 +1,4 @@
// #include "libslic3r/GCodeSender.hpp"
//#include "slic3r/Utils/Serial.hpp"
#include "Tab.hpp"
#include "PresetHints.hpp"
@@ -101,6 +102,8 @@
#include "Widgets/MultiNozzleSync.hpp"
#include "Widgets/SwitchButton.hpp"
#include "Widgets/TabCtrl.hpp"
#include "Widgets/ComboBox.hpp"
#include "Search.hpp"
#include "BedShapeDialog.hpp"
#include "libslic3r/GCode/Thumbnails.hpp"
#include "WipeTowerDialog.hpp"
+30 -138
View File
@@ -199,7 +199,6 @@ UVEditorCanvas::UVEditorCanvas(wxWindow *parent)
Bind(wxEVT_MIDDLE_DOWN, &UVEditorCanvas::on_mouse, this);
Bind(wxEVT_MIDDLE_UP, &UVEditorCanvas::on_mouse, this);
Bind(wxEVT_MOTION, &UVEditorCanvas::on_mouse, this);
Bind(wxEVT_MOUSE_CAPTURE_LOST, &UVEditorCanvas::on_capture_lost, this);
Bind(wxEVT_MOUSEWHEEL, &UVEditorCanvas::on_mouse, this);
Bind(wxEVT_LEAVE_WINDOW, &UVEditorCanvas::on_leave, this);
Bind(wxEVT_KEY_DOWN, &UVEditorCanvas::on_key, this);
@@ -751,47 +750,10 @@ void UVEditorCanvas::end_gesture()
m_rot_raw_deg = 0.f;
m_rot_applied_deg = 0.f;
m_modal_scale_accum = 1.f;
drop_mouse();
}
void UVEditorCanvas::cancel_gesture()
{
// Undo what the gesture already applied live, as the Esc path does, and commit nothing.
if (m_on_island_edit) {
if (m_gesture == Gesture::RotateIslandModal && m_rot_applied_deg != 0.f)
m_on_island_edit(m_selected_island, Vec2f::Zero(), -m_rot_applied_deg, 1.f, false);
if (m_gesture == Gesture::ScaleIslandModal && m_modal_scale_accum != 1.f)
m_on_island_edit(m_selected_island, Vec2f::Zero(), 0.f, 1.f / m_modal_scale_accum, false);
}
m_gesture = Gesture::None;
m_rot_raw_deg = 0.f;
m_rot_applied_deg = 0.f;
m_modal_scale_accum = 1.f;
m_vertex_edit_moved = false;
}
void UVEditorCanvas::grab_mouse()
{
if (!HasCapture())
CaptureMouse();
}
void UVEditorCanvas::drop_mouse()
{
if (HasCapture())
ReleaseMouse();
}
// The capture was taken from us (a dialog opened, another application grabbed the pointer). wx
// requires this to cancel the gesture: no commit, no Skip(), and no ReleaseMouse() - the capture is
// already gone, and releasing it again would unbalance the stack.
void UVEditorCanvas::on_capture_lost(wxMouseCaptureLostEvent &)
{
cancel_gesture();
Refresh();
}
void UVEditorCanvas::on_key(wxKeyEvent &evt)
{
const int key = evt.GetKeyCode();
@@ -970,7 +932,7 @@ void UVEditorCanvas::on_mouse(wxMouseEvent &evt)
}
m_gesture = (m_selected_island >= 0) ? Gesture::MoveIsland : Gesture::Pan;
}
grab_mouse();
CaptureMouse();
Refresh();
} else if (type == wxEVT_RIGHT_DOWN && m_selected_island >= 0 && m_select_mode == SelectMode::Island) {
const Vec2f rel = screen_to_uv(pos) - island_centroid(m_selected_island);
@@ -980,16 +942,12 @@ void UVEditorCanvas::on_mouse(wxMouseEvent &evt)
m_rot_base_deg = island_rotation_deg(m_selected_island);
m_rot_display_deg = m_rot_base_deg;
m_gesture_last_angle = std::atan2(rel.y(), rel.x());
grab_mouse();
CaptureMouse();
} else if (type == wxEVT_MIDDLE_DOWN) {
m_gesture = Gesture::Pan;
m_drag_last_px = pos;
grab_mouse();
CaptureMouse();
} else if (type == wxEVT_LEFT_UP || type == wxEVT_RIGHT_UP || type == wxEVT_MIDDLE_UP) {
// The drag is over either way. A modal R/S keeps running until a click confirms it, but it
// tracks the pointer over this canvas and needs no capture to do so, so the capture goes back
// here rather than waiting for that click - which may never come.
drop_mouse();
if (m_gesture != Gesture::RotateIslandModal && m_gesture != Gesture::ScaleIslandModal) {
end_gesture();
Refresh();
@@ -1777,7 +1735,7 @@ public:
bool toggle, bool accent = false, int size_dip = 26)
: wxWindow(parent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE | wxFULL_REPAINT_ON_RESIZE)
, m_icon_name(icon), m_icon_dip(size_dip >= 26 ? 16 : 14), m_label(label), m_toggle(toggle), m_accent(accent)
, m_size_dip(size_dip), m_tip(tip)
, m_size_dip(size_dip)
{
SetBackgroundStyle(wxBG_STYLE_PAINT);
SetToolTip(tip);
@@ -1790,7 +1748,7 @@ public:
Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent &) { m_hover = true; Refresh(); });
Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent &) { m_hover = false; m_pressed = false; Refresh(); });
Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &) {
if (usable()) {
if (IsEnabled()) {
m_pressed = true;
Refresh();
}
@@ -1799,7 +1757,7 @@ public:
const bool was_pressed = m_pressed;
m_pressed = false;
Refresh();
if (!was_pressed || !usable() || !GetClientRect().Contains(e.GetPosition()))
if (!was_pressed || !IsEnabled() || !GetClientRect().Contains(e.GetPosition()))
return;
if (m_toggle)
m_on = !m_on;
@@ -1839,30 +1797,6 @@ public:
Refresh();
return changed;
}
// Soft-disable: the button is drawn faded and swallows clicks, but stays a live window, so hovering it
// still raises its tooltip - now with `reason` appended, saying what to do to make it usable. A window
// really disabled with Enable(false) gets no mouse events at all on GTK and MSW, which leaves the user
// guessing; this is the same trade-off the gizmo panel's icon_toggle() makes with its `unavailable`.
// An empty reason makes the button usable again.
void SetUnavailable(const wxString &reason)
{
if (reason == m_unavailable)
return;
m_unavailable = reason;
SetToolTip(reason.empty() || m_tip.empty() ? m_tip : m_tip + "\n\n" + reason);
if (!m_unavailable.empty())
m_pressed = false; // a reason appearing mid-press cancels the press
Refresh();
}
// Replaces the plain tooltip, keeping whatever reason is currently appended to it.
void SetTip(const wxString &tip)
{
if (tip == m_tip)
return;
m_tip = tip;
SetToolTip(m_unavailable.empty() || m_tip.empty() ? m_tip : m_tip + "\n\n" + m_unavailable);
}
bool usable() const { return IsEnabled() && m_unavailable.empty(); }
protected:
wxSize DoGetBestSize() const override
@@ -1881,7 +1815,7 @@ private:
const PaneColors c = PaneColors::current();
const wxRect r = GetClientRect();
const wxColour teal(0x00, 0x96, 0x88);
const bool enabled = usable();
const bool enabled = IsEnabled();
wxColour fill = c.bg, border = c.frame, text = c.ink;
if (m_accent) {
@@ -1941,8 +1875,6 @@ private:
bool m_toggle = false;
bool m_accent = false;
int m_size_dip = 26;
wxString m_tip; // the tooltip without any m_unavailable reason appended
wxString m_unavailable; // non-empty: faded and unclickable, and why (see SetUnavailable())
bool m_on = false;
bool m_badge = false;
bool m_hover = false;
@@ -1988,9 +1920,6 @@ UVEditorPanel::UVEditorPanel(wxWindow *parent) : wxPanel(parent, wxID_ANY)
});
m_layer_name->SetMinSize(wxSize(FromDIP(30), -1));
m_tile = text(wxEmptyString, c.dim);
m_tile->SetToolTip(_L("The active layer's tile size: how much of the model one repeat of the texture covers. The "
"canvas is measured in tiles, so one grid cell is one repeat. Change it with Tiling in the "
"layer's settings."));
header->Add(m_thumb, 0, wxALIGN_CENTER_VERTICAL);
header->Add(m_layer_name, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, gap);
header->Add(m_tile, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, gap);
@@ -2044,19 +1973,9 @@ UVEditorPanel::UVEditorPanel(wxWindow *parent) : wxPanel(parent, wxID_ANY)
strip->Add(r, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, FromDIP(11));
strip->AddSpacer(FromDIP(7));
};
m_select[0] = tool(ID_UV_SELECT_ISLAND, "texture_displacement_uv_select_island",
_L("Island - work on whole islands. Click one to select it, then drag to move it, right-drag to "
"rotate it, or press R to rotate and S to scale with the mouse (click or Enter to confirm, Esc "
"to cancel)."),
true);
m_select[1] = tool(ID_UV_SELECT_VERTEX, "texture_displacement_uv_select_vertex",
_L("Vertex - drag vertices to reshape an island by hand; Shift adds to the selection, Ctrl "
"toggles one in or out of it."),
true);
m_select[2] = tool(ID_UV_SELECT_EDGE, "texture_displacement_uv_select_edge",
_L("Edge - drag edges to reshape an island by hand; Shift adds to the selection, Ctrl toggles "
"one in or out of it."),
true);
m_select[0] = tool(ID_UV_SELECT_ISLAND, "texture_displacement_uv_select_island", _L("Island - move, rotate and scale whole islands"), true);
m_select[1] = tool(ID_UV_SELECT_VERTEX, "texture_displacement_uv_select_vertex", _L("Vertex - drag vertices to reshape; Shift/Ctrl to multi-select"), true);
m_select[2] = tool(ID_UV_SELECT_EDGE, "texture_displacement_uv_select_edge", _L("Edge - drag edges to reshape; Shift/Ctrl to multi-select"), true);
strip_rule();
m_mark_seams = tool(ID_UV_MARK_SEAMS, "texture_displacement_uv_seam",
_L("Mark seams - click edges on the model to cut the unwrap along them. The edge under the cursor is "
@@ -2077,11 +1996,7 @@ UVEditorPanel::UVEditorPanel(wxWindow *parent) : wxPanel(parent, wxID_ANY)
m_clear_edits = tool(ID_UV_CLEAR_EDITS, "texture_displacement_uv_clear_edits",
_L("Clear UV edits - discard all manual vertex/edge moves and return the unwrap to its automatic shape"), false);
m_snap = tool(ID_UV_SNAP, "texture_displacement_uv_snap", _L("Snap - stick islands together when dragging one against another"), true);
m_frame = tool(ID_UV_FRAME, "texture_displacement_uv_frame",
_L("Frame all islands, fitting every one of them in view (Home or F).\n"
"Elsewhere on the canvas: scroll to zoom around the cursor, and middle-drag - or drag empty space - "
"to pan."),
false);
m_frame = tool(ID_UV_FRAME, "texture_displacement_uv_frame", _L("Frame all islands (Home)"), false);
strip->AddSpacer(FromDIP(4));
m_canvas = new UVEditorCanvas(this);
@@ -2092,7 +2007,6 @@ UVEditorPanel::UVEditorPanel(wxWindow *parent) : wxPanel(parent, wxID_ANY)
// ---- status line: the current gesture on the left, the unwrap summary on the right ----
m_status = text(wxEmptyString, c.dim, wxST_ELLIPSIZE_END);
m_status->SetToolTip(_L("What is selected, and the exact figures of the move, rotation or scale while you drag one."));
m_status->SetMinSize(wxSize(FromDIP(40), -1));
m_stats = text(wxEmptyString, c.dim);
auto *status = new wxBoxSizer(wxHORIZONTAL);
@@ -2168,25 +2082,19 @@ void UVEditorPanel::apply_state(const UVEditorCanvas::PaneState &s)
} else {
m_thumb->SetBitmap(wxNullBitmap);
}
// Every tool that cannot be used right now is faded with the reason appended to its tooltip, rather than
// being hard-disabled (which would hide the tooltip too - see UVToolButton::SetUnavailable()).
const wxString no_layer = s.has_layer ? wxString() :
_L("The pane follows the active texture layer, and that layer has to be mapped "
"with Unwrap (LSCM). Add a layer and set its Mapping to Unwrap.");
m_thumb->SetUnavailable(no_layer);
m_layer_name->SetToolTip(s.has_layer ? m_thumb->GetToolTipText() : no_layer);
m_thumb->Enable(s.has_layer);
for (int i = 0; i < 3; ++i) {
m_background[i]->SetValue(int(s.background) == i);
m_background[i]->SetUnavailable(no_layer);
m_background[i]->Enable(s.has_layer);
}
m_unwrap->SetUnavailable(no_layer);
m_unwrap->Enable(s.has_layer);
m_unwrap->SetBadge(s.unwrap_stale);
m_unwrap->SetTip(s.unwrap_stale ?
_L("Out of date - the paint, the seams or the seam angle changed since this unwrap was made. "
"Press to unwrap again.") :
_L("Flatten the painted area into UV islands. It is computed only when you press this, not on "
"every edit - so paint, change the seam angle or mark seams first, then press Unwrap."));
m_unwrap->SetToolTip(s.unwrap_stale ?
_L("Out of date - the paint, the seams or the seam angle changed since this unwrap was made. "
"Press to unwrap again.") :
_L("Flatten the painted area into UV islands. It is computed only when you press this, not on "
"every edit - so paint, change the seam angle or mark seams first, then press Unwrap."));
if (m_seam_angle->GetValue() != int(std::lround(s.seam_angle_deg)))
m_seam_angle->SetValue(int(std::lround(s.seam_angle_deg)));
@@ -2195,26 +2103,15 @@ void UVEditorPanel::apply_state(const UVEditorCanvas::PaneState &s)
m_connect->Enable(s.has_layer);
m_mark_seams->SetValue(s.mark_seams);
m_mark_seams->SetUnavailable(no_layer);
m_mark_seams->Enable(s.has_layer);
m_seam_path->SetValue(s.seam_path);
m_seam_path->SetUnavailable(!no_layer.empty() ? no_layer :
s.mark_seams ? wxString() :
_L("Turn Mark seams on first - Path is a quicker way of marking them."));
m_clear_seams->SetUnavailable(!no_layer.empty() ? no_layer :
s.has_seams ? wxString() :
_L("No seams are marked on this layer."));
m_clear_edits->SetUnavailable(!no_layer.empty() ? no_layer :
s.has_uv_edits ? wxString() :
_L("No islands have been reshaped by hand, so there is nothing to "
"discard."));
m_seam_path->Enable(s.has_layer && s.mark_seams);
m_clear_seams->Enable(s.has_layer && s.has_seams);
m_clear_edits->Enable(s.has_layer && s.has_uv_edits);
m_stats->SetLabel(s.unwrapped ? wxString::Format(_L("%d islands, %s faces"), s.island_count,
wxString(std::to_string(s.face_count))) :
wxString());
m_stats->SetToolTip(s.unwrapped ? _L("How the painted area came out of the unwrap: the number of separate pieces it "
"was cut into (at the seams and at edges sharper than the seam angle), and how "
"many triangles they hold in total.") :
wxString());
refresh_selection_tools();
if (relayout)
Layout();
@@ -2224,24 +2121,19 @@ void UVEditorPanel::refresh_selection_tools()
{
const bool has_islands = m_canvas->has_islands();
const int mode = int(m_canvas->select_mode());
// Faded rather than hard-disabled, so the tooltip still says what is missing (see apply_state()).
const wxString not_unwrapped = has_islands ? wxString() : _L("Press Unwrap first - there are no islands to work on yet.");
for (int i = 0; i < 3; ++i) {
m_select[i]->SetValue(i == mode);
m_select[i]->SetUnavailable(not_unwrapped);
m_select[i]->Enable(has_islands);
}
const bool island_picked = has_islands && m_canvas->select_mode() == UVEditorCanvas::SelectMode::Island &&
m_canvas->selected_island() >= 0;
const wxString no_island = !not_unwrapped.empty() ? not_unwrapped :
island_picked ? wxString() :
_L("Click an island on the canvas first, in Island mode.");
m_avg_scale->SetUnavailable(not_unwrapped);
m_cut->SetUnavailable(no_island);
m_join->SetUnavailable(no_island);
m_unjoin->SetUnavailable(no_island);
m_snap->SetUnavailable(not_unwrapped);
m_avg_scale->Enable(has_islands);
m_cut->Enable(island_picked);
m_join->Enable(island_picked);
m_unjoin->Enable(island_picked);
m_snap->Enable(has_islands);
m_snap->SetValue(m_canvas->snap_enabled());
m_frame->SetUnavailable(not_unwrapped);
m_frame->Enable(has_islands);
}
void UVEditorPanel::on_tool(wxCommandEvent &evt)
-11
View File
@@ -200,7 +200,6 @@ private:
void on_paint(wxPaintEvent &evt);
void on_size(wxSizeEvent &evt);
void on_mouse(wxMouseEvent &evt);
void on_capture_lost(wxMouseCaptureLostEvent &evt);
void on_key(wxKeyEvent &evt);
void on_leave(wxMouseEvent &evt); // drops the +/- cursor hint when the pointer leaves the canvas
void on_erase_background(wxEraseEvent &evt) {} // required to avoid flicker on MSW, deliberately a no-op
@@ -245,16 +244,6 @@ private:
// vertex of some *other* island, in texture-UV space. Zero if nothing is within reach (#2).
Vec2f snap_correction(int island) const;
void end_gesture();
// Cancels the gesture instead of finishing it: nothing is committed, and a modal rotate/scale is
// put back the way Esc puts it back. Used when the capture is taken away from us.
void cancel_gesture();
// One capture at a time, released exactly once. Pressing a second button mid-drag would otherwise
// nest a second capture that the single release on button-up cannot undo, and macOS never sends
// wxEVT_MOUSE_CAPTURE_LOST to recover from that. A leaked capture is not a local problem there:
// while any wx window holds one, wxOSX routes every mouse event to it and the application stops
// seeing enter/leave and motion entirely, which also takes its tooltips down.
void grab_mouse();
void drop_mouse();
// Rebuilds the status line from the current gesture/selection and pushes it to m_on_status.
void update_status();
// Re-picks what a click at `pos` would grab in the current select mode, and repaints when that changed.
+2
View File
@@ -20,6 +20,8 @@
#include "../GUI/DeviceCore/DevConfig.h"
#include "../GUI/DeviceCore/DevExtruderSystem.h"
#include "../GUI/DeviceCore/DevManager.h"
#include "../GUI/DeviceCore/DevStorage.h"
#include "libslic3r/FlushVolCalc.hpp"
#include "../GUI/Plater.hpp"
#include <memory>
#include "slic3r/GUI/Jobs/Worker.hpp"
+3
View File
@@ -365,6 +365,7 @@ void StyleManager::init_trunc_names(float max_width) {
#include "slic3r/GUI/Plater.hpp"
// for get DPI
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/MainFrame.hpp"
#include "slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp"
@@ -553,6 +554,8 @@ bool StyleManager::set_wx_font(const wxFont &wx_font, std::unique_ptr<FontFile>
return true;
}
#include <libslic3r/AppConfig.hpp>
#include "WxFontUtils.hpp"
#include "fast_float/fast_float.h"
// StylesSerializable
+1
View File
@@ -89,6 +89,7 @@ using fn_ft_job_get_result = ft_err(FT_CALL *)(FT_JobHandle *, uint32_t timeo
using fn_ft_tunnel_start_job = ft_err(FT_CALL *)(FT_TunnelHandle *, FT_JobHandle *);
using fn_ft_job_cancel = ft_err(FT_CALL *)(FT_JobHandle *);
using fn_ft_job_msg_destroy = void(FT_CALL *)(ft_job_msg *);
using fn_ft_job_set_msg_cb = ft_err(FT_CALL *)(FT_JobHandle *, void(FT_CALL *)(void *user, ft_job_msg msg), void *user);
using fn_ft_job_try_get_msg = ft_err(FT_CALL *)(FT_JobHandle *, ft_job_msg *out_msg);
using fn_ft_job_get_msg = ft_err(FT_CALL *)(FT_JobHandle *, uint32_t timeout_ms, ft_job_msg *out_msg);
+167 -84
View File
@@ -441,19 +441,7 @@ int MoonrakerPrinterAgent::set_queue_on_main_fn(QueueOnMainFn fn)
void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index, const std::vector<AmsTrayData>& trays)
{
// Look up MachineObject via DeviceManager
auto* dev_manager = GUI::wxGetApp().getDeviceManager();
if (!dev_manager) {
return;
}
MachineObject* obj = dev_manager->get_my_machine(device_info.dev_id);
if (!obj) {
return;
}
// Build BBL-format JSON for DevFilaSystemParser::ParseV1_0
nlohmann::json ams_json = nlohmann::json::object();
nlohmann::json ams_array = nlohmann::json::array();
// Calculate ams_exist_bits and tray_exist_bits
@@ -461,11 +449,9 @@ void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index,
unsigned long tray_exist_bits = 0;
for (int ams_id = 0; ams_id < ams_count; ++ams_id) {
ams_exist_bits |= (1 << ams_id);
nlohmann::json ams_unit = nlohmann::json::object();
ams_unit["id"] = std::to_string(ams_id);
ams_unit["info"] = "0002"; // treat as AMS_LITE
if (ams_id < 32) {
ams_exist_bits |= (1UL << ams_id);
}
nlohmann::json tray_array = nlohmann::json::array();
int max_slot_in_this_ams = std::min(3, max_lane_index - ams_id * 4);
@@ -481,43 +467,127 @@ void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index,
}
}
nlohmann::json tray_json = nlohmann::json::object();
tray_json["id"] = std::to_string(slot_id);
tray_json["tag_uid"] = "0000000000000000";
if (tray && tray->has_filament) {
tray_exist_bits |= (1 << slot_index);
tray_json["tray_info_idx"] = tray->tray_info_idx;
tray_json["tray_type"] = tray->tray_type;
tray_json["tray_color"] = normalize_color_value(tray->tray_color);
// Add temperature data if provided
if (tray->bed_temp > 0) {
tray_json["bed_temp"] = std::to_string(tray->bed_temp);
}
if (tray->nozzle_temp > 0) {
tray_json["nozzle_temp_max"] = std::to_string(tray->nozzle_temp);
}
} else {
tray_json["tray_info_idx"] = "";
tray_json["tray_type"] = "";
tray_json["tray_color"] = "00000000";
tray_json["tray_slot_placeholder"] = "1";
if (tray && tray->has_filament && slot_index < 32) {
tray_exist_bits |= (1UL << slot_index);
}
tray_array.push_back(tray_json);
static const AmsTrayData empty_tray;
tray_array.push_back(make_ams_tray_json(std::to_string(slot_id), tray ? *tray : empty_tray));
}
nlohmann::json ams_unit = nlohmann::json::object();
ams_unit["id"] = std::to_string(ams_id);
ams_unit["info"] = "0002"; // treat as AMS_LITE
ams_unit["tray"] = tray_array;
ams_array.push_back(ams_unit);
}
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::build_ams_payload: Parsed " << trays.size() << " trays";
submit_ams_payload(ams_array, ams_exist_bits, tray_exist_bits);
}
void MoonrakerPrinterAgent::build_ams_payload_grouped(const std::vector<AmsTrayData>& trays)
{
// One AMS box per extruder, best effort. Lane-data changers (AFC, recent Happy Hare) report an
// extruder per lane but no physical box layout, so a box here is a logical feed group, not a
// discovered unit. Lanes without an extruder fall back to the main extruder.
std::map<int, std::vector<const AmsTrayData*>> boxes;
for (const auto& tray : trays) {
boxes[tray.extruder_id].push_back(&tray);
}
nlohmann::json ams_array = nlohmann::json::array();
unsigned long ams_exist_bits = 0;
unsigned long tray_exist_bits = 0;
int ams_id = 0;
for (auto& [extruder_id, lanes] : boxes) {
if (ams_id < 32) {
ams_exist_bits |= (1UL << ams_id);
}
// Keep a deterministic slot order within the box.
std::stable_sort(lanes.begin(), lanes.end(),
[](const AmsTrayData* a, const AmsTrayData* b) { return a->slot_index < b->slot_index; });
nlohmann::json tray_array = nlohmann::json::array();
for (size_t slot = 0; slot < lanes.size(); ++slot) {
const AmsTrayData& tray = *lanes[slot];
int bit = ams_id * 4 + static_cast<int>(slot);
if (tray.has_filament && bit < 32) {
tray_exist_bits |= (1UL << bit);
}
tray_array.push_back(make_ams_tray_json(std::to_string(slot), tray));
}
nlohmann::json ams_unit = nlohmann::json::object();
ams_unit["id"] = std::to_string(ams_id);
// info: bits 0-3 = unit type (2 = AMS_LITE), bits 8-11 = bound extruder id.
const int info_val = 2 | ((extruder_id & 0x0F) << 8);
const char* hex_digits = "0123456789ABCDEF";
std::string info = "0000";
info[3] = hex_digits[info_val & 0x0F];
info[2] = hex_digits[(info_val >> 4) & 0x0F];
info[1] = hex_digits[(info_val >> 8) & 0x0F];
info[0] = hex_digits[(info_val >> 12) & 0x0F];
ams_unit["info"] = info;
ams_unit["tray"] = tray_array;
ams_array.push_back(ams_unit);
++ams_id;
}
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::build_ams_payload_grouped: Parsed " << trays.size()
<< " trays in " << boxes.size() << " extruder box(es)";
submit_ams_payload(ams_array, ams_exist_bits, tray_exist_bits);
}
nlohmann::json MoonrakerPrinterAgent::make_ams_tray_json(const std::string& tray_id, const AmsTrayData& tray)
{
nlohmann::json tray_json = nlohmann::json::object();
tray_json["id"] = tray_id;
tray_json["tag_uid"] = "0000000000000000";
if (tray.has_filament) {
tray_json["tray_info_idx"] = tray.tray_info_idx;
tray_json["tray_type"] = tray.tray_type;
tray_json["tray_color"] = normalize_color_value(tray.tray_color);
// Add temperature data if provided
if (tray.bed_temp > 0) {
tray_json["bed_temp"] = std::to_string(tray.bed_temp);
}
if (tray.nozzle_temp > 0) {
tray_json["nozzle_temp_max"] = std::to_string(tray.nozzle_temp);
}
} else {
tray_json["tray_info_idx"] = "";
tray_json["tray_type"] = "";
tray_json["tray_color"] = "00000000";
tray_json["tray_slot_placeholder"] = "1";
}
return tray_json;
}
void MoonrakerPrinterAgent::submit_ams_payload(const nlohmann::json& ams_array, unsigned long ams_exist_bits, unsigned long tray_exist_bits)
{
// Look up MachineObject via DeviceManager
auto* dev_manager = GUI::wxGetApp().getDeviceManager();
if (!dev_manager) {
return;
}
MachineObject* obj = dev_manager->get_my_machine(device_info.dev_id);
if (!obj) {
return;
}
// Format as hex strings (matching BBL protocol)
std::ostringstream ams_exist_ss;
ams_exist_ss << std::hex << std::uppercase << ams_exist_bits;
std::ostringstream tray_exist_ss;
tray_exist_ss << std::hex << std::uppercase << tray_exist_bits;
nlohmann::json ams_json = nlohmann::json::object();
ams_json["ams"] = ams_array;
ams_json["ams_exist_bits"] = ams_exist_ss.str();
ams_json["tray_exist_bits"] = tray_exist_ss.str();
@@ -528,7 +598,6 @@ void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index,
// Call the parser to populate DevFilaSystem
DevFilaSystemParser::ParseV1_0(print_json, obj, obj->GetFilaSystem().get(), false);
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::build_ams_payload: Parsed " << trays.size() << " trays";
// Set printer_type so update_sync_status() can match it against the preset's printer type.
// Without this, the comparison fails and all sync badges are cleared.
@@ -568,17 +637,15 @@ bool MoonrakerPrinterAgent::fetch_filament_info(std::string dev_id, FilamentSync
if (fetch_moonraker_filament_data(trays, max_lane_index)) {
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: Detected Moonraker filament system with "
<< (max_lane_index + 1) << " lanes";
int ams_count = (max_lane_index + 4) / 4;
build_ams_payload(ams_count, max_lane_index, trays);
build_ams_payload_grouped(trays);
return true;
}
// Attempt Happy Hare first (more widely adopted, supports more filament changers)
// Fall back to the Happy Hare `mmu` object for HH builds that do not publish lane_data
if (fetch_hh_filament_info(trays, max_lane_index)) {
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: Detected Happy Hare MMU with "
<< (max_lane_index + 1) << " gates";
int ams_count = (max_lane_index + 4) / 4;
build_ams_payload(ams_count, max_lane_index, trays);
build_ams_payload_grouped(trays);
return true;
}
@@ -684,6 +751,30 @@ int MoonrakerPrinterAgent::safe_json_int(const nlohmann::json& obj, const char*
return 0;
}
int MoonrakerPrinterAgent::safe_json_int_flexible(const nlohmann::json& obj, const char* key, int default_value)
{
auto it = obj.find(key);
if (it == obj.end())
return default_value;
if (it->is_number_integer())
return it->get<int>();
if (it->is_string()) {
const std::string s = it->get<std::string>();
if (s.empty())
return default_value;
try {
std::size_t consumed = 0;
const int value = std::stoi(s, &consumed);
if (consumed != s.size()) // reject partial parses such as "2oops"
return default_value;
return value;
} catch (...) {
return default_value;
}
}
return default_value;
}
std::string MoonrakerPrinterAgent::safe_array_string(const nlohmann::json& arr, int idx)
{
if (arr.is_array() && idx >= 0 && idx < static_cast<int>(arr.size()) && arr[idx].is_string())
@@ -792,23 +883,22 @@ bool MoonrakerPrinterAgent::fetch_moonraker_filament_data(std::vector<AmsTrayDat
continue;
}
// Extract lane index from the "lane" field (tool number, 0-based)
std::string lane_str = safe_json_string(lane_obj, "lane");
int lane_index = -1;
if (!lane_str.empty()) {
try {
lane_index = std::stoi(lane_str);
} catch (...) {
lane_index = -1;
}
}
// Lane tool number (0-based). AFC reports it as a string in the documented sample but it
// can also be a number; accept either and skip lanes without a usable index.
const int lane_index = safe_json_int_flexible(lane_obj, "lane", -1);
if (lane_index < 0) {
continue;
}
AmsTrayData tray;
tray.slot_index = lane_index;
// AFC exposes the lane's extruder/toolhead as "extruder_index"; tolerate "extruder_id" as
// an alias. Unknown or negative falls back to the main extruder (best effort).
tray.extruder_id = safe_json_int_flexible(lane_obj, "extruder_index",
safe_json_int_flexible(lane_obj, "extruder_id", 0));
if (tray.extruder_id < 0) {
tray.extruder_id = 0;
}
tray.tray_color = safe_json_string(lane_obj, "color");
tray.tray_type = safe_json_string(lane_obj, "material");
tray.bed_temp = safe_json_int(lane_obj, "bed_temp");
@@ -913,48 +1003,41 @@ bool MoonrakerPrinterAgent::fetch_hh_filament_info(std::vector<AmsTrayData>& tra
return false;
}
// Parse gate data
// Parse gate data. Every gate becomes a lane so empty/unknown gates surface
// as empty AMS slots rather than vanishing. HH is single-extruder, so all
// lanes belong to feed group 0.
trays.clear();
max_lane_index = 0;
for (int gate_idx = 0; gate_idx < num_gates; ++gate_idx) {
// Check gate_status: -1 = unknown, 0 = empty, 1 or 2 = available
int status = safe_array_int(gate_status, gate_idx);
if (status <= 0) {
continue; // Skip unknown or empty gates
}
// Extract gate data
std::string material = safe_array_string(gate_material, gate_idx);
std::string color = safe_array_string(gate_color, gate_idx);
int nozzle_temp = safe_array_int(gate_temperature, gate_idx);
// Skip if no material type (empty gate)
if (material.empty()) {
continue;
}
AmsTrayData tray;
tray.slot_index = gate_idx;
tray.tray_type = material;
tray.tray_color = color;
tray.nozzle_temp = nozzle_temp;
tray.bed_temp = 0; // HH doesn't provide bed temp in gate arrays
tray.has_filament = true;
tray.extruder_id = 0;
auto* bundle = GUI::wxGetApp().preset_bundle;
tray.tray_info_idx = bundle
? bundle->filaments.filament_id_by_type(tray.tray_type)
: map_filament_type_to_generic_id(tray.tray_type);
if (status > 0 && !material.empty()) {
tray.tray_type = material;
tray.tray_color = color;
tray.nozzle_temp = nozzle_temp;
tray.bed_temp = 0; // HH doesn't provide bed temp in gate arrays
tray.has_filament = true;
auto* bundle = GUI::wxGetApp().preset_bundle;
tray.tray_info_idx = bundle
? bundle->filaments.filament_id_by_type(tray.tray_type)
: map_filament_type_to_generic_id(tray.tray_type);
} else {
tray.has_filament = false;
}
max_lane_index = std::max(max_lane_index, gate_idx);
trays.push_back(tray);
}
if (trays.empty()) {
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_hh_filament_info: No valid HH gates found";
return false;
}
max_lane_index = num_gates - 1;
return true;
}
@@ -88,6 +88,7 @@ protected:
// Tray data for AMS payload building
struct AmsTrayData {
int slot_index = 0; // 0-based slot index
int extruder_id = 0; // Extruder this lane feeds (AFC extruder_index); 0 when unknown
bool has_filament = false;
std::string tray_type; // Material type (e.g., "PLA", "ASA")
std::string tray_color; // Raw color (#RRGGBB, 0xRRGGBB, or RRGGBBAA)
@@ -99,6 +100,10 @@ protected:
// Build ams JSON and call parser
void build_ams_payload(int ams_count, int max_lane_index, const std::vector<AmsTrayData>& trays);
// Build ams JSON with one box per extruder (best effort, for lane_data-based filament
// changers such as AFC) and call the parser. Boxes are ordered by extruder id.
void build_ams_payload_grouped(const std::vector<AmsTrayData>& trays);
// Methods that derived classes may need to override or access
virtual bool init_device_info(const std::string& dev_id, const std::string& dev_ip, const std::string& username, const std::string& password, bool use_ssl, const std::string& port);
virtual bool fetch_device_info(const std::string& base_url, const std::string& api_key, MoonrakerDeviceInfo& info, std::string& error) const;
@@ -164,9 +169,18 @@ private:
bool fetch_hh_filament_info(std::vector<AmsTrayData>& trays, int& max_lane_index);
bool fetch_moonraker_filament_data(std::vector<AmsTrayData>& trays, int& max_lane_index);
// Build one BBL tray JSON object (placeholder when the tray has no filament).
static nlohmann::json make_ams_tray_json(const std::string& tray_id, const AmsTrayData& tray);
// Wrap the AMS boxes into the payload ParseV1_0 understands and apply the pull-mode
// readiness state (printer_type, push counters, storage, module versions).
void submit_ams_payload(const nlohmann::json& ams_array, unsigned long ams_exist_bits, unsigned long tray_exist_bits);
// JSON helper methods
static std::string safe_json_string(const nlohmann::json& obj, const char* key);
static int safe_json_int(const nlohmann::json& obj, const char* key);
// Accepts either a JSON number or a numeric string (AFC reports some fields as strings).
static int safe_json_int_flexible(const nlohmann::json& obj, const char* key, int default_value);
static std::string safe_array_string(const nlohmann::json& arr, int idx);
static int safe_array_int(const nlohmann::json& arr, int idx);
static std::string normalize_color_value(const std::string& color);
+1
View File
@@ -48,6 +48,7 @@
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/UpdateDialogs.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/format.hpp"
#include "slic3r/GUI/NotificationManager.hpp"
+2 -2
View File
@@ -16,12 +16,12 @@
namespace Slic3r {
namespace Utils {
using boost::asio::ip::tcp;
// Generic command / response TCP telnet like console class.
// Used by the MKS host to send G-code commands to test connection ("M105") and to start printing ("M23 filename", "M24").
class TCPConsole
{
using tcp = boost::asio::ip::tcp;
public:
TCPConsole() : m_resolver(m_io_context), m_socket(m_io_context) { set_defaults(); }
TCPConsole(const std::string& host_name, const std::string& port_name) : m_resolver(m_io_context), m_socket(m_io_context)
+16 -11
View File
@@ -16,6 +16,11 @@
#include <iostream>
#include <string>
#include <chrono>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
class WebSocketClient {
public:
@@ -31,7 +36,7 @@ public:
}
try {
// Close the WebSocket connection
ws_.close(boost::beast::websocket::close_code::normal);
ws_.close(websocket::close_code::normal);
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
@@ -44,7 +49,7 @@ public:
auto const results = resolver_.resolve(host, port);
// Make the connection on the IP address we get from a lookup
auto ep = boost::asio::connect(ws_.next_layer(), results);
auto ep = net::connect(ws_.next_layer(), results);
std::string _host = host;
//if _host last char is '/', remove it
if(_host.size()>0&&_host[host.size()-1] == '/'){
@@ -53,10 +58,10 @@ public:
// _host += ':' + std::to_string(ep.port());
// Set a decorator to change the User-Agent of the handshake
ws_.set_option(boost::beast::websocket::stream_base::decorator(
[](boost::beast::websocket::request_type& req)
ws_.set_option(websocket::stream_base::decorator(
[](websocket::request_type& req)
{
req.set(boost::beast::http::field::user_agent,"ElegooSlicer");
req.set(http::field::user_agent,"ElegooSlicer");
}));
// Perform the WebSocket handshake
ws_.handshake(_host, path);
@@ -65,25 +70,25 @@ public:
void send(const std::string& message){
// Send a message
ws_.write(boost::asio::buffer(message));
ws_.write(net::buffer(message));
}
std::string receive(int timeout = 0){
// This buffer will hold the incoming message
boost::beast::flat_buffer buffer;
beast::flat_buffer buffer;
// Read a message into our buffer
ws_.read(buffer);
// Return the message as a string
return boost::beast::buffers_to_string(buffer.data());
return beast::buffers_to_string(buffer.data());
}
private:
boost::asio::io_context ioc_;
boost::asio::ip::tcp::resolver resolver_;
boost::beast::websocket::stream<boost::asio::ip::tcp::socket> ws_;
net::io_context ioc_;
tcp::resolver resolver_;
websocket::stream<tcp::socket> ws_;
bool is_connect;
};
-7
View File
@@ -115,9 +115,6 @@ static const std::unordered_map<std::string, AuditEventCategory> audit_event_cat
{"subprocess.Popen", AuditEventCategory::ProcessCreate},
{"_winapi.CreateProcess", AuditEventCategory::ProcessCreate},
{"_posixsubprocess.fork_exec", AuditEventCategory::ProcessCreate},
// processreplace: exec* replaces the current process image rather than spawning a child
{"os.exec", AuditEventCategory::ProcessReplace},
};
// Returns the category event_name belongs to, or AuditEventCategory::None when it isn't audited.
@@ -711,7 +708,6 @@ static const std::unordered_map<std::string, std::vector<Py_ssize_t>> audit_targ
{"pty.spawn", {0}},
{"_winapi.CreateProcess", {1, 0}},
{"_posixsubprocess.fork_exec", {0}},
{"os.exec", {0}},
};
AuditEventCategory open_category(PyObject* args)
@@ -765,7 +761,6 @@ std::vector<std::string>* permission_list_for(AuditEventCategory category, Plugi
case AuditEventCategory::Http: return &permissions.network_http;
case AuditEventCategory::Socket: return &permissions.network_socket;
case AuditEventCategory::ProcessCreate: return &permissions.process;
case AuditEventCategory::ProcessReplace: return &permissions.process;
default: return nullptr;
}
}
@@ -837,8 +832,6 @@ wxString audit_message(AuditEventCategory category, const wxString& plugin_name,
return wxString::Format(_L("Plugin \"%s\" is requesting to open a network connection to:\n%s"), plugin_name, target_list);
case AuditEventCategory::ProcessCreate:
return wxString::Format(_L("Plugin \"%s\" is requesting to run the following command(s):\n%s"), plugin_name, target_list);
case AuditEventCategory::ProcessReplace:
return wxString::Format(_L("Plugin \"%s\" is requesting to replace the running application with:\n%s"), plugin_name, target_list);
default:
return wxString::Format(_L("Plugin \"%s\" is requesting permission for the Python audit event \"%s\"."), plugin_name, event_name);
}
-1
View File
@@ -45,7 +45,6 @@ enum class AuditEventCategory {
Http,
Socket,
ProcessCreate,
ProcessReplace,
Threading,
};
+5 -43
View File
@@ -28,7 +28,6 @@
#include <wx/app.h>
#include <wx/defs.h>
#include <wx/thread.h>
#include <wx/timer.h>
#include <wx/toplevel.h>
#include <wx/event.h>
#include <wx/progdlg.h>
@@ -416,44 +415,6 @@ struct UiDockPanelHandle
int id{0};
};
// Polls until the plater is shown on screen, then runs the callback with it (null when the app
// is closing instead) and deletes itself. A plugin loaded at startup opens its panes from
// on_load, before the main window exists, and a pane added before that window is laid out is
// sized against the unsized frame and keeps that width (AuiMgr::track_docked_size).
class PlaterShownWaiter : public wxTimer
{
public:
explicit PlaterShownWaiter(std::function<void(GUI::Plater*)> fn) : m_fn(std::move(fn)) {}
// True once the callback ran.
bool try_run()
{
const bool closing = GUI::wxGetApp().is_closing();
GUI::Plater* plater = closing ? nullptr : GUI::wxGetApp().plater();
if (!closing && (plater == nullptr || !plater->IsShownOnScreen()))
return false;
Stop();
m_fn(plater);
// Off the timer callback's stack: wxGTK's timeout callback still reads the timer after Notify().
GUI::wxGetApp().CallAfter([this]() { delete this; });
return true;
}
void Notify() override { try_run(); }
private:
std::function<void(GUI::Plater*)> m_fn;
};
void run_when_plater_shown(std::function<void(GUI::Plater*)> fn)
{
GUI::wxGetApp().CallAfter([fn = std::move(fn)]() mutable {
auto* waiter = new PlaterShownWaiter(std::move(fn));
if (!waiter->try_run())
waiter->Start(100);
});
}
py::object ui_create_dock_panel(const std::string& html, const std::string& title, int width, int height,
py::object on_message, py::object on_close, const std::string& dock)
{
@@ -473,13 +434,14 @@ py::object ui_create_dock_panel(const std::string& html, const std::string& titl
const int new_id = UiRegistry::instance().reserve_id();
UiRegistry::instance().bind(new_id, nullptr, plugin_key);
run_when_plater_shown([new_id, plugin_key, html, title, dock, w, h,
msg_adapter = std::move(msg_adapter),
close_holder = std::move(close_holder)](GUI::Plater* plater) mutable {
GUI::wxGetApp().CallAfter([new_id, plugin_key, html, title, dock, w, h,
msg_adapter = std::move(msg_adapter),
close_holder = std::move(close_holder)]() mutable {
if (!UiRegistry::instance().is_open(new_id))
return;
if (plater == nullptr) {
GUI::Plater* plater = GUI::wxGetApp().plater();
if (plater == nullptr || GUI::wxGetApp().is_closing()) {
UiRegistry::instance().remove(new_id);
return;
}
+60 -58
View File
@@ -17,18 +17,20 @@
#include <map>
#include <unordered_map>
using json = nlohmann::json;
// Put serializers in correct ADL namespaces for each type
namespace Slic3r {
namespace FilamentGroupUtils {
inline void to_json(nlohmann::json& j, const Color& c) {
inline void to_json(json& j, const Color& c) {
char buf[10];
snprintf(buf, sizeof(buf), "#%02X%02X%02X%02X", c.r, c.g, c.b, c.a);
j = std::string(buf);
}
inline void from_json(const nlohmann::json& j, Color& c) {
inline void from_json(const json& j, Color& c) {
std::string s = j.get<std::string>();
if (s.size() >= 7 && s[0] == '#') {
c.r = (unsigned char)std::stoi(s.substr(1, 2), nullptr, 16);
@@ -38,8 +40,8 @@ inline void from_json(const nlohmann::json& j, Color& c) {
}
}
inline void to_json(nlohmann::json& j, const FilamentInfo& fi) {
j = nlohmann::json{
inline void to_json(json& j, const FilamentInfo& fi) {
j = json{
{"color", fi.color},
{"type", fi.type},
{"is_support", fi.is_support},
@@ -47,15 +49,15 @@ inline void to_json(nlohmann::json& j, const FilamentInfo& fi) {
};
}
inline void from_json(const nlohmann::json& j, FilamentInfo& fi) {
inline void from_json(const json& j, FilamentInfo& fi) {
fi.color = j.at("color").get<Color>();
j.at("type").get_to(fi.type);
j.at("is_support").get_to(fi.is_support);
fi.usage_type = (FilamentUsageType)j.at("usage_type").get<int>();
}
inline void to_json(nlohmann::json& j, const MachineFilamentInfo& mfi) {
j = nlohmann::json{
inline void to_json(json& j, const MachineFilamentInfo& mfi) {
j = json{
{"color", mfi.color},
{"type", mfi.type},
{"is_support", mfi.is_support},
@@ -65,7 +67,7 @@ inline void to_json(nlohmann::json& j, const MachineFilamentInfo& mfi) {
};
}
inline void from_json(const nlohmann::json& j, MachineFilamentInfo& mfi) {
inline void from_json(const json& j, MachineFilamentInfo& mfi) {
mfi.color = j.at("color").get<Color>();
j.at("type").get_to(mfi.type);
j.at("is_support").get_to(mfi.is_support);
@@ -78,8 +80,8 @@ inline void from_json(const nlohmann::json& j, MachineFilamentInfo& mfi) {
namespace MultiNozzleUtils {
inline void to_json(nlohmann::json& j, const NozzleInfo& ni) {
j = nlohmann::json{
inline void to_json(json& j, const NozzleInfo& ni) {
j = json{
{"diameter", ni.diameter},
{"volume_type", (int)ni.volume_type},
{"extruder_id", ni.extruder_id},
@@ -87,15 +89,15 @@ inline void to_json(nlohmann::json& j, const NozzleInfo& ni) {
};
}
inline void from_json(const nlohmann::json& j, NozzleInfo& ni) {
inline void from_json(const json& j, NozzleInfo& ni) {
j.at("diameter").get_to(ni.diameter);
ni.volume_type = (NozzleVolumeType)j.at("volume_type").get<int>();
j.at("extruder_id").get_to(ni.extruder_id);
j.at("group_id").get_to(ni.group_id);
}
inline void to_json(nlohmann::json& j, const FilamentChangeTimeParams& p) {
j = nlohmann::json{
inline void to_json(json& j, const FilamentChangeTimeParams& p) {
j = json{
{"selector_load_time", p.selector_load_time},
{"selector_unload_time", p.selector_unload_time},
{"standard_load_time", p.standard_load_time},
@@ -103,7 +105,7 @@ inline void to_json(nlohmann::json& j, const FilamentChangeTimeParams& p) {
};
}
inline void from_json(const nlohmann::json& j, FilamentChangeTimeParams& p) {
inline void from_json(const json& j, FilamentChangeTimeParams& p) {
j.at("selector_load_time").get_to(p.selector_load_time);
j.at("selector_unload_time").get_to(p.selector_unload_time);
j.at("standard_load_time").get_to(p.standard_load_time);
@@ -114,22 +116,22 @@ inline void from_json(const nlohmann::json& j, FilamentChangeTimeParams& p) {
// ============ Helper: set<int> as JSON array ============
namespace FGTestDetail {
inline nlohmann::json set_to_json(const std::set<int>& s) {
return nlohmann::json(std::vector<int>(s.begin(), s.end()));
inline json set_to_json(const std::set<int>& s) {
return json(std::vector<int>(s.begin(), s.end()));
}
inline std::set<int> json_to_set(const nlohmann::json& j) {
inline std::set<int> json_to_set(const json& j) {
auto v = j.get<std::vector<int>>();
return std::set<int>(v.begin(), v.end());
}
inline nlohmann::json nvt_set_to_json(const std::set<NozzleVolumeType>& s) {
inline json nvt_set_to_json(const std::set<NozzleVolumeType>& s) {
std::vector<int> v;
for (auto t : s) v.push_back((int)t);
return nlohmann::json(v);
return json(v);
}
inline std::set<NozzleVolumeType> json_to_nvt_set(const nlohmann::json& j) {
inline std::set<NozzleVolumeType> json_to_nvt_set(const json& j) {
std::set<NozzleVolumeType> s;
for (auto& item : j) s.insert((NozzleVolumeType)item.get<int>());
return s;
@@ -137,28 +139,28 @@ inline std::set<NozzleVolumeType> json_to_nvt_set(const nlohmann::json& j) {
} // namespace FGTestDetail
// ============ FilamentGroupContext::ModelInfo ============
inline void to_json(nlohmann::json& j, const FilamentGroupContext::ModelInfo& mi) {
inline void to_json(json& j, const FilamentGroupContext::ModelInfo& mi) {
using namespace FGTestDetail;
j["flush_matrix"] = mi.flush_matrix;
j["layer_filaments"] = mi.layer_filaments;
j["filament_info"] = nlohmann::json::array();
j["filament_info"] = json::array();
for (auto& fi : mi.filament_info)
j["filament_info"].push_back(fi);
j["filament_ids"] = mi.filament_ids;
j["unprintable_filaments"] = nlohmann::json::array();
j["unprintable_filaments"] = json::array();
for (auto& s : mi.unprintable_filaments)
j["unprintable_filaments"].push_back(set_to_json(s));
nlohmann::json uv = nlohmann::json::object();
json uv = json::object();
for (auto& [fil, types] : mi.unprintable_volumes)
uv[std::to_string(fil)] = nvt_set_to_json(types);
j["unprintable_volumes"] = uv;
}
inline void from_json(const nlohmann::json& j, FilamentGroupContext::ModelInfo& mi) {
inline void from_json(const json& j, FilamentGroupContext::ModelInfo& mi) {
using namespace FGTestDetail;
j.at("flush_matrix").get_to(mi.flush_matrix);
j.at("layer_filaments").get_to(mi.layer_filaments);
@@ -181,8 +183,8 @@ inline void from_json(const nlohmann::json& j, FilamentGroupContext::ModelInfo&
}
// ============ FilamentGroupContext::GroupInfo ============
inline void to_json(nlohmann::json& j, const FilamentGroupContext::GroupInfo& gi) {
j = nlohmann::json{
inline void to_json(json& j, const FilamentGroupContext::GroupInfo& gi) {
j = json{
{"total_filament_num", gi.total_filament_num},
{"max_gap_threshold", gi.max_gap_threshold},
{"mode", (int)gi.mode},
@@ -193,7 +195,7 @@ inline void to_json(nlohmann::json& j, const FilamentGroupContext::GroupInfo& gi
};
}
inline void from_json(const nlohmann::json& j, FilamentGroupContext::GroupInfo& gi) {
inline void from_json(const json& j, FilamentGroupContext::GroupInfo& gi) {
j.at("total_filament_num").get_to(gi.total_filament_num);
j.at("max_gap_threshold").get_to(gi.max_gap_threshold);
gi.mode = (FGMode)j.at("mode").get<int>();
@@ -204,12 +206,12 @@ inline void from_json(const nlohmann::json& j, FilamentGroupContext::GroupInfo&
}
// ============ FilamentGroupContext::MachineInfo ============
inline void to_json(nlohmann::json& j, const FilamentGroupContext::MachineInfo& mi) {
inline void to_json(json& j, const FilamentGroupContext::MachineInfo& mi) {
j["max_group_size"] = mi.max_group_size;
j["machine_filament_info"] = nlohmann::json::array();
j["machine_filament_info"] = json::array();
for (auto& vec : mi.machine_filament_info) {
nlohmann::json arr = nlohmann::json::array();
json arr = json::array();
for (auto& mfi : vec) arr.push_back(mfi);
j["machine_filament_info"].push_back(arr);
}
@@ -218,7 +220,7 @@ inline void to_json(nlohmann::json& j, const FilamentGroupContext::MachineInfo&
j["master_extruder_id"] = mi.master_extruder_id;
}
inline void from_json(const nlohmann::json& j, FilamentGroupContext::MachineInfo& mi) {
inline void from_json(const json& j, FilamentGroupContext::MachineInfo& mi) {
j.at("max_group_size").get_to(mi.max_group_size);
mi.machine_filament_info.clear();
@@ -234,10 +236,10 @@ inline void from_json(const nlohmann::json& j, FilamentGroupContext::MachineInfo
}
// ============ FilamentGroupContext::SpeedInfo ============
inline void to_json(nlohmann::json& j, const FilamentGroupContext::SpeedInfo& si) {
nlohmann::json fpt = nlohmann::json::object();
inline void to_json(json& j, const FilamentGroupContext::SpeedInfo& si) {
json fpt = json::object();
for (auto& [fil, inner] : si.filament_print_time) {
nlohmann::json inner_j = nlohmann::json::object();
json inner_j = json::object();
for (auto& [layer, time] : inner)
inner_j[std::to_string(layer)] = time;
fpt[std::to_string(fil)] = inner_j;
@@ -250,7 +252,7 @@ inline void to_json(nlohmann::json& j, const FilamentGroupContext::SpeedInfo& si
j["ams_preload_enabled"] = si.ams_preload_enabled;
}
inline void from_json(const nlohmann::json& j, FilamentGroupContext::SpeedInfo& si) {
inline void from_json(const json& j, FilamentGroupContext::SpeedInfo& si) {
si.filament_print_time.clear();
if (j.contains("filament_print_time")) {
for (auto& [k, v] : j.at("filament_print_time").items()) {
@@ -267,23 +269,23 @@ inline void from_json(const nlohmann::json& j, FilamentGroupContext::SpeedInfo&
}
// ============ FilamentGroupContext::NozzleInfo ============
inline void to_json(nlohmann::json& j, const FilamentGroupContext::NozzleInfo& ni) {
nlohmann::json enl = nlohmann::json::object();
inline void to_json(json& j, const FilamentGroupContext::NozzleInfo& ni) {
json enl = json::object();
for (auto& [ext, nozzles] : ni.extruder_nozzle_list)
enl[std::to_string(ext)] = nozzles;
j["extruder_nozzle_list"] = enl;
j["nozzle_list"] = nlohmann::json::array();
j["nozzle_list"] = json::array();
for (auto& n : ni.nozzle_list)
j["nozzle_list"].push_back(n);
nlohmann::json ns = nlohmann::json::object();
json ns = json::object();
for (auto& [noz, fil] : ni.nozzle_status)
ns[std::to_string(noz)] = fil;
j["nozzle_status"] = ns;
}
inline void from_json(const nlohmann::json& j, FilamentGroupContext::NozzleInfo& ni) {
inline void from_json(const json& j, FilamentGroupContext::NozzleInfo& ni) {
ni.extruder_nozzle_list.clear();
for (auto& [k, v] : j.at("extruder_nozzle_list").items())
ni.extruder_nozzle_list[std::stoi(k)] = v.get<std::vector<int>>();
@@ -300,8 +302,8 @@ inline void from_json(const nlohmann::json& j, FilamentGroupContext::NozzleInfo&
}
// ============ Full FilamentGroupContext ============
inline void to_json(nlohmann::json& j, const FilamentGroupContext& ctx) {
nlohmann::json mi, gi, mai, si, ni;
inline void to_json(json& j, const FilamentGroupContext& ctx) {
json mi, gi, mai, si, ni;
to_json(mi, ctx.model_info);
to_json(gi, ctx.group_info);
to_json(mai, ctx.machine_info);
@@ -314,7 +316,7 @@ inline void to_json(nlohmann::json& j, const FilamentGroupContext& ctx) {
j["nozzle_info"] = ni;
}
inline void from_json(const nlohmann::json& j, FilamentGroupContext& ctx) {
inline void from_json(const json& j, FilamentGroupContext& ctx) {
from_json(j.at("model_info"), ctx.model_info);
from_json(j.at("group_info"), ctx.group_info);
from_json(j.at("machine_info"), ctx.machine_info);
@@ -334,11 +336,11 @@ struct TestMetadata {
int seed = 0;
};
inline void to_json(nlohmann::json& j, const TestMetadata& m) {
j = nlohmann::json{{"id", m.id}, {"config_type", m.config_type}, {"seed", m.seed}};
inline void to_json(json& j, const TestMetadata& m) {
j = json{{"id", m.id}, {"config_type", m.config_type}, {"seed", m.seed}};
}
inline void from_json(const nlohmann::json& j, TestMetadata& m) {
inline void from_json(const json& j, TestMetadata& m) {
j.at("id").get_to(m.id);
j.at("config_type").get_to(m.config_type);
j.at("seed").get_to(m.seed);
@@ -352,8 +354,8 @@ struct TestResult {
std::vector<std::string> violations;
};
inline void to_json(nlohmann::json& j, const TestResult& r) {
j = nlohmann::json{
inline void to_json(json& j, const TestResult& r) {
j = json{
{"filament_map", r.filament_map},
{"flush_cost", r.flush_cost},
{"elapsed_ms", r.elapsed_ms},
@@ -362,7 +364,7 @@ inline void to_json(nlohmann::json& j, const TestResult& r) {
};
}
inline void from_json(const nlohmann::json& j, TestResult& r) {
inline void from_json(const json& j, TestResult& r) {
j.at("filament_map").get_to(r.filament_map);
j.at("flush_cost").get_to(r.flush_cost);
j.at("elapsed_ms").get_to(r.elapsed_ms);
@@ -378,15 +380,15 @@ struct BaseResult {
bool constraints_ok = true;
};
inline void to_json(nlohmann::json& j, const BaseResult& g) {
j = nlohmann::json{
inline void to_json(json& j, const BaseResult& g) {
j = json{
{"full_score", g.full_score},
{"flush_cost", g.flush_cost},
{"constraints_ok", g.constraints_ok}
};
}
inline void from_json(const nlohmann::json& j, BaseResult& g) {
inline void from_json(const json& j, BaseResult& g) {
j.at("full_score").get_to(g.full_score);
j.at("flush_cost").get_to(g.flush_cost);
j.at("constraints_ok").get_to(g.constraints_ok);
@@ -401,7 +403,7 @@ struct TestCase {
inline TestCase load_test_case(const std::string& path) {
std::ifstream f(path);
nlohmann::json j = nlohmann::json::parse(f);
json j = json::parse(f);
TestCase tc;
tc.metadata = j.at("metadata").get<TestMetadata>();
Slic3r::from_json(j.at("context"), tc.context);
@@ -411,9 +413,9 @@ inline TestCase load_test_case(const std::string& path) {
}
inline void save_test_case(const std::string& path, const TestCase& tc) {
nlohmann::json j;
json j;
j["metadata"] = tc.metadata;
nlohmann::json ctx_j;
json ctx_j;
Slic3r::to_json(ctx_j, tc.context);
j["context"] = ctx_j;
if (tc.base_result)
@@ -430,14 +432,14 @@ inline void save_result(const std::string& case_path, const TestResult& result)
else
result_path += ".result.json";
nlohmann::json j = result;
json j = result;
std::ofstream f(result_path);
f << j.dump(2);
}
inline TestResult load_result(const std::string& result_path) {
std::ifstream f(result_path);
nlohmann::json j = nlohmann::json::parse(f);
json j = json::parse(f);
return j.get<TestResult>();
}
@@ -863,23 +863,3 @@ TEST_CASE("The device drying options are rebuilt as each filament's values in sl
set_filament_dev_options(config, {&two_values, &no_value});
REQUIRE(config.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations")->values == std::vector<std::string>({"1", "0", ""}));
}
TEST_CASE("The mixed filament metadata is sized to the filament count", "[Config]")
{
DynamicPrintConfig config;
config.option<ConfigOptionBools>("filament_is_mixed", true)->values = {false, false, true};
config.option<ConfigOptionStrings>("filament_mixed_components", true)->values = {"", "", "1,2"};
config.option<ConfigOptionBools>("filament_mixed_gradient", true)->values = {false};
config.option<ConfigOptionStrings>("filament_mixed_gradient_range", true)->values = {""};
resize_mixed_filament_metadata(config, 3, 3);
REQUIRE(config.option<ConfigOptionBools>("filament_is_mixed")->values == std::vector<unsigned char>({false, false, true}));
REQUIRE(config.option<ConfigOptionStrings>("filament_mixed_components")->values == std::vector<std::string>({"", "", "1,2"}));
REQUIRE(config.option<ConfigOptionBools>("filament_mixed_gradient")->values == std::vector<unsigned char>({false, false, false}));
REQUIRE(config.option<ConfigOptionStrings>("filament_mixed_gradient_range")->values == std::vector<std::string>({"", "", ""}));
REQUIRE(config.option<ConfigOptionStrings>("filament_mixed_gradient_curve")->values == std::vector<std::string>({"", "", ""}));
resize_mixed_filament_metadata(config, 2, 4);
REQUIRE(config.option<ConfigOptionBools>("filament_is_mixed")->values == std::vector<unsigned char>({false, false, false, false}));
REQUIRE(config.option<ConfigOptionStrings>("filament_mixed_components")->values == std::vector<std::string>({"", "", "", ""}));
}
@@ -24,6 +24,7 @@
#include "test_utils.hpp"
#include <algorithm>
#include <iostream>
#include <initializer_list>
#include <vector>
-2
View File
@@ -31,8 +31,6 @@
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/ExtrusionEntity.hpp"
using namespace Slic3r;
namespace {
const char *const BELOW_PAD_TEST_OBJECTS[] = {
-2
View File
@@ -36,8 +36,6 @@
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/Line.hpp"
using namespace Slic3r;
void test_support_model_collision(const std::string &obj_filename,
const sla::SupportTreeConfig &input_supportcfg,
const sla::HollowingConfig &hollowingcfg,
+38 -36
View File
@@ -33,31 +33,33 @@ namespace Slic3r::sla { struct PadConfig; }
namespace Slic3r::sla { struct SupportTreeConfig; }
using namespace Slic3r;
enum e_validity {
ASSUME_NO_EMPTY = 1,
ASSUME_MANIFOLD = 2,
ASSUME_NO_REPAIR = 4
};
void check_validity(const Slic3r::TriangleMesh &input_mesh,
void check_validity(const TriangleMesh &input_mesh,
int flags = ASSUME_NO_EMPTY | ASSUME_MANIFOLD |
ASSUME_NO_REPAIR);
struct PadByproducts
{
Slic3r::ExPolygons model_contours;
Slic3r::ExPolygons support_contours;
Slic3r::TriangleMesh mesh;
ExPolygons model_contours;
ExPolygons support_contours;
TriangleMesh mesh;
};
void test_concave_hull(const Slic3r::ExPolygons &polys);
void test_concave_hull(const ExPolygons &polys);
void test_pad(const std::string & obj_filename,
const Slic3r::sla::PadConfig &padcfg,
const sla::PadConfig &padcfg,
PadByproducts & out);
inline void test_pad(const std::string & obj_filename,
const Slic3r::sla::PadConfig &padcfg = {})
const sla::PadConfig &padcfg = {})
{
PadByproducts byproducts;
test_pad(obj_filename, padcfg, byproducts);
@@ -67,53 +69,53 @@ struct SupportByproducts
{
std::string obj_fname;
std::vector<float> slicegrid;
std::vector<Slic3r::ExPolygons> model_slices;
Slic3r::sla::SupportTreeBuilder supporttree;
Slic3r::TriangleMesh input_mesh;
std::vector<ExPolygons> model_slices;
sla::SupportTreeBuilder supporttree;
TriangleMesh input_mesh;
};
const constexpr float CLOSING_RADIUS = 0.005f;
void check_support_tree_integrity(const Slic3r::sla::SupportTreeBuilder &stree,
const Slic3r::sla::SupportTreeConfig &cfg);
void check_support_tree_integrity(const sla::SupportTreeBuilder &stree,
const sla::SupportTreeConfig &cfg);
void test_supports(const std::string &obj_filename,
const Slic3r::sla::SupportTreeConfig &supportcfg,
const Slic3r::sla::HollowingConfig &hollowingcfg,
const Slic3r::sla::DrainHoles &drainholes,
const sla::SupportTreeConfig &supportcfg,
const sla::HollowingConfig &hollowingcfg,
const sla::DrainHoles &drainholes,
SupportByproducts &out);
inline void test_supports(const std::string &obj_filename,
const Slic3r::sla::SupportTreeConfig &supportcfg,
const sla::SupportTreeConfig &supportcfg,
SupportByproducts &out)
{
Slic3r::sla::HollowingConfig hcfg;
sla::HollowingConfig hcfg;
hcfg.enabled = false;
test_supports(obj_filename, supportcfg, hcfg, {}, out);
}
inline void test_supports(const std::string &obj_filename,
const Slic3r::sla::SupportTreeConfig &supportcfg = {})
const sla::SupportTreeConfig &supportcfg = {})
{
SupportByproducts byproducts;
test_supports(obj_filename, supportcfg, byproducts);
}
void export_failed_case(const std::vector<Slic3r::ExPolygons> &support_slices,
void export_failed_case(const std::vector<ExPolygons> &support_slices,
const SupportByproducts &byproducts);
void test_support_model_collision(
const std::string &obj_filename,
const Slic3r::sla::SupportTreeConfig &input_supportcfg,
const Slic3r::sla::HollowingConfig &hollowingcfg,
const Slic3r::sla::DrainHoles &drainholes);
const sla::SupportTreeConfig &input_supportcfg,
const sla::HollowingConfig &hollowingcfg,
const sla::DrainHoles &drainholes);
inline void test_support_model_collision(
const std::string &obj_filename,
const Slic3r::sla::SupportTreeConfig &input_supportcfg = {})
const sla::SupportTreeConfig &input_supportcfg = {})
{
Slic3r::sla::HollowingConfig hcfg;
sla::HollowingConfig hcfg;
hcfg.enabled = false;
test_support_model_collision(obj_filename, input_supportcfg, hcfg, {});
}
@@ -154,8 +156,8 @@ template <class I, class II> void test_pairhash()
REQUIRE(a != b);
II hash_ab = Slic3r::sla::pairhash<I, II>(a, b);
II hash_ba = Slic3r::sla::pairhash<I, II>(b, a);
II hash_ab = sla::pairhash<I, II>(a, b);
II hash_ba = sla::pairhash<I, II>(b, a);
REQUIRE(hash_ab == hash_ba);
auto it = ints.find(hash_ab);
@@ -178,23 +180,23 @@ static constexpr const TPixel FullBlack = 0;
template <class A, int N> constexpr int arraysize(const A (&)[N]) { return N; }
void check_raster_transformations(Slic3r::sla::RasterBase::Orientation o,
Slic3r::sla::RasterBase::TMirroring mirroring);
void check_raster_transformations(sla::RasterBase::Orientation o,
sla::RasterBase::TMirroring mirroring);
Slic3r::ExPolygon square_with_hole(double v);
ExPolygon square_with_hole(double v);
inline double pixel_area(TPixel px, const Slic3r::sla::PixelDim &pxdim)
inline double pixel_area(TPixel px, const sla::PixelDim &pxdim)
{
return (pxdim.h_mm * pxdim.w_mm) * px * 1. / (FullWhite - FullBlack);
}
double raster_white_area(const Slic3r::sla::RasterGrayscaleAA &raster);
long raster_pxsum(const Slic3r::sla::RasterGrayscaleAA &raster);
double raster_white_area(const sla::RasterGrayscaleAA &raster);
long raster_pxsum(const sla::RasterGrayscaleAA &raster);
double predict_error(const Slic3r::ExPolygon &p, const Slic3r::sla::PixelDim &pd);
double predict_error(const ExPolygon &p, const sla::PixelDim &pd);
Slic3r::sla::SupportPoints calc_support_pts(
const Slic3r::TriangleMesh & mesh,
const Slic3r::sla::SupportPointGenerator::Config &cfg = {});
sla::SupportPoints calc_support_pts(
const TriangleMesh & mesh,
const sla::SupportPointGenerator::Config &cfg = {});
#endif // SLA_TEST_UTILS_HPP