Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/xs
diff options
context:
space:
mode:
authortamasmeszaros <meszaros.q@gmail.com>2018-07-27 18:37:33 +0300
committertamasmeszaros <meszaros.q@gmail.com>2018-07-27 18:46:19 +0300
commit4e901a9db778660d3471a49cd95d66f85b2dbc88 (patch)
tree3c5c646864100eb3710cb1eb713acd647ad37736 /xs
parentf364bd1884c318c68be9d0ee4529848919ced917 (diff)
parent78a8acfcf187d05cfea8a0521cf238937221a86a (diff)
Merge remote-tracking branch 'origin/master' into feature_arrange_with_libnest2d
Diffstat (limited to 'xs')
-rw-r--r--xs/CMakeLists.txt4
-rw-r--r--xs/src/avrdude/CMakeLists.txt114
-rw-r--r--xs/src/avrdude/Makefile.standalone54
-rw-r--r--xs/src/avrdude/avrdude-slic3r.cpp15
-rw-r--r--xs/src/avrdude/avrdude-slic3r.hpp5
-rw-r--r--xs/src/avrdude/fileio.c56
-rw-r--r--xs/src/avrdude/libavrdude.h6
-rw-r--r--xs/src/avrdude/main-standalone.c9
-rw-r--r--xs/src/avrdude/main.c2
-rw-r--r--xs/src/avrdude/ser_win32.c2
-rw-r--r--xs/src/avrdude/update.c20
-rw-r--r--xs/src/libslic3r/GCodeSender.cpp17
-rw-r--r--xs/src/libslic3r/Model.cpp3
-rw-r--r--xs/src/slic3r/GUI/3DScene.cpp34
-rw-r--r--xs/src/slic3r/GUI/3DScene.hpp11
-rw-r--r--xs/src/slic3r/GUI/FirmwareDialog.cpp349
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3D.cpp918
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3D.hpp28
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3DManager.cpp34
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3DManager.hpp5
-rw-r--r--xs/src/slic3r/GUI/GUI.cpp2
-rw-r--r--xs/src/slic3r/GUI/Preset.cpp5
-rw-r--r--xs/src/slic3r/GUI/Preset.hpp3
-rw-r--r--xs/src/slic3r/GUI/PresetBundle.cpp3
-rw-r--r--xs/src/slic3r/GUI/Tab.cpp7
-rw-r--r--xs/src/slic3r/Utils/HexFile.cpp107
-rw-r--r--xs/src/slic3r/Utils/HexFile.hpp32
-rw-r--r--xs/src/slic3r/Utils/Serial.cpp314
-rw-r--r--xs/src/slic3r/Utils/Serial.hpp65
-rw-r--r--xs/xsp/GUI_3DScene.xsp35
30 files changed, 1563 insertions, 696 deletions
diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index 976943d64..95ff990aa 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -26,7 +26,7 @@ include_directories(${LIBDIR}/libslic3r)
if(WIN32)
# BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking.
- add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB)
+ add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x601)
# -D_ITERATOR_DEBUG_LEVEL)
if(WIN10SDK_PATH)
message("Building with Win10 Netfabb STL fixing service support")
@@ -258,6 +258,8 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/Utils/PresetUpdater.hpp
${LIBDIR}/slic3r/Utils/Time.cpp
${LIBDIR}/slic3r/Utils/Time.hpp
+ ${LIBDIR}/slic3r/Utils/HexFile.cpp
+ ${LIBDIR}/slic3r/Utils/HexFile.hpp
${LIBDIR}/slic3r/IProgressIndicator.hpp
${LIBDIR}/slic3r/AppController.hpp
${LIBDIR}/slic3r/AppController.cpp
diff --git a/xs/src/avrdude/CMakeLists.txt b/xs/src/avrdude/CMakeLists.txt
index 043f8fb7b..d88563368 100644
--- a/xs/src/avrdude/CMakeLists.txt
+++ b/xs/src/avrdude/CMakeLists.txt
@@ -1,3 +1,4 @@
+cmake_minimum_required(VERSION 3.0)
add_definitions(-D_BSD_SOURCE -D_DEFAULT_SOURCE) # To enable various useful macros and functions on Unices
@@ -13,67 +14,74 @@ endif()
set(AVRDUDE_SOURCES
- ${LIBDIR}/avrdude/arduino.c
- ${LIBDIR}/avrdude/avr.c
- # ${LIBDIR}/avrdude/avrftdi.c
- # ${LIBDIR}/avrdude/avrftdi_tpi.c
- ${LIBDIR}/avrdude/avrpart.c
- ${LIBDIR}/avrdude/avr910.c
- ${LIBDIR}/avrdude/bitbang.c
- ${LIBDIR}/avrdude/buspirate.c
- ${LIBDIR}/avrdude/butterfly.c
- ${LIBDIR}/avrdude/config.c
- ${LIBDIR}/avrdude/config_gram.c
- # ${LIBDIR}/avrdude/confwin.c
- ${LIBDIR}/avrdude/crc16.c
- # ${LIBDIR}/avrdude/dfu.c
- ${LIBDIR}/avrdude/fileio.c
- # ${LIBDIR}/avrdude/flip1.c
- # ${LIBDIR}/avrdude/flip2.c
- # ${LIBDIR}/avrdude/ft245r.c
- # ${LIBDIR}/avrdude/jtagmkI.c
- # ${LIBDIR}/avrdude/jtagmkII.c
- # ${LIBDIR}/avrdude/jtag3.c
- ${LIBDIR}/avrdude/lexer.c
- ${LIBDIR}/avrdude/linuxgpio.c
- ${LIBDIR}/avrdude/lists.c
- # ${LIBDIR}/avrdude/par.c
- ${LIBDIR}/avrdude/pgm.c
- ${LIBDIR}/avrdude/pgm_type.c
- ${LIBDIR}/avrdude/pickit2.c
- ${LIBDIR}/avrdude/pindefs.c
- # ${LIBDIR}/avrdude/ppi.c
- # ${LIBDIR}/avrdude/ppiwin.c
- ${LIBDIR}/avrdude/safemode.c
- ${LIBDIR}/avrdude/ser_avrdoper.c
- ${LIBDIR}/avrdude/serbb_posix.c
- ${LIBDIR}/avrdude/serbb_win32.c
- ${LIBDIR}/avrdude/ser_posix.c
- ${LIBDIR}/avrdude/ser_win32.c
- ${LIBDIR}/avrdude/stk500.c
- ${LIBDIR}/avrdude/stk500generic.c
- ${LIBDIR}/avrdude/stk500v2.c
- ${LIBDIR}/avrdude/term.c
- ${LIBDIR}/avrdude/update.c
- # ${LIBDIR}/avrdude/usbasp.c
- # ${LIBDIR}/avrdude/usb_hidapi.c
- # ${LIBDIR}/avrdude/usb_libusb.c
- # ${LIBDIR}/avrdude/usbtiny.c
- ${LIBDIR}/avrdude/wiring.c
+ arduino.c
+ avr.c
+ # avrftdi.c
+ # avrftdi_tpi.c
+ avrpart.c
+ avr910.c
+ bitbang.c
+ buspirate.c
+ butterfly.c
+ config.c
+ config_gram.c
+ # confwin.c
+ crc16.c
+ # dfu.c
+ fileio.c
+ # flip1.c
+ # flip2.c
+ # ft245r.c
+ # jtagmkI.c
+ # jtagmkII.c
+ # jtag3.c
+ lexer.c
+ linuxgpio.c
+ lists.c
+ # par.c
+ pgm.c
+ pgm_type.c
+ pickit2.c
+ pindefs.c
+ # ppi.c
+ # ppiwin.c
+ safemode.c
+ ser_avrdoper.c
+ serbb_posix.c
+ serbb_win32.c
+ ser_posix.c
+ ser_win32.c
+ stk500.c
+ stk500generic.c
+ stk500v2.c
+ term.c
+ update.c
+ # usbasp.c
+ # usb_hidapi.c
+ # usb_libusb.c
+ # usbtiny.c
+ wiring.c
- ${LIBDIR}/avrdude/main.c
- ${LIBDIR}/avrdude/avrdude-slic3r.hpp
- ${LIBDIR}/avrdude/avrdude-slic3r.cpp
+ main.c
+ avrdude-slic3r.hpp
+ avrdude-slic3r.cpp
)
if (WIN32)
set(AVRDUDE_SOURCES ${AVRDUDE_SOURCES}
- ${LIBDIR}/avrdude/windows/unistd.cpp
- ${LIBDIR}/avrdude/windows/getopt.c
+ windows/unistd.cpp
+ windows/getopt.c
)
endif()
add_library(avrdude STATIC ${AVRDUDE_SOURCES})
+set(STANDALONE_SOURCES
+ main-standalone.c
+)
+add_executable(avrdude-slic3r ${STANDALONE_SOURCES})
+target_link_libraries(avrdude-slic3r avrdude)
+set_target_properties(avrdude-slic3r PROPERTIES EXCLUDE_FROM_ALL TRUE)
+
if (WIN32)
target_compile_definitions(avrdude PRIVATE WIN32NATIVE=1)
- target_include_directories(avrdude SYSTEM PRIVATE ${LIBDIR}/avrdude/windows) # So that sources find the getopt.h windows drop-in
+ target_include_directories(avrdude SYSTEM PRIVATE windows) # So that sources find the getopt.h windows drop-in
endif()
diff --git a/xs/src/avrdude/Makefile.standalone b/xs/src/avrdude/Makefile.standalone
new file mode 100644
index 000000000..d9a773771
--- /dev/null
+++ b/xs/src/avrdude/Makefile.standalone
@@ -0,0 +1,54 @@
+
+TARGET = avrdude-slic3r
+
+SOURCES = \
+ arduino.c \
+ avr.c \
+ avrpart.c \
+ avr910.c \
+ bitbang.c \
+ buspirate.c \
+ butterfly.c \
+ config.c \
+ config_gram.c \
+ crc16.c \
+ fileio.c \
+ lexer.c \
+ linuxgpio.c \
+ lists.c \
+ pgm.c \
+ pgm_type.c \
+ pickit2.c \
+ pindefs.c \
+ safemode.c \
+ ser_avrdoper.c \
+ serbb_posix.c \
+ serbb_win32.c \
+ ser_posix.c \
+ ser_win32.c \
+ stk500.c \
+ stk500generic.c \
+ stk500v2.c \
+ term.c \
+ update.c \
+ wiring.c \
+ main.c \
+ main-standalone.c
+
+OBJECTS = $(SOURCES:.c=.o)
+CFLAGS = -std=c99 -Wall -D_BSD_SOURCE -D_DEFAULT_SOURCE -O3 -DNDEBUG -fPIC
+LDFLAGS = -lm
+
+CC = gcc
+RM = rm
+
+all: $(TARGET)
+
+$(TARGET): $(OBJECTS)
+ $(CC) -o ./$@ $(OBJECTS) $(LDFLAGS)
+
+$(OBJECTS): %.o: %.c
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+clean:
+ $(RM) -f $(OBJECTS) $(TARGET)
diff --git a/xs/src/avrdude/avrdude-slic3r.cpp b/xs/src/avrdude/avrdude-slic3r.cpp
index 030353413..4a7f22d6e 100644
--- a/xs/src/avrdude/avrdude-slic3r.cpp
+++ b/xs/src/avrdude/avrdude-slic3r.cpp
@@ -36,6 +36,7 @@ struct AvrDude::priv
std::string sys_config;
std::deque<std::vector<std::string>> args;
size_t current_args_set = 0;
+ bool cancelled = false;
RunFn run_fn;
MessageFn message_fn;
ProgressFn progress_fn;
@@ -141,11 +142,16 @@ AvrDude::Ptr AvrDude::run()
if (self->p) {
auto avrdude_thread = std::thread([self]() {
+ bool cancel = false;
+ int res = -1;
+
if (self->p->run_fn) {
- self->p->run_fn();
+ self->p->run_fn(*self);
}
- auto res = self->p->run();
+ if (! self->p->cancelled) {
+ res = self->p->run();
+ }
if (self->p->complete_fn) {
self->p->complete_fn(res, self->p->current_args_set);
@@ -160,7 +166,10 @@ AvrDude::Ptr AvrDude::run()
void AvrDude::cancel()
{
- ::avrdude_cancel();
+ if (p) {
+ p->cancelled = true;
+ ::avrdude_cancel();
+ }
}
void AvrDude::join()
diff --git a/xs/src/avrdude/avrdude-slic3r.hpp b/xs/src/avrdude/avrdude-slic3r.hpp
index 273aa2378..399df2358 100644
--- a/xs/src/avrdude/avrdude-slic3r.hpp
+++ b/xs/src/avrdude/avrdude-slic3r.hpp
@@ -12,7 +12,7 @@ class AvrDude
{
public:
typedef std::shared_ptr<AvrDude> Ptr;
- typedef std::function<void()> RunFn;
+ typedef std::function<void(AvrDude&)> RunFn;
typedef std::function<void(const char * /* msg */, unsigned /* size */)> MessageFn;
typedef std::function<void(const char * /* task */, unsigned /* progress */)> ProgressFn;
typedef std::function<void(int /* exit status */, size_t /* args_id */)> CompleteFn;
@@ -31,7 +31,8 @@ public:
AvrDude& push_args(std::vector<std::string> args);
// Set a callback to be called just after run() before avrdude is ran
- // This can be used to perform any needed setup tasks from the background thread.
+ // This can be used to perform any needed setup tasks from the background thread,
+ // and, optionally, to cancel by writing true to the `cancel` argument.
// This has no effect when using run_sync().
AvrDude& on_run(RunFn fn);
diff --git a/xs/src/avrdude/fileio.c b/xs/src/avrdude/fileio.c
index aa57f5587..708159295 100644
--- a/xs/src/avrdude/fileio.c
+++ b/xs/src/avrdude/fileio.c
@@ -98,11 +98,11 @@ static int fileio_num(struct fioparms * fio,
char * filename, FILE * f, AVRMEM * mem, int size,
FILEFMT fmt);
-static int fmt_autodetect(char * fname, size_t offset);
+static int fmt_autodetect(char * fname, unsigned section);
-static FILE *fopen_and_seek(const char *filename, const char *mode, size_t offset)
+static FILE *fopen_and_seek(const char *filename, const char *mode, unsigned section)
{
FILE *file;
// On Windows we need to convert the filename to UTF-16
@@ -118,16 +118,38 @@ static FILE *fopen_and_seek(const char *filename, const char *mode, size_t offse
file = fopen(filename, mode);
#endif
- if (file != NULL) {
- // Some systems allow seeking past the end of file, so we need check for that first and disallow
- if (fseek(file, 0, SEEK_END) != 0
- || offset >= ftell(file)
- || fseek(file, offset, SEEK_SET) != 0
- ) {
- fclose(file);
- file = NULL;
- errno = EINVAL;
+ if (file == NULL) {
+ return NULL;
+ }
+
+ // Seek to the specified 'section'
+ static const char *hex_terminator = ":00000001FF\r";
+ unsigned terms_seen = 0;
+ char buffer[MAX_LINE_LEN + 1];
+
+ while (terms_seen < section && fgets(buffer, MAX_LINE_LEN, file) != NULL) {
+ size_t len = strlen(buffer);
+
+ if (buffer[len - 1] == '\n') {
+ len--;
+ buffer[len] = 0;
+ }
+ if (buffer[len - 1] != '\r') {
+ buffer[len] = '\r';
+ len++;
+ buffer[len] = 0;
}
+
+ if (strcmp(buffer, hex_terminator) == 0) {
+ // Found a section terminator
+ terms_seen++;
+ }
+ }
+
+ if (feof(file)) {
+ // Section not found
+ fclose(file);
+ return NULL;
}
return file;
@@ -1392,7 +1414,7 @@ int fileio_setparms(int op, struct fioparms * fp,
-static int fmt_autodetect(char * fname, size_t offset)
+static int fmt_autodetect(char * fname, unsigned section)
{
FILE * f;
unsigned char buf[MAX_LINE_LEN];
@@ -1402,9 +1424,9 @@ static int fmt_autodetect(char * fname, size_t offset)
int first = 1;
#if defined(WIN32NATIVE)
- f = fopen_and_seek(fname, "r", offset);
+ f = fopen_and_seek(fname, "r", section);
#else
- f = fopen_and_seek(fname, "rb", offset);
+ f = fopen_and_seek(fname, "rb", section);
#endif
if (f == NULL) {
@@ -1480,7 +1502,7 @@ static int fmt_autodetect(char * fname, size_t offset)
int fileio(int op, char * filename, FILEFMT format,
- struct avrpart * p, char * memtype, int size, size_t offset)
+ struct avrpart * p, char * memtype, int size, unsigned section)
{
int rc;
FILE * f;
@@ -1539,7 +1561,7 @@ int fileio(int op, char * filename, FILEFMT format,
return -1;
}
- format_detect = fmt_autodetect(fname, offset);
+ format_detect = fmt_autodetect(fname, section);
if (format_detect < 0) {
avrdude_message(MSG_INFO, "%s: can't determine file format for %s, specify explicitly\n",
progname, fname);
@@ -1570,7 +1592,7 @@ int fileio(int op, char * filename, FILEFMT format,
if (format != FMT_IMM) {
if (!using_stdio) {
- f = fopen_and_seek(fname, fio.mode, offset);
+ f = fopen_and_seek(fname, fio.mode, section);
if (f == NULL) {
avrdude_message(MSG_INFO, "%s: can't open %s file %s: %s\n",
progname, fio.iodesc, fname, strerror(errno));
diff --git a/xs/src/avrdude/libavrdude.h b/xs/src/avrdude/libavrdude.h
index 536f1a2f7..aef792476 100644
--- a/xs/src/avrdude/libavrdude.h
+++ b/xs/src/avrdude/libavrdude.h
@@ -821,7 +821,7 @@ extern "C" {
char * fmtstr(FILEFMT format);
int fileio(int op, char * filename, FILEFMT format,
- struct avrpart * p, char * memtype, int size, size_t offset);
+ struct avrpart * p, char * memtype, int size, unsigned section);
#ifdef __cplusplus
}
@@ -870,7 +870,7 @@ enum updateflags {
typedef struct update_t {
char * memtype;
int op;
- size_t offset;
+ unsigned section;
char * filename;
int format;
} UPDATE;
@@ -882,7 +882,7 @@ extern "C" {
extern UPDATE * parse_op(char * s);
extern UPDATE * dup_update(UPDATE * upd);
extern UPDATE * new_update(int op, char * memtype, int filefmt,
- char * filename, size_t offset);
+ char * filename, unsigned section);
extern void free_update(UPDATE * upd);
extern int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd,
enum updateflags flags);
diff --git a/xs/src/avrdude/main-standalone.c b/xs/src/avrdude/main-standalone.c
new file mode 100644
index 000000000..359a055ca
--- /dev/null
+++ b/xs/src/avrdude/main-standalone.c
@@ -0,0 +1,9 @@
+#include "avrdude.h"
+
+
+static const char* SYS_CONFIG = "/etc/avrdude-slic3r.conf";
+
+int main(int argc, char *argv[])
+{
+ return avrdude_main(argc, argv, SYS_CONFIG);
+}
diff --git a/xs/src/avrdude/main.c b/xs/src/avrdude/main.c
index d4c34fe44..5d73403b0 100644
--- a/xs/src/avrdude/main.c
+++ b/xs/src/avrdude/main.c
@@ -194,7 +194,7 @@ static void usage(void)
" -F Override invalid signature check.\n"
" -e Perform a chip erase.\n"
" -O Perform RC oscillator calibration (see AVR053). \n"
- " -U <memtype>:r|w|v:<offset>:<filename>[:format]\n"
+ " -U <memtype>:r|w|v:<section>:<filename>[:format]\n"
" Memory operation specification.\n"
" Multiple -U options are allowed, each request\n"
" is performed in the order specified.\n"
diff --git a/xs/src/avrdude/ser_win32.c b/xs/src/avrdude/ser_win32.c
index 20d085d13..3a05cfa90 100644
--- a/xs/src/avrdude/ser_win32.c
+++ b/xs/src/avrdude/ser_win32.c
@@ -311,8 +311,10 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
static void ser_close(union filedescriptor *fd)
{
if (serial_over_ethernet) {
+#ifdef HAVE_LIBWS2_32
closesocket(fd->ifd);
WSACleanup();
+#endif
} else {
HANDLE hComPort=(HANDLE)fd->pfd;
if (hComPort != INVALID_HANDLE_VALUE)
diff --git a/xs/src/avrdude/update.c b/xs/src/avrdude/update.c
index e9dd6e325..417cbf71d 100644
--- a/xs/src/avrdude/update.c
+++ b/xs/src/avrdude/update.c
@@ -101,22 +101,22 @@ UPDATE * parse_op(char * s)
p++;
- // Extension: Parse file contents offset
- size_t offset = 0;
+ // Extension: Parse file section number
+ unsigned section = 0;
for (; *p != ':'; p++) {
if (*p >= '0' && *p <= '9') {
- offset *= 10;
- offset += *p - 0x30;
+ section *= 10;
+ section += *p - 0x30;
} else {
- avrdude_message(MSG_INFO, "%s: invalid update specification: offset is not a number\n", progname);
+ avrdude_message(MSG_INFO, "%s: invalid update specification: <section> is not a number\n", progname);
free(upd->memtype);
free(upd);
return NULL;
}
}
- upd->offset = offset;
+ upd->section = section;
p++;
/*
@@ -194,7 +194,7 @@ UPDATE * dup_update(UPDATE * upd)
return u;
}
-UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, size_t offset)
+UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, unsigned section)
{
UPDATE * u;
@@ -208,7 +208,7 @@ UPDATE * new_update(int op, char * memtype, int filefmt, char * filename, size_t
u->filename = strdup(filename);
u->op = op;
u->format = filefmt;
- u->offset = offset;
+ u->section = section;
return u;
}
@@ -286,7 +286,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
progname,
strcmp(upd->filename, "-")==0 ? "<stdin>" : upd->filename);
}
- rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset);
+ rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->section);
if (rc < 0) {
avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
progname, upd->filename);
@@ -351,7 +351,7 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f
progname, mem->desc, upd->filename);
}
- rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->offset);
+ rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1, upd->section);
if (rc < 0) {
avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n",
progname, upd->filename);
diff --git a/xs/src/libslic3r/GCodeSender.cpp b/xs/src/libslic3r/GCodeSender.cpp
index c3530e00f..0988091ce 100644
--- a/xs/src/libslic3r/GCodeSender.cpp
+++ b/xs/src/libslic3r/GCodeSender.cpp
@@ -2,6 +2,7 @@
#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>
@@ -568,16 +569,12 @@ GCodeSender::set_DTR(bool on)
void
GCodeSender::reset()
{
- this->set_DTR(false);
- boost::this_thread::sleep(boost::posix_time::milliseconds(200));
- this->set_DTR(true);
- boost::this_thread::sleep(boost::posix_time::milliseconds(200));
- this->set_DTR(false);
- boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
- {
- boost::lock_guard<boost::mutex> l(this->queue_mutex);
- this->can_send = true;
- }
+ 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
diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index 90ebface2..87519d5e7 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -611,14 +611,13 @@ bool arrange(Model &model, coordf_t dist, const Slic3r::BoundingBoxf* bb,
// The score is a weighted sum of the distance from pile center
// and the pile size
score = GRAVITY_RATIO * dist + DENSITY_RATIO * density;
- std::cout << "big " << std::endl;
} else if(itemnormarea < BIG_ITEM_TRESHOLD && bigs.empty()) {
// If there are no big items, only small, we should consider the
// density here as well to not get silly results
auto bindist = pl::distance(ibb.center(), bin.center()) / norm;
auto density = std::sqrt(fullbb.width()*fullbb.height()) / norm;
- score = GRAVITY_RATIO* bindist + DENSITY_RATIO * density;
+ score = GRAVITY_RATIO * bindist + DENSITY_RATIO * density;
} else {
// Here there are the small items that should be placed around the
// already processed bigger items.
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index 62659033a..6ec339153 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -210,11 +210,12 @@ GLVolume::GLVolume(float r, float g, float b, float a)
, selected(false)
, is_active(true)
, zoom_to_volumes(true)
- , outside_printer_detection_enabled(true)
+ , shader_outside_printer_detection_enabled(false)
, is_outside(false)
, hover(false)
, is_modifier(false)
, is_wipe_tower(false)
+ , is_extrusion_path(false)
, tverts_range(0, size_t(-1))
, qverts_range(0, size_t(-1))
{
@@ -250,7 +251,7 @@ void GLVolume::set_render_color()
set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4);
else if (hover)
set_render_color(HOVER_COLOR, 4);
- else if (is_outside)
+ else if (is_outside && shader_outside_printer_detection_enabled)
set_render_color(OUTSIDE_COLOR, 4);
else
set_render_color(color, 4);
@@ -441,7 +442,7 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c
::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
if (detection_id != -1)
- ::glUniform1i(detection_id, outside_printer_detection_enabled ? 1 : 0);
+ ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0);
if (worldmatrix_id != -1)
::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
@@ -460,7 +461,7 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c
::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
if (detection_id != -1)
- ::glUniform1i(detection_id, outside_printer_detection_enabled ? 1 : 0);
+ ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0);
if (worldmatrix_id != -1)
::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
@@ -633,7 +634,7 @@ std::vector<int> GLVolumeCollection::load_object(
v.extruder_id = extruder_id;
}
v.is_modifier = model_volume->modifier;
- v.outside_printer_detection_enabled = !model_volume->modifier;
+ v.shader_outside_printer_detection_enabled = !model_volume->modifier;
v.set_origin(Pointf3(instance->offset.x, instance->offset.y, 0.0));
v.set_angle_z(instance->rotation);
v.set_scale_factor(instance->scaling_factor);
@@ -1786,6 +1787,11 @@ void _3DScene::enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable)
s_canvas_mgr.enable_force_zoom_to_bed(canvas, enable);
}
+void _3DScene::enable_dynamic_background(wxGLCanvas* canvas, bool enable)
+{
+ s_canvas_mgr.enable_dynamic_background(canvas, enable);
+}
+
void _3DScene::allow_multisample(wxGLCanvas* canvas, bool allow)
{
s_canvas_mgr.allow_multisample(canvas, allow);
@@ -1968,24 +1974,14 @@ void _3DScene::reload_scene(wxGLCanvas* canvas, bool force)
s_canvas_mgr.reload_scene(canvas, force);
}
-void _3DScene::load_print_toolpaths(wxGLCanvas* canvas)
-{
- s_canvas_mgr.load_print_toolpaths(canvas);
-}
-
-void _3DScene::load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& str_tool_colors)
-{
- s_canvas_mgr.load_print_object_toolpaths(canvas, print_object, str_tool_colors);
-}
-
-void _3DScene::load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors)
+void _3DScene::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors)
{
- s_canvas_mgr.load_wipe_tower_toolpaths(canvas, str_tool_colors);
+ s_canvas_mgr.load_gcode_preview(canvas, preview_data, str_tool_colors);
}
-void _3DScene::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors)
+void _3DScene::load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors)
{
- s_canvas_mgr.load_gcode_preview(canvas, preview_data, str_tool_colors);
+ s_canvas_mgr.load_preview(canvas, str_tool_colors);
}
void _3DScene::reset_legend_texture()
diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp
index 5409b9588..a2050e5a1 100644
--- a/xs/src/slic3r/GUI/3DScene.hpp
+++ b/xs/src/slic3r/GUI/3DScene.hpp
@@ -289,8 +289,8 @@ public:
bool is_active;
// Whether or not to use this volume when applying zoom_to_volumes()
bool zoom_to_volumes;
- // Wheter or not this volume is enabled for outside print volume detection.
- bool outside_printer_detection_enabled;
+ // Wheter or not this volume is enabled for outside print volume detection in shader.
+ bool shader_outside_printer_detection_enabled;
// Wheter or not this volume is outside print volume.
bool is_outside;
// Boolean: Is mouse over this object?
@@ -299,6 +299,8 @@ public:
bool is_modifier;
// Wheter or not this volume has been generated from the wipe tower
bool is_wipe_tower;
+ // Wheter or not this volume has been generated from an extrusion path
+ bool is_extrusion_path;
// Interleaved triangles & normals with indexed triangles & quads.
GLIndexedVertexArray indexed_vertex_array;
@@ -497,6 +499,7 @@ public:
static void enable_gizmos(wxGLCanvas* canvas, bool enable);
static void enable_shader(wxGLCanvas* canvas, bool enable);
static void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable);
+ static void enable_dynamic_background(wxGLCanvas* canvas, bool enable);
static void allow_multisample(wxGLCanvas* canvas, bool allow);
static void zoom_to_bed(wxGLCanvas* canvas);
@@ -536,10 +539,8 @@ public:
static void reload_scene(wxGLCanvas* canvas, bool force);
- static void load_print_toolpaths(wxGLCanvas* canvas);
- static void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& str_tool_colors);
- static void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
static void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors);
+ static void load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
static void reset_legend_texture();
diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp
index d74743055..30339e3cb 100644
--- a/xs/src/slic3r/GUI/FirmwareDialog.cpp
+++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp
@@ -1,12 +1,23 @@
-#include "FirmwareDialog.hpp"
-
#include <numeric>
#include <algorithm>
+#include <thread>
+#include <stdexcept>
#include <boost/format.hpp>
+#include <boost/asio.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/log/trivial.hpp>
+#include "libslic3r/Utils.hpp"
+#include "avrdude/avrdude-slic3r.hpp"
+#include "GUI.hpp"
+#include "MsgDialog.hpp"
+#include "../Utils/HexFile.hpp"
+#include "../Utils/Serial.hpp"
+
+// wx includes need to come after asio because of the WinSock.h problem
+#include "FirmwareDialog.hpp"
+
#include <wx/app.h>
#include <wx/event.h>
#include <wx/sizer.h>
@@ -22,16 +33,18 @@
#include <wx/collpane.h>
#include <wx/msgdlg.h>
-#include "libslic3r/Utils.hpp"
-#include "avrdude/avrdude-slic3r.hpp"
-#include "GUI.hpp"
-#include "../Utils/Serial.hpp"
namespace fs = boost::filesystem;
+namespace asio = boost::asio;
+using boost::system::error_code;
namespace Slic3r {
+using Utils::HexFile;
+using Utils::SerialPortInfo;
+using Utils::Serial;
+
// This enum discriminates the kind of information in EVT_AVRDUDE,
// it's stored in the ExtraLong field of wxCommandEvent.
@@ -40,6 +53,7 @@ enum AvrdudeEvent
AE_MESSAGE,
AE_PROGRESS,
AE_EXIT,
+ AE_ERROR,
};
wxDECLARE_EVENT(EVT_AVRDUDE, wxCommandEvent);
@@ -52,7 +66,6 @@ struct FirmwareDialog::priv
{
enum AvrDudeComplete
{
- AC_NONE,
AC_SUCCESS,
AC_FAILURE,
AC_CANCEL,
@@ -61,7 +74,7 @@ struct FirmwareDialog::priv
FirmwareDialog *q; // PIMPL back pointer ("Q-Pointer")
wxComboBox *port_picker;
- std::vector<Utils::SerialPortInfo> ports;
+ std::vector<SerialPortInfo> ports;
wxFilePickerCtrl *hex_picker;
wxStaticText *txt_status;
wxGauge *progressbar;
@@ -80,7 +93,9 @@ struct FirmwareDialog::priv
AvrDude::Ptr avrdude;
std::string avrdude_config;
unsigned progress_tasks_done;
+ unsigned progress_tasks_bar;
bool cancelled;
+ const bool extra_verbose; // For debugging
priv(FirmwareDialog *q) :
q(q),
@@ -89,16 +104,25 @@ struct FirmwareDialog::priv
timer_pulse(q),
avrdude_config((fs::path(::Slic3r::resources_dir()) / "avrdude" / "avrdude.conf").string()),
progress_tasks_done(0),
- cancelled(false)
+ progress_tasks_bar(0),
+ cancelled(false),
+ extra_verbose(false)
{}
void find_serial_ports();
- void flashing_start(bool flashing_l10n);
+ void flashing_start(unsigned tasks);
void flashing_done(AvrDudeComplete complete);
- size_t hex_lang_offset(const wxString &path);
+ void check_model_id(const HexFile &metadata, const SerialPortInfo &port);
+
+ void prepare_common(AvrDude &, const SerialPortInfo &port, const std::string &filename);
+ void prepare_mk2(AvrDude &, const SerialPortInfo &port, const std::string &filename);
+ void prepare_mk3(AvrDude &, const SerialPortInfo &port, const std::string &filename);
+ void prepare_mm_control(AvrDude &, const SerialPortInfo &port, const std::string &filename);
void perform_upload();
+
void cancel();
void on_avrdude(const wxCommandEvent &evt);
+ void ensure_joined();
};
void FirmwareDialog::priv::find_serial_ports()
@@ -108,7 +132,7 @@ void FirmwareDialog::priv::find_serial_ports()
this->ports = new_ports;
port_picker->Clear();
for (const auto &port : this->ports)
- port_picker->Append(port.friendly_name);
+ port_picker->Append(wxString::FromUTF8(port.friendly_name.data()));
if (ports.size() > 0) {
int idx = port_picker->GetValue().IsEmpty() ? 0 : -1;
for (int i = 0; i < (int)this->ports.size(); ++ i)
@@ -122,7 +146,7 @@ void FirmwareDialog::priv::find_serial_ports()
}
}
-void FirmwareDialog::priv::flashing_start(bool flashing_l10n)
+void FirmwareDialog::priv::flashing_start(unsigned tasks)
{
txt_stdout->Clear();
txt_status->SetLabel(_(L("Flashing in progress. Please do not disconnect the printer!")));
@@ -132,9 +156,10 @@ void FirmwareDialog::priv::flashing_start(bool flashing_l10n)
hex_picker->Disable();
btn_close->Disable();
btn_flash->SetLabel(btn_flash_label_flashing);
- progressbar->SetRange(flashing_l10n ? 500 : 200); // See progress callback below
+ progressbar->SetRange(200 * tasks); // See progress callback below
progressbar->SetValue(0);
progress_tasks_done = 0;
+ progress_tasks_bar = 0;
cancelled = false;
timer_pulse.Start(50);
}
@@ -158,63 +183,49 @@ void FirmwareDialog::priv::flashing_done(AvrDudeComplete complete)
}
}
-size_t FirmwareDialog::priv::hex_lang_offset(const wxString &path)
+void FirmwareDialog::priv::check_model_id(const HexFile &metadata, const SerialPortInfo &port)
{
- fs::ifstream file(fs::path(path.wx_str()));
- if (! file.good()) {
- return 0;
+ if (metadata.model_id.empty()) {
+ // No data to check against
+ return;
+ }
+
+ asio::io_service io;
+ Serial serial(io, port.port, 115200);
+ serial.printer_setup();
+
+ enum {
+ TIMEOUT = 1000,
+ RETREIES = 3,
+ };
+
+ if (! serial.printer_ready_wait(RETREIES, TIMEOUT)) {
+ throw wxString::Format(_(L("Could not connect to the printer at %s")), port.port);
}
- static const char *hex_terminator = ":00000001FF\r";
- size_t res = 0;
std::string line;
- while (getline(file, line, '\n').good()) {
- // Account for LF vs CRLF
- if (!line.empty() && line.back() != '\r') {
- line.push_back('\r');
+ error_code ec;
+ serial.printer_write_line("PRUSA Rev");
+ while (serial.read_line(TIMEOUT, line, ec)) {
+ if (ec) { throw wxString::Format(_(L("Could not connect to the printer at %s")), port.port); }
+ if (line == "ok") { continue; }
+
+ if (line == metadata.model_id) {
+ return;
+ } else {
+ throw wxString::Format(_(L(
+ "The firmware hex file does not match the printer model.\n"
+ "The hex file is intended for:\n %s\n"
+ "Printer reports:\n %s"
+ )), metadata.model_id, line);
}
- if (line == hex_terminator) {
- if (res == 0) {
- // This is the first terminator seen, save the position
- res = file.tellg();
- } else {
- // We've found another terminator, return the offset just after the first one
- // which is the start of the second 'section'.
- return res;
- }
- }
+ line.clear();
}
-
- return 0;
}
-void FirmwareDialog::priv::perform_upload()
+void FirmwareDialog::priv::prepare_common(AvrDude &avrdude, const SerialPortInfo &port, const std::string &filename)
{
- auto filename = hex_picker->GetPath();
- std::string port = port_picker->GetValue().ToStdString();
- int selection = port_picker->GetSelection();
- if (selection != -1) {
- // Verify whether the combo box list selection equals to the combo box edit value.
- if (this->ports[selection].friendly_name == port)
- port = this->ports[selection].port;
- }
- if (filename.IsEmpty() || port.empty()) { return; }
-
- const bool extra_verbose = false; // For debugging
- const auto lang_offset = hex_lang_offset(filename);
- const auto filename_utf8 = filename.utf8_str();
-
- flashing_start(lang_offset > 0);
-
- // It is ok here to use the q-pointer to the FirmwareDialog
- // because the dialog ensures it doesn't exit before the background thread is done.
- auto q = this->q;
-
- // Init the avrdude object
- AvrDude avrdude(avrdude_config);
-
- // Build argument list(s)
std::vector<std::string> args {{
extra_verbose ? "-vvvvv" : "-v",
"-p", "atmega2560",
@@ -222,11 +233,10 @@ void FirmwareDialog::priv::perform_upload()
// The Prusa's avrdude is patched to never send semicolons inside the data packets, as the USB to serial chip
// is flashed with a buggy firmware.
"-c", "wiring",
- "-P", port,
+ "-P", port.port,
"-b", "115200", // TODO: Allow other rates? Ditto below.
"-D",
- // XXX: Safe mode?
- "-U", (boost::format("flash:w:0:%1%:i") % filename_utf8.data()).str(),
+ "-U", (boost::format("flash:w:0:%1%:i") % filename).str(),
}};
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
@@ -235,32 +245,167 @@ void FirmwareDialog::priv::perform_upload()
});
avrdude.push_args(std::move(args));
-
- if (lang_offset > 0) {
- // The hex file also contains another section with l10n data to be flashed into the external flash on MK3 (Einsy)
- // This is done via another avrdude invocation, here we build arg list for that:
- std::vector<std::string> args_l10n {{
- extra_verbose ? "-vvvvv" : "-v",
- "-p", "atmega2560",
- // Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2).
- // The Prusa's avrdude is patched again to never send semicolons inside the data packets.
- "-c", "arduino",
- "-P", port,
- "-b", "115200",
- "-D",
- "-u", // disable safe mode
- "-U", (boost::format("flash:w:%1%:%2%:i") % lang_offset % filename_utf8.data()).str(),
- }};
-
- BOOST_LOG_TRIVIAL(info) << "Invoking avrdude for external flash flashing, arguments: "
- << std::accumulate(std::next(args_l10n.begin()), args_l10n.end(), args_l10n[0], [](std::string a, const std::string &b) {
- return a + ' ' + b;
- });
-
- avrdude.push_args(std::move(args_l10n));
+}
+
+void FirmwareDialog::priv::prepare_mk2(AvrDude &avrdude, const SerialPortInfo &port, const std::string &filename)
+{
+ prepare_common(avrdude, port, filename);
+}
+
+void FirmwareDialog::priv::prepare_mk3(AvrDude &avrdude, const SerialPortInfo &port, const std::string &filename)
+{
+ prepare_common(avrdude, port, filename);
+
+ // The hex file also contains another section with l10n data to be flashed into the external flash on MK3 (Einsy)
+ // This is done via another avrdude invocation, here we build arg list for that:
+ std::vector<std::string> args_l10n {{
+ extra_verbose ? "-vvvvv" : "-v",
+ "-p", "atmega2560",
+ // Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2).
+ // The Prusa's avrdude is patched again to never send semicolons inside the data packets.
+ "-c", "arduino",
+ "-P", port.port,
+ "-b", "115200",
+ "-D",
+ "-u", // disable safe mode
+ "-U", (boost::format("flash:w:1:%1%:i") % filename).str(),
+ }};
+
+ BOOST_LOG_TRIVIAL(info) << "Invoking avrdude for external flash flashing, arguments: "
+ << std::accumulate(std::next(args_l10n.begin()), args_l10n.end(), args_l10n[0], [](std::string a, const std::string &b) {
+ return a + ' ' + b;
+ });
+
+ avrdude.push_args(std::move(args_l10n));
+}
+
+void FirmwareDialog::priv::prepare_mm_control(AvrDude &avrdude, const SerialPortInfo &port_in, const std::string &filename)
+{
+ // Check if the port has the PID/VID of 0x2c99/3
+ // If not, check if it is the MMU (0x2c99/4) and reboot the by opening @ 1200 bauds
+ BOOST_LOG_TRIVIAL(info) << "Flashing MMU 2.0, looking for VID/PID 0x2c99/3 or 0x2c99/4 ...";
+ SerialPortInfo port = port_in;
+ if (! port.id_match(0x2c99, 3)) {
+ if (! port.id_match(0x2c99, 4)) {
+ // This is not a Prusa MMU 2.0 device
+ BOOST_LOG_TRIVIAL(error) << boost::format("Not a Prusa MMU 2.0 device: `%1%`") % port.port;
+ throw wxString::Format(_(L("The device at `%s` is not am Original Prusa i3 MMU 2.0 device")), port.port);
+ }
+
+ BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/4 at `%1%`, rebooting the device ...") % port.port;
+
+ {
+ asio::io_service io;
+ Serial serial(io, port.port, 1200);
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ }
+
+ // Wait for the bootloader to show up
+ std::this_thread::sleep_for(std::chrono::milliseconds(2000));
+
+ // Look for the rebooted device
+ BOOST_LOG_TRIVIAL(info) << "... looking for VID/PID 0x2c99/3 ...";
+ auto new_ports = Utils::scan_serial_ports_extended();
+ unsigned hits = 0;
+ for (auto &&new_port : new_ports) {
+ if (new_port.id_match(0x2c99, 3)) {
+ hits++;
+ port = std::move(new_port);
+ }
+ }
+
+ if (hits == 0) {
+ BOOST_LOG_TRIVIAL(error) << "No VID/PID 0x2c99/3 device found after rebooting the MMU 2.0";
+ throw wxString::Format(_(L("Failed to reboot the device at `%s` for programming")), port.port);
+ } else if (hits > 1) {
+ // We found multiple 0x2c99/3 devices, this is bad, because there's no way to find out
+ // which one is the one user wants to flash.
+ BOOST_LOG_TRIVIAL(error) << "Several VID/PID 0x2c99/3 devices found after rebooting the MMU 2.0";
+ throw wxString::Format(_(L("Multiple Original Prusa i3 MMU 2.0 devices found. Please only connect one at a time for flashing.")), port.port);
+ }
}
-
+
+ BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/3 at `%1%`, flashing ...") % port.port;
+
+ std::vector<std::string> args {{
+ extra_verbose ? "-vvvvv" : "-v",
+ "-p", "atmega32u4",
+ "-c", "avr109",
+ "-P", port.port,
+ "-b", "57600",
+ "-D",
+ "-U", (boost::format("flash:w:0:%1%:i") % filename).str(),
+ }};
+
+ BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
+ << std::accumulate(std::next(args.begin()), args.end(), args[0], [](std::string a, const std::string &b) {
+ return a + ' ' + b;
+ });
+
+ avrdude.push_args(std::move(args));
+}
+
+
+void FirmwareDialog::priv::perform_upload()
+{
+ auto filename = hex_picker->GetPath();
+ if (filename.IsEmpty()) { return; }
+
+ int selection = port_picker->GetSelection();
+ if (selection == wxNOT_FOUND) { return; }
+
+ const SerialPortInfo &port = this->ports[selection];
+ // Verify whether the combo box list selection equals to the combo box edit value.
+ if (wxString::FromUTF8(this->ports[selection].friendly_name.data()) != port_picker->GetValue()) {
+ return;
+ }
+
+ const bool extra_verbose = false; // For debugging
+ HexFile metadata(filename.wx_str());
+ const std::string filename_utf8(filename.utf8_str().data());
+
+ flashing_start(metadata.device == HexFile::DEV_MK3 ? 2 : 1);
+
+ // Init the avrdude object
+ AvrDude avrdude(avrdude_config);
+
+ // It is ok here to use the q-pointer to the FirmwareDialog
+ // because the dialog ensures it doesn't exit before the background thread is done.
+ auto q = this->q;
+
this->avrdude = avrdude
+ .on_run([=](AvrDude &avrdude) {
+ auto queue_error = [&](wxString message) {
+ avrdude.cancel();
+
+ auto evt = new wxCommandEvent(EVT_AVRDUDE, this->q->GetId());
+ evt->SetExtraLong(AE_ERROR);
+ evt->SetString(std::move(message));
+ wxQueueEvent(this->q, evt);
+ };
+
+ try {
+ switch (metadata.device) {
+ case HexFile::DEV_MK3:
+ this->check_model_id(metadata, port);
+ this->prepare_mk3(avrdude, port, filename_utf8);
+ break;
+
+ case HexFile::DEV_MM_CONTROL:
+ this->check_model_id(metadata, port);
+ this->prepare_mm_control(avrdude, port, filename_utf8);
+ break;
+
+ default:
+ this->prepare_mk2(avrdude, port, filename_utf8);
+ break;
+ }
+ } catch (const wxString &message) {
+ queue_error(message);
+ } catch (const std::exception &ex) {
+ queue_error(wxString::Format(_(L("Error accessing port at %s: %s")), port.port, ex.what()));
+ }
+ })
.on_message(std::move([q, extra_verbose](const char *msg, unsigned /* size */) {
if (extra_verbose) {
BOOST_LOG_TRIVIAL(debug) << "avrdude: " << msg;
@@ -313,12 +458,15 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
// and then display overall progress during the latter tasks.
if (progress_tasks_done > 0) {
- progressbar->SetValue(progress_tasks_done - 100 + evt.GetInt());
+ progressbar->SetValue(progress_tasks_bar + evt.GetInt());
}
if (evt.GetInt() == 100) {
timer_pulse.Stop();
- progress_tasks_done += 100;
+ if (progress_tasks_done % 3 != 0) {
+ progress_tasks_bar += 100;
+ }
+ progress_tasks_done++;
}
break;
@@ -328,11 +476,17 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
complete_kind = cancelled ? AC_CANCEL : (evt.GetInt() == 0 ? AC_SUCCESS : AC_FAILURE);
flashing_done(complete_kind);
+ ensure_joined();
+ break;
- // Make sure the background thread is collected and the AvrDude object reset
- if (avrdude) { avrdude->join(); }
- avrdude.reset();
-
+ case AE_ERROR:
+ txt_stdout->AppendText(evt.GetString());
+ flashing_done(AC_FAILURE);
+ ensure_joined();
+ {
+ GUI::ErrorDialog dlg(this->q, evt.GetString());
+ dlg.ShowModal();
+ }
break;
default:
@@ -340,6 +494,13 @@ void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt)
}
}
+void FirmwareDialog::priv::ensure_joined()
+{
+ // Make sure the background thread is collected and the AvrDude object reset
+ if (avrdude) { avrdude->join(); }
+ avrdude.reset();
+}
+
// Public
diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp
index 722f1c112..6396233e8 100644
--- a/xs/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp
@@ -51,6 +51,9 @@ static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
+static const float DEFAULT_BG_COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f };
+static const float ERROR_BG_COLOR[3] = { 144.0f / 255.0f, 49.0f / 255.0f, 10.0f / 255.0f };
+
namespace Slic3r {
namespace GUI {
@@ -1703,6 +1706,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
, m_picking_enabled(false)
, m_moving_enabled(false)
, m_shader_enabled(false)
+ , m_dynamic_background_enabled(false)
, m_multisample_allowed(false)
, m_color_by("volume")
, m_select_by("object")
@@ -1828,7 +1832,6 @@ unsigned int GLCanvas3D::get_volumes_count() const
void GLCanvas3D::reset_volumes()
{
-
if (!m_volumes.empty())
{
// ensures this canvas is current
@@ -1839,6 +1842,9 @@ void GLCanvas3D::reset_volumes()
m_volumes.clear();
m_dirty = true;
}
+
+ enable_warning_texture(false);
+ _reset_warning_texture();
}
void GLCanvas3D::deselect_volumes()
@@ -2065,6 +2071,11 @@ void GLCanvas3D::enable_force_zoom_to_bed(bool enable)
m_force_zoom_to_bed_enabled = enable;
}
+void GLCanvas3D::enable_dynamic_background(bool enable)
+{
+ m_dynamic_background_enabled = enable;
+}
+
void GLCanvas3D::allow_multisample(bool allow)
{
m_multisample_allowed = allow;
@@ -2326,372 +2337,6 @@ void GLCanvas3D::reload_scene(bool force)
}
}
-void GLCanvas3D::load_print_toolpaths()
-{
- // ensures this canvas is current
- if (!set_current())
- return;
-
- if (m_print == nullptr)
- return;
-
- if (!m_print->state.is_done(psSkirt) || !m_print->state.is_done(psBrim))
- return;
-
- if (!m_print->has_skirt() && (m_print->config.brim_width.value == 0))
- return;
-
- const float color[] = { 0.5f, 1.0f, 0.5f, 1.0f }; // greenish
-
- // number of skirt layers
- size_t total_layer_count = 0;
- for (const PrintObject* print_object : m_print->objects)
- {
- total_layer_count = std::max(total_layer_count, print_object->total_layer_count());
- }
- size_t skirt_height = m_print->has_infinite_skirt() ? total_layer_count : std::min<size_t>(m_print->config.skirt_height.value, total_layer_count);
- if ((skirt_height == 0) && (m_print->config.brim_width.value > 0))
- skirt_height = 1;
-
- // get first skirt_height layers (maybe this should be moved to a PrintObject method?)
- const PrintObject* object0 = m_print->objects.front();
- std::vector<float> print_zs;
- print_zs.reserve(skirt_height * 2);
- for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++i)
- {
- print_zs.push_back(float(object0->layers[i]->print_z));
- }
- //FIXME why there are support layers?
- for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++i)
- {
- print_zs.push_back(float(object0->support_layers[i]->print_z));
- }
- sort_remove_duplicates(print_zs);
- if (print_zs.size() > skirt_height)
- print_zs.erase(print_zs.begin() + skirt_height, print_zs.end());
-
- m_volumes.volumes.emplace_back(new GLVolume(color));
- GLVolume& volume = *m_volumes.volumes.back();
- for (size_t i = 0; i < skirt_height; ++i) {
- volume.print_zs.push_back(print_zs[i]);
- volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size());
- volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size());
- if (i == 0)
- _3DScene::extrusionentity_to_verts(m_print->brim, print_zs[i], Point(0, 0), volume);
-
- _3DScene::extrusionentity_to_verts(m_print->skirt, print_zs[i], Point(0, 0), volume);
- }
- volume.bounding_box = volume.indexed_vertex_array.bounding_box();
- volume.indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
-}
-
-void GLCanvas3D::load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors)
-{
- std::vector<float> tool_colors = _parse_colors(str_tool_colors);
-
- struct Ctxt
- {
- const Points *shifted_copies;
- std::vector<const Layer*> layers;
- bool has_perimeters;
- bool has_infill;
- bool has_support;
- const std::vector<float>* tool_colors;
-
- // Number of vertices (each vertex is 6x4=24 bytes long)
- static const size_t alloc_size_max() { return 131072; } // 3.15MB
- // static const size_t alloc_size_max () { return 65536; } // 1.57MB
- // static const size_t alloc_size_max () { return 32768; } // 786kB
- static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
-
- static const float* color_perimeters() { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow
- static const float* color_infill() { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish
- static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
-
- // For cloring by a tool, return a parsed color.
- bool color_by_tool() const { return tool_colors != nullptr; }
- size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
- const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
- int volume_idx(int extruder, int feature) const
- {
- return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(extruder - 1, 0)) : feature;
- }
- } ctxt;
-
- ctxt.shifted_copies = &print_object._shifted_copies;
-
- // order layers by print_z
- ctxt.layers.reserve(print_object.layers.size() + print_object.support_layers.size());
- for (const Layer *layer : print_object.layers)
- ctxt.layers.push_back(layer);
- for (const Layer *layer : print_object.support_layers)
- ctxt.layers.push_back(layer);
- std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; });
-
- // Maximum size of an allocation block: 32MB / sizeof(float)
- ctxt.has_perimeters = print_object.state.is_done(posPerimeters);
- ctxt.has_infill = print_object.state.is_done(posInfill);
- ctxt.has_support = print_object.state.is_done(posSupportMaterial);
- ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
-
- BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start";
-
- //FIXME Improve the heuristics for a grain size.
- size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1));
- tbb::spin_mutex new_volume_mutex;
- auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
- auto *volume = new GLVolume(color);
- new_volume_mutex.lock();
- volume->outside_printer_detection_enabled = false;
- m_volumes.volumes.emplace_back(volume);
- new_volume_mutex.unlock();
- return volume;
- };
- const size_t volumes_cnt_initial = m_volumes.volumes.size();
- std::vector<GLVolumeCollection> volumes_per_thread(ctxt.layers.size());
- tbb::parallel_for(
- tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size),
- [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
- std::vector<GLVolume*> vols;
- if (ctxt.color_by_tool()) {
- for (size_t i = 0; i < ctxt.number_tools(); ++i)
- vols.emplace_back(new_volume(ctxt.color_tool(i)));
- }
- else
- vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) };
- for (GLVolume *vol : vols)
- vol->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
- for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
- const Layer *layer = ctxt.layers[idx_layer];
- for (size_t i = 0; i < vols.size(); ++i) {
- GLVolume &vol = *vols[i];
- if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) {
- vol.print_zs.push_back(layer->print_z);
- vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
- vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
- }
- }
- for (const Point &copy : *ctxt.shifted_copies) {
- for (const LayerRegion *layerm : layer->regions) {
- if (ctxt.has_perimeters)
- _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy,
- *vols[ctxt.volume_idx(layerm->region()->config.perimeter_extruder.value, 0)]);
- if (ctxt.has_infill) {
- for (const ExtrusionEntity *ee : layerm->fills.entities) {
- // fill represents infill extrusions of a single island.
- const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
- if (!fill->entities.empty())
- _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy,
- *vols[ctxt.volume_idx(
- is_solid_infill(fill->entities.front()->role()) ?
- layerm->region()->config.solid_infill_extruder :
- layerm->region()->config.infill_extruder,
- 1)]);
- }
- }
- }
- if (ctxt.has_support) {
- const SupportLayer *support_layer = dynamic_cast<const SupportLayer*>(layer);
- if (support_layer) {
- for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
- _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy,
- *vols[ctxt.volume_idx(
- (extrusion_entity->role() == erSupportMaterial) ?
- support_layer->object()->config.support_material_extruder :
- support_layer->object()->config.support_material_interface_extruder,
- 2)]);
- }
- }
- }
- for (size_t i = 0; i < vols.size(); ++i) {
- GLVolume &vol = *vols[i];
- if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
- // Store the vertex arrays and restart their containers,
- vols[i] = new_volume(vol.color);
- GLVolume &vol_new = *vols[i];
- // Assign the large pre-allocated buffers to the new GLVolume.
- vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
- // Copy the content back to the old GLVolume.
- vol.indexed_vertex_array = vol_new.indexed_vertex_array;
- // Finalize a bounding box of the old GLVolume.
- vol.bounding_box = vol.indexed_vertex_array.bounding_box();
- // Clear the buffers, but keep them pre-allocated.
- vol_new.indexed_vertex_array.clear();
- // Just make sure that clear did not clear the reserved memory.
- vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
- }
- }
- }
- for (GLVolume *vol : vols) {
- vol->bounding_box = vol->indexed_vertex_array.bounding_box();
- vol->indexed_vertex_array.shrink_to_fit();
- }
- });
-
- BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results";
- // Remove empty volumes from the newly added volumes.
- m_volumes.volumes.erase(
- std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
- [](const GLVolume *volume) { return volume->empty(); }),
- m_volumes.volumes.end());
- for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
- m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
-
- BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end";
-}
-
-void GLCanvas3D::load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors)
-{
- if ((m_print == nullptr) || m_print->m_wipe_tower_tool_changes.empty())
- return;
-
- if (!m_print->state.is_done(psWipeTower))
- return;
-
- std::vector<float> tool_colors = _parse_colors(str_tool_colors);
-
- struct Ctxt
- {
- const Print *print;
- const std::vector<float> *tool_colors;
-
- // Number of vertices (each vertex is 6x4=24 bytes long)
- static const size_t alloc_size_max() { return 131072; } // 3.15MB
- static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
-
- static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
-
- // For cloring by a tool, return a parsed color.
- bool color_by_tool() const { return tool_colors != nullptr; }
- size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
- const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
- int volume_idx(int tool, int feature) const
- {
- return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(tool, 0)) : feature;
- }
-
- const std::vector<WipeTower::ToolChangeResult>& tool_change(size_t idx) {
- return priming.empty() ?
- ((idx == print->m_wipe_tower_tool_changes.size()) ? final : print->m_wipe_tower_tool_changes[idx]) :
- ((idx == 0) ? priming : (idx == print->m_wipe_tower_tool_changes.size() + 1) ? final : print->m_wipe_tower_tool_changes[idx - 1]);
- }
- std::vector<WipeTower::ToolChangeResult> priming;
- std::vector<WipeTower::ToolChangeResult> final;
- } ctxt;
-
- ctxt.print = m_print;
- ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
- if (m_print->m_wipe_tower_priming)
- ctxt.priming.emplace_back(*m_print->m_wipe_tower_priming.get());
- if (m_print->m_wipe_tower_final_purge)
- ctxt.final.emplace_back(*m_print->m_wipe_tower_final_purge.get());
-
- BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
-
- //FIXME Improve the heuristics for a grain size.
- size_t n_items = m_print->m_wipe_tower_tool_changes.size() + (ctxt.priming.empty() ? 0 : 1);
- size_t grain_size = std::max(n_items / 128, size_t(1));
- tbb::spin_mutex new_volume_mutex;
- auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
- auto *volume = new GLVolume(color);
- new_volume_mutex.lock();
- volume->outside_printer_detection_enabled = false;
- m_volumes.volumes.emplace_back(volume);
- new_volume_mutex.unlock();
- return volume;
- };
- const size_t volumes_cnt_initial = m_volumes.volumes.size();
- std::vector<GLVolumeCollection> volumes_per_thread(n_items);
- tbb::parallel_for(
- tbb::blocked_range<size_t>(0, n_items, grain_size),
- [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
- // Bounding box of this slab of a wipe tower.
- std::vector<GLVolume*> vols;
- if (ctxt.color_by_tool()) {
- for (size_t i = 0; i < ctxt.number_tools(); ++i)
- vols.emplace_back(new_volume(ctxt.color_tool(i)));
- }
- else
- vols = { new_volume(ctxt.color_support()) };
- for (GLVolume *volume : vols)
- volume->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
- for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
- const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.tool_change(idx_layer);
- for (size_t i = 0; i < vols.size(); ++i) {
- GLVolume &vol = *vols[i];
- if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) {
- vol.print_zs.push_back(layer.front().print_z);
- vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
- vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
- }
- }
- for (const WipeTower::ToolChangeResult &extrusions : layer) {
- for (size_t i = 1; i < extrusions.extrusions.size();) {
- const WipeTower::Extrusion &e = extrusions.extrusions[i];
- if (e.width == 0.) {
- ++i;
- continue;
- }
- size_t j = i + 1;
- if (ctxt.color_by_tool())
- for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.f; ++j);
- else
- for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.f; ++j);
- size_t n_lines = j - i;
- Lines lines;
- std::vector<double> widths;
- std::vector<double> heights;
- lines.reserve(n_lines);
- widths.reserve(n_lines);
- heights.assign(n_lines, extrusions.layer_height);
- for (; i < j; ++i) {
- const WipeTower::Extrusion &e = extrusions.extrusions[i];
- assert(e.width > 0.f);
- const WipeTower::Extrusion &e_prev = *(&e - 1);
- lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y));
- widths.emplace_back(e.width);
- }
- _3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z,
- *vols[ctxt.volume_idx(e.tool, 0)]);
- }
- }
- }
- for (size_t i = 0; i < vols.size(); ++i) {
- GLVolume &vol = *vols[i];
- if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
- // Store the vertex arrays and restart their containers,
- vols[i] = new_volume(vol.color);
- GLVolume &vol_new = *vols[i];
- // Assign the large pre-allocated buffers to the new GLVolume.
- vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
- // Copy the content back to the old GLVolume.
- vol.indexed_vertex_array = vol_new.indexed_vertex_array;
- // Finalize a bounding box of the old GLVolume.
- vol.bounding_box = vol.indexed_vertex_array.bounding_box();
- // Clear the buffers, but keep them pre-allocated.
- vol_new.indexed_vertex_array.clear();
- // Just make sure that clear did not clear the reserved memory.
- vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
- }
- }
- for (GLVolume *vol : vols) {
- vol->bounding_box = vol->indexed_vertex_array.bounding_box();
- vol->indexed_vertex_array.shrink_to_fit();
- }
- });
-
- BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results";
- // Remove empty volumes from the newly added volumes.
- m_volumes.volumes.erase(
- std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
- [](const GLVolume *volume) { return volume->empty(); }),
- m_volumes.volumes.end());
- for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
- m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
-
- BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end";
-}
-
void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors)
{
if ((m_canvas != nullptr) && (m_print != nullptr))
@@ -2723,12 +2368,37 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const
_load_shells();
}
+ _update_toolpath_volumes_outside_state();
}
_update_gcode_volumes_visibility(preview_data);
+ _show_warning_texture_if_needed();
}
}
+void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors)
+{
+ if (m_print == nullptr)
+ return;
+
+ _load_print_toolpaths();
+ _load_wipe_tower_toolpaths(str_tool_colors);
+ for (const PrintObject* object : m_print->objects)
+ {
+ if (object != nullptr)
+ _load_print_object_toolpaths(*object, str_tool_colors);
+ }
+
+ for (GLVolume* volume : m_volumes.volumes)
+ {
+ volume->is_extrusion_path = true;
+ }
+
+ _update_toolpath_volumes_outside_state();
+ _show_warning_texture_if_needed();
+ reset_legend_texture();
+}
+
void GLCanvas3D::register_on_viewport_changed_callback(void* callback)
{
if (callback != nullptr)
@@ -3025,6 +2695,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_mouse.set_start_position_2D_as_invalid();
#endif
}
+ else if (evt.Leaving())
+ {
+ // to remove hover when mouse goes out of this canvas
+ m_mouse.position = Pointf((coordf_t)pos.x, (coordf_t)pos.y);
+ render();
+ }
else if (evt.LeftDClick() && (m_hover_volume_id != -1))
m_on_double_click_callback.call();
else if (evt.LeftDown() || evt.RightDown())
@@ -3062,7 +2738,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
{
update_gizmos_data();
m_gizmos.start_dragging();
- m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id();
+ m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id(selected_object_idx);
m_dirty = true;
}
else
@@ -3163,7 +2839,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
{
if (v != nullptr)
{
- if ((m_mouse.drag.move_with_shift && (v->select_group_id == group_id)) || (v->drag_group_id == group_id))
+ if ((m_mouse.drag.move_with_shift && (v->select_group_id == group_id)) || (!m_mouse.drag.move_with_shift && (v->drag_group_id == group_id)))
volumes.push_back(v);
}
}
@@ -3234,7 +2910,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (!volumes.empty())
{
- const BoundingBoxf3& bb = volumes[0]->transformed_bounding_box();
+ BoundingBoxf3 bb;
+ for (const GLVolume* volume : volumes)
+ {
+ bb.merge(volume->transformed_bounding_box());
+ }
const Pointf3& size = bb.size();
m_on_update_geometry_info_callback.call(size.x, size.y, size.z, m_gizmos.get_scale());
}
@@ -3381,7 +3061,13 @@ void GLCanvas3D::on_key_down(wxKeyEvent& evt)
if (key == WXK_DELETE)
m_on_remove_object_callback.call();
else
- evt.Skip();
+ {
+#ifdef __WXOSX__
+ if (key == WXK_BACK)
+ m_on_remove_object_callback.call();
+#endif
+ evt.Skip();
+ }
}
}
@@ -3502,11 +3188,45 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box() const
BoundingBoxf3 GLCanvas3D::_selected_volumes_bounding_box() const
{
BoundingBoxf3 bb;
+
+ std::vector<const GLVolume*> selected_volumes;
for (const GLVolume* volume : m_volumes.volumes)
{
if ((volume != nullptr) && !volume->is_wipe_tower && volume->selected)
+ selected_volumes.push_back(volume);
+ }
+
+ bool use_drag_group_id = selected_volumes.size() > 1;
+ if (use_drag_group_id)
+ {
+ int drag_group_id = selected_volumes[0]->drag_group_id;
+ for (const GLVolume* volume : selected_volumes)
+ {
+ if (drag_group_id != volume->drag_group_id)
+ {
+ use_drag_group_id = false;
+ break;
+ }
+ }
+ }
+
+ if (use_drag_group_id)
+ {
+ for (const GLVolume* volume : selected_volumes)
+ {
+ bb.merge(volume->bounding_box);
+ }
+
+ bb = bb.transformed(selected_volumes[0]->world_matrix());
+ }
+ else
+ {
+ for (const GLVolume* volume : selected_volumes)
+ {
bb.merge(volume->transformed_bounding_box());
+ }
}
+
return bb;
}
@@ -3726,8 +3446,6 @@ void GLCanvas3D::_render_background() const
{
::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- static const float COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f };
-
::glPushMatrix();
::glLoadIdentity();
::glMatrixMode(GL_PROJECTION);
@@ -3739,11 +3457,16 @@ void GLCanvas3D::_render_background() const
::glBegin(GL_QUADS);
::glColor3f(0.0f, 0.0f, 0.0f);
- ::glVertex3f(-1.0f, -1.0f, 1.0f);
- ::glVertex3f(1.0f, -1.0f, 1.0f);
- ::glColor3f(COLOR[0], COLOR[1], COLOR[2]);
- ::glVertex3f(1.0f, 1.0f, 1.0f);
- ::glVertex3f(-1.0f, 1.0f, 1.0f);
+ ::glVertex2f(-1.0f, -1.0f);
+ ::glVertex2f(1.0f, -1.0f);
+
+ if (m_dynamic_background_enabled && _is_any_volume_outside())
+ ::glColor3f(ERROR_BG_COLOR[0], ERROR_BG_COLOR[1], ERROR_BG_COLOR[2]);
+ else
+ ::glColor3f(DEFAULT_BG_COLOR[0], DEFAULT_BG_COLOR[1], DEFAULT_BG_COLOR[2]);
+
+ ::glVertex2f(1.0f, 1.0f);
+ ::glVertex2f(-1.0f, 1.0f);
::glEnd();
::glEnable(GL_DEPTH_TEST);
@@ -4083,33 +3806,382 @@ int GLCanvas3D::_get_first_selected_object_id() const
return -1;
}
-int GLCanvas3D::_get_first_selected_volume_id() const
+int GLCanvas3D::_get_first_selected_volume_id(int object_id) const
{
- if (m_print != nullptr)
+ int volume_id = -1;
+
+ for (const GLVolume* vol : m_volumes.volumes)
{
- int objects_count = (int)m_print->objects.size();
+ ++volume_id;
+ if ((vol != nullptr) && vol->selected && (object_id == vol->select_group_id / 1000000))
+ return volume_id;
+ }
- for (const GLVolume* vol : m_volumes.volumes)
+ return -1;
+}
+
+void GLCanvas3D::_load_print_toolpaths()
+{
+ // ensures this canvas is current
+ if (!set_current())
+ return;
+
+ if (m_print == nullptr)
+ return;
+
+ if (!m_print->state.is_done(psSkirt) || !m_print->state.is_done(psBrim))
+ return;
+
+ if (!m_print->has_skirt() && (m_print->config.brim_width.value == 0))
+ return;
+
+ const float color[] = { 0.5f, 1.0f, 0.5f, 1.0f }; // greenish
+
+ // number of skirt layers
+ size_t total_layer_count = 0;
+ for (const PrintObject* print_object : m_print->objects)
+ {
+ total_layer_count = std::max(total_layer_count, print_object->total_layer_count());
+ }
+ size_t skirt_height = m_print->has_infinite_skirt() ? total_layer_count : std::min<size_t>(m_print->config.skirt_height.value, total_layer_count);
+ if ((skirt_height == 0) && (m_print->config.brim_width.value > 0))
+ skirt_height = 1;
+
+ // get first skirt_height layers (maybe this should be moved to a PrintObject method?)
+ const PrintObject* object0 = m_print->objects.front();
+ std::vector<float> print_zs;
+ print_zs.reserve(skirt_height * 2);
+ for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++i)
+ {
+ print_zs.push_back(float(object0->layers[i]->print_z));
+ }
+ //FIXME why there are support layers?
+ for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++i)
+ {
+ print_zs.push_back(float(object0->support_layers[i]->print_z));
+ }
+ sort_remove_duplicates(print_zs);
+ if (print_zs.size() > skirt_height)
+ print_zs.erase(print_zs.begin() + skirt_height, print_zs.end());
+
+ m_volumes.volumes.emplace_back(new GLVolume(color));
+ GLVolume& volume = *m_volumes.volumes.back();
+ for (size_t i = 0; i < skirt_height; ++i) {
+ volume.print_zs.push_back(print_zs[i]);
+ volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size());
+ volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size());
+ if (i == 0)
+ _3DScene::extrusionentity_to_verts(m_print->brim, print_zs[i], Point(0, 0), volume);
+
+ _3DScene::extrusionentity_to_verts(m_print->skirt, print_zs[i], Point(0, 0), volume);
+ }
+ volume.bounding_box = volume.indexed_vertex_array.bounding_box();
+ volume.indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
+}
+
+void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors)
+{
+ std::vector<float> tool_colors = _parse_colors(str_tool_colors);
+
+ struct Ctxt
+ {
+ const Points *shifted_copies;
+ std::vector<const Layer*> layers;
+ bool has_perimeters;
+ bool has_infill;
+ bool has_support;
+ const std::vector<float>* tool_colors;
+
+ // Number of vertices (each vertex is 6x4=24 bytes long)
+ static const size_t alloc_size_max() { return 131072; } // 3.15MB
+ // static const size_t alloc_size_max () { return 65536; } // 1.57MB
+ // static const size_t alloc_size_max () { return 32768; } // 786kB
+ static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
+
+ static const float* color_perimeters() { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow
+ static const float* color_infill() { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish
+ static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
+
+ // For cloring by a tool, return a parsed color.
+ bool color_by_tool() const { return tool_colors != nullptr; }
+ size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
+ const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
+ int volume_idx(int extruder, int feature) const
{
- if ((vol != nullptr) && vol->selected)
- {
- int object_id = vol->select_group_id / 1000000;
- // Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy.
- if ((object_id < 10000) && (object_id < objects_count))
- {
- int volume_id = 0;
- for (int i = 0; i < object_id; ++i)
- {
- const PrintObject* obj = m_print->objects[i];
- const ModelObject* model = obj->model_object();
- volume_id += model->instances.size();
+ return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(extruder - 1, 0)) : feature;
+ }
+ } ctxt;
+
+ ctxt.shifted_copies = &print_object._shifted_copies;
+
+ // order layers by print_z
+ ctxt.layers.reserve(print_object.layers.size() + print_object.support_layers.size());
+ for (const Layer *layer : print_object.layers)
+ ctxt.layers.push_back(layer);
+ for (const Layer *layer : print_object.support_layers)
+ ctxt.layers.push_back(layer);
+ std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; });
+
+ // Maximum size of an allocation block: 32MB / sizeof(float)
+ ctxt.has_perimeters = print_object.state.is_done(posPerimeters);
+ ctxt.has_infill = print_object.state.is_done(posInfill);
+ ctxt.has_support = print_object.state.is_done(posSupportMaterial);
+ ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start";
+
+ //FIXME Improve the heuristics for a grain size.
+ size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1));
+ tbb::spin_mutex new_volume_mutex;
+ auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
+ auto *volume = new GLVolume(color);
+ new_volume_mutex.lock();
+ m_volumes.volumes.emplace_back(volume);
+ new_volume_mutex.unlock();
+ return volume;
+ };
+ const size_t volumes_cnt_initial = m_volumes.volumes.size();
+ std::vector<GLVolumeCollection> volumes_per_thread(ctxt.layers.size());
+ tbb::parallel_for(
+ tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size),
+ [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
+ std::vector<GLVolume*> vols;
+ if (ctxt.color_by_tool()) {
+ for (size_t i = 0; i < ctxt.number_tools(); ++i)
+ vols.emplace_back(new_volume(ctxt.color_tool(i)));
+ }
+ else
+ vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) };
+ for (GLVolume *vol : vols)
+ vol->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+ for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
+ const Layer *layer = ctxt.layers[idx_layer];
+ for (size_t i = 0; i < vols.size(); ++i) {
+ GLVolume &vol = *vols[i];
+ if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) {
+ vol.print_zs.push_back(layer->print_z);
+ vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
+ vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
+ }
+ }
+ for (const Point &copy : *ctxt.shifted_copies) {
+ for (const LayerRegion *layerm : layer->regions) {
+ if (ctxt.has_perimeters)
+ _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy,
+ *vols[ctxt.volume_idx(layerm->region()->config.perimeter_extruder.value, 0)]);
+ if (ctxt.has_infill) {
+ for (const ExtrusionEntity *ee : layerm->fills.entities) {
+ // fill represents infill extrusions of a single island.
+ const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+ if (!fill->entities.empty())
+ _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy,
+ *vols[ctxt.volume_idx(
+ is_solid_infill(fill->entities.front()->role()) ?
+ layerm->region()->config.solid_infill_extruder :
+ layerm->region()->config.infill_extruder,
+ 1)]);
+ }
+ }
+ }
+ if (ctxt.has_support) {
+ const SupportLayer *support_layer = dynamic_cast<const SupportLayer*>(layer);
+ if (support_layer) {
+ for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
+ _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy,
+ *vols[ctxt.volume_idx(
+ (extrusion_entity->role() == erSupportMaterial) ?
+ support_layer->object()->config.support_material_extruder :
+ support_layer->object()->config.support_material_interface_extruder,
+ 2)]);
+ }
+ }
+ }
+ for (size_t i = 0; i < vols.size(); ++i) {
+ GLVolume &vol = *vols[i];
+ if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
+ // Store the vertex arrays and restart their containers,
+ vols[i] = new_volume(vol.color);
+ GLVolume &vol_new = *vols[i];
+ // Assign the large pre-allocated buffers to the new GLVolume.
+ vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
+ // Copy the content back to the old GLVolume.
+ vol.indexed_vertex_array = vol_new.indexed_vertex_array;
+ // Finalize a bounding box of the old GLVolume.
+ vol.bounding_box = vol.indexed_vertex_array.bounding_box();
+ // Clear the buffers, but keep them pre-allocated.
+ vol_new.indexed_vertex_array.clear();
+ // Just make sure that clear did not clear the reserved memory.
+ vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+ }
+ }
+ }
+ for (GLVolume *vol : vols) {
+ vol->bounding_box = vol->indexed_vertex_array.bounding_box();
+ vol->indexed_vertex_array.shrink_to_fit();
+ }
+ });
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results";
+ // Remove empty volumes from the newly added volumes.
+ m_volumes.volumes.erase(
+ std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
+ [](const GLVolume *volume) { return volume->empty(); }),
+ m_volumes.volumes.end());
+ for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
+ m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end";
+}
+
+void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors)
+{
+ if ((m_print == nullptr) || m_print->m_wipe_tower_tool_changes.empty())
+ return;
+
+ if (!m_print->state.is_done(psWipeTower))
+ return;
+
+ std::vector<float> tool_colors = _parse_colors(str_tool_colors);
+
+ struct Ctxt
+ {
+ const Print *print;
+ const std::vector<float> *tool_colors;
+
+ // Number of vertices (each vertex is 6x4=24 bytes long)
+ static const size_t alloc_size_max() { return 131072; } // 3.15MB
+ static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
+
+ static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
+
+ // For cloring by a tool, return a parsed color.
+ bool color_by_tool() const { return tool_colors != nullptr; }
+ size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
+ const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
+ int volume_idx(int tool, int feature) const
+ {
+ return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(tool, 0)) : feature;
+ }
+
+ const std::vector<WipeTower::ToolChangeResult>& tool_change(size_t idx) {
+ return priming.empty() ?
+ ((idx == print->m_wipe_tower_tool_changes.size()) ? final : print->m_wipe_tower_tool_changes[idx]) :
+ ((idx == 0) ? priming : (idx == print->m_wipe_tower_tool_changes.size() + 1) ? final : print->m_wipe_tower_tool_changes[idx - 1]);
+ }
+ std::vector<WipeTower::ToolChangeResult> priming;
+ std::vector<WipeTower::ToolChangeResult> final;
+ } ctxt;
+
+ ctxt.print = m_print;
+ ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
+ if (m_print->m_wipe_tower_priming)
+ ctxt.priming.emplace_back(*m_print->m_wipe_tower_priming.get());
+ if (m_print->m_wipe_tower_final_purge)
+ ctxt.final.emplace_back(*m_print->m_wipe_tower_final_purge.get());
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
+
+ //FIXME Improve the heuristics for a grain size.
+ size_t n_items = m_print->m_wipe_tower_tool_changes.size() + (ctxt.priming.empty() ? 0 : 1);
+ size_t grain_size = std::max(n_items / 128, size_t(1));
+ tbb::spin_mutex new_volume_mutex;
+ auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
+ auto *volume = new GLVolume(color);
+ new_volume_mutex.lock();
+ m_volumes.volumes.emplace_back(volume);
+ new_volume_mutex.unlock();
+ return volume;
+ };
+ const size_t volumes_cnt_initial = m_volumes.volumes.size();
+ std::vector<GLVolumeCollection> volumes_per_thread(n_items);
+ tbb::parallel_for(
+ tbb::blocked_range<size_t>(0, n_items, grain_size),
+ [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
+ // Bounding box of this slab of a wipe tower.
+ std::vector<GLVolume*> vols;
+ if (ctxt.color_by_tool()) {
+ for (size_t i = 0; i < ctxt.number_tools(); ++i)
+ vols.emplace_back(new_volume(ctxt.color_tool(i)));
+ }
+ else
+ vols = { new_volume(ctxt.color_support()) };
+ for (GLVolume *volume : vols)
+ volume->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+ for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
+ const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.tool_change(idx_layer);
+ for (size_t i = 0; i < vols.size(); ++i) {
+ GLVolume &vol = *vols[i];
+ if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) {
+ vol.print_zs.push_back(layer.front().print_z);
+ vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
+ vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
+ }
+ }
+ for (const WipeTower::ToolChangeResult &extrusions : layer) {
+ for (size_t i = 1; i < extrusions.extrusions.size();) {
+ const WipeTower::Extrusion &e = extrusions.extrusions[i];
+ if (e.width == 0.) {
+ ++i;
+ continue;
}
- return volume_id;
+ size_t j = i + 1;
+ if (ctxt.color_by_tool())
+ for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.f; ++j);
+ else
+ for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.f; ++j);
+ size_t n_lines = j - i;
+ Lines lines;
+ std::vector<double> widths;
+ std::vector<double> heights;
+ lines.reserve(n_lines);
+ widths.reserve(n_lines);
+ heights.assign(n_lines, extrusions.layer_height);
+ for (; i < j; ++i) {
+ const WipeTower::Extrusion &e = extrusions.extrusions[i];
+ assert(e.width > 0.f);
+ const WipeTower::Extrusion &e_prev = *(&e - 1);
+ lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y));
+ widths.emplace_back(e.width);
+ }
+ _3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z,
+ *vols[ctxt.volume_idx(e.tool, 0)]);
}
}
}
- }
- return -1;
+ for (size_t i = 0; i < vols.size(); ++i) {
+ GLVolume &vol = *vols[i];
+ if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
+ // Store the vertex arrays and restart their containers,
+ vols[i] = new_volume(vol.color);
+ GLVolume &vol_new = *vols[i];
+ // Assign the large pre-allocated buffers to the new GLVolume.
+ vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
+ // Copy the content back to the old GLVolume.
+ vol.indexed_vertex_array = vol_new.indexed_vertex_array;
+ // Finalize a bounding box of the old GLVolume.
+ vol.bounding_box = vol.indexed_vertex_array.bounding_box();
+ // Clear the buffers, but keep them pre-allocated.
+ vol_new.indexed_vertex_array.clear();
+ // Just make sure that clear did not clear the reserved memory.
+ vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
+ }
+ }
+ for (GLVolume *vol : vols) {
+ vol->bounding_box = vol->indexed_vertex_array.bounding_box();
+ vol->indexed_vertex_array.shrink_to_fit();
+ }
+ });
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results";
+ // Remove empty volumes from the newly added volumes.
+ m_volumes.volumes.erase(
+ std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
+ [](const GLVolume *volume) { return volume->empty(); }),
+ m_volumes.volumes.end());
+ for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
+ m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
+
+ BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end";
}
static inline int hex_digit_to_int(const char c)
@@ -4230,6 +4302,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
if (volume != nullptr)
{
filter.volume = volume;
+ volume->is_extrusion_path = true;
m_volumes.volumes.emplace_back(volume);
}
else
@@ -4643,7 +4716,6 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe
for (std::vector<GLVolume*>::iterator it = begin; it != end; ++it)
{
GLVolume* volume = *it;
- volume->outside_printer_detection_enabled = false;
switch (m_gcode_preview_volume_index.first_volumes[i].type)
{
@@ -4691,6 +4763,45 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe
}
}
+void GLCanvas3D::_update_toolpath_volumes_outside_state()
+{
+ // tolerance to avoid false detection at bed edges
+ static const coordf_t tolerance_x = 0.05;
+ static const coordf_t tolerance_y = 0.05;
+
+ BoundingBoxf3 print_volume;
+ if (m_config != nullptr)
+ {
+ const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(m_config->option("bed_shape"));
+ if (opt != nullptr)
+ {
+ BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
+ print_volume = BoundingBoxf3(Pointf3(unscale(bed_box_2D.min.x) - tolerance_x, unscale(bed_box_2D.min.y) - tolerance_y, 0.0), Pointf3(unscale(bed_box_2D.max.x) + tolerance_x, unscale(bed_box_2D.max.y) + tolerance_y, m_config->opt_float("max_print_height")));
+ // Allow the objects to protrude below the print bed
+ print_volume.min.z = -1e10;
+ }
+ }
+
+ for (GLVolume* volume : m_volumes.volumes)
+ {
+ volume->is_outside = ((print_volume.radius() > 0.0) && volume->is_extrusion_path) ? !print_volume.contains(volume->bounding_box) : false;
+ }
+}
+
+void GLCanvas3D::_show_warning_texture_if_needed()
+{
+ if (_is_any_volume_outside())
+ {
+ enable_warning_texture(true);
+ _generate_warning_texture(L("Detected toolpath outside print volume"));
+ }
+ else
+ {
+ enable_warning_texture(false);
+ _reset_warning_texture();
+ }
+}
+
void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
{
if (m_model == nullptr)
@@ -4796,5 +4907,16 @@ void GLCanvas3D::_reset_warning_texture()
m_warning_texture.reset();
}
+bool GLCanvas3D::_is_any_volume_outside() const
+{
+ for (const GLVolume* volume : m_volumes.volumes)
+ {
+ if ((volume != nullptr) && volume->is_outside)
+ return true;
+ }
+
+ return false;
+}
+
} // namespace GUI
} // namespace Slic3r
diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp
index a9a6117ec..a87486261 100644
--- a/xs/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp
@@ -449,6 +449,7 @@ private:
bool m_picking_enabled;
bool m_moving_enabled;
bool m_shader_enabled;
+ bool m_dynamic_background_enabled;
bool m_multisample_allowed;
std::string m_color_by;
@@ -539,6 +540,7 @@ public:
void enable_gizmos(bool enable);
void enable_shader(bool enable);
void enable_force_zoom_to_bed(bool enable);
+ void enable_dynamic_background(bool enable);
void allow_multisample(bool allow);
void zoom_to_bed();
@@ -559,16 +561,8 @@ public:
void reload_scene(bool force);
- // Create 3D thick extrusion lines for a skirt and brim.
- // Adds a new Slic3r::GUI::3DScene::Volume to volumes.
- void load_print_toolpaths();
- // Create 3D thick extrusion lines for object forming extrusions.
- // Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
- // one for perimeters, one for infill and one for supports.
- void load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors);
- // Create 3D thick extrusion lines for wipe tower extrusions
- void load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors);
void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors);
+ void load_preview(const std::vector<std::string>& str_tool_colors);
void register_on_viewport_changed_callback(void* callback);
void register_on_double_click_callback(void* callback);
@@ -650,7 +644,17 @@ private:
void _stop_timer();
int _get_first_selected_object_id() const;
- int _get_first_selected_volume_id() const;
+ int _get_first_selected_volume_id(int object_id) const;
+
+ // Create 3D thick extrusion lines for a skirt and brim.
+ // Adds a new Slic3r::GUI::3DScene::Volume to volumes.
+ void _load_print_toolpaths();
+ // Create 3D thick extrusion lines for object forming extrusions.
+ // Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
+ // one for perimeters, one for infill and one for supports.
+ void _load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors);
+ // Create 3D thick extrusion lines for wipe tower extrusions
+ void _load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors);
// generates gcode extrusion paths geometry
void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
@@ -667,6 +671,8 @@ private:
void _load_shells();
// sets gcode geometry visibility according to user selection
void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data);
+ void _update_toolpath_volumes_outside_state();
+ void _show_warning_texture_if_needed();
void _on_move(const std::vector<int>& volume_idxs);
void _on_select(int volume_idx);
@@ -678,6 +684,8 @@ private:
void _generate_warning_texture(const std::string& msg);
void _reset_warning_texture();
+ bool _is_any_volume_outside() const;
+
static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
};
diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
index 5e9048d54..5249f6dc4 100644
--- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
+++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
@@ -418,6 +418,13 @@ void GLCanvas3DManager::enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable
it->second->enable_force_zoom_to_bed(enable);
}
+void GLCanvas3DManager::enable_dynamic_background(wxGLCanvas* canvas, bool enable)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->enable_dynamic_background(enable);
+}
+
void GLCanvas3DManager::allow_multisample(wxGLCanvas* canvas, bool allow)
{
CanvasesMap::iterator it = _get_canvas(canvas);
@@ -516,38 +523,21 @@ void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force)
it->second->reload_scene(force);
}
-void GLCanvas3DManager::load_print_toolpaths(wxGLCanvas* canvas)
-{
- CanvasesMap::iterator it = _get_canvas(canvas);
- if (it != m_canvases.end())
- it->second->load_print_toolpaths();
-}
-
-void GLCanvas3DManager::load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& tool_colors)
+void GLCanvas3DManager::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors)
{
- if (print_object == nullptr)
+ if (preview_data == nullptr)
return;
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
- it->second->load_print_object_toolpaths(*print_object, tool_colors);
+ it->second->load_gcode_preview(*preview_data, str_tool_colors);
}
-void GLCanvas3DManager::load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors)
+void GLCanvas3DManager::load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
- it->second->load_wipe_tower_toolpaths(str_tool_colors);
-}
-
-void GLCanvas3DManager::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors)
-{
- if (preview_data == nullptr)
- return;
-
- CanvasesMap::iterator it = _get_canvas(canvas);
- if (it != m_canvases.end())
- it->second->load_gcode_preview(*preview_data, str_tool_colors);
+ it->second->load_preview(str_tool_colors);
}
void GLCanvas3DManager::reset_legend_texture()
diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
index 32aa712d3..55c42fda6 100644
--- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
+++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
@@ -112,6 +112,7 @@ public:
void enable_gizmos(wxGLCanvas* canvas, bool enable);
void enable_shader(wxGLCanvas* canvas, bool enable);
void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable);
+ void enable_dynamic_background(wxGLCanvas* canvas, bool enable);
void allow_multisample(wxGLCanvas* canvas, bool allow);
void zoom_to_bed(wxGLCanvas* canvas);
@@ -132,10 +133,8 @@ public:
void reload_scene(wxGLCanvas* canvas, bool force);
- void load_print_toolpaths(wxGLCanvas* canvas);
- void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& tool_colors);
- void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors);
+ void load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
void reset_legend_texture();
diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index af7022f2b..9c62fe8eb 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -250,6 +250,7 @@ bool select_language(wxArrayString & names,
g_wxLocale->AddCatalogLookupPathPrefix(wxPathOnly(localization_dir()));
g_wxLocale->AddCatalog(g_wxApp->GetAppName());
wxSetlocale(LC_NUMERIC, "C");
+ Preset::update_suffix_modified();
return true;
}
return false;
@@ -275,6 +276,7 @@ bool load_language()
g_wxLocale->AddCatalogLookupPathPrefix(wxPathOnly(localization_dir()));
g_wxLocale->AddCatalog(g_wxApp->GetAppName());
wxSetlocale(LC_NUMERIC, "C");
+ Preset::update_suffix_modified();
return true;
}
}
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index 54a5c90fe..49e235146 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -146,6 +146,11 @@ const std::string& Preset::suffix_modified()
{
return g_suffix_modified;
}
+
+void Preset::update_suffix_modified()
+{
+ g_suffix_modified = (" (" + _(L("modified")) + ")").ToUTF8().data();
+}
// Remove an optional "(modified)" suffix from a name.
// This converts a UI name to a unique preset identifier.
std::string Preset::remove_suffix_modified(const std::string &name)
diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp
index a2ee1d2eb..0d00cae48 100644
--- a/xs/src/slic3r/GUI/Preset.hpp
+++ b/xs/src/slic3r/GUI/Preset.hpp
@@ -167,6 +167,7 @@ public:
static const std::vector<std::string>& printer_options();
// Nozzle options of the printer options.
static const std::vector<std::string>& nozzle_options();
+ static void update_suffix_modified();
protected:
friend class PresetCollection;
@@ -260,7 +261,7 @@ public:
// used to update preset_choice from Tab
const std::deque<Preset>& get_presets() { return m_presets; }
int get_idx_selected() { return m_idx_selected; }
- const std::string& get_suffix_modified();
+ static const std::string& get_suffix_modified();
// Return a preset possibly with modifications.
Preset& default_preset() { return m_presets.front(); }
diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp
index 58553e1bc..94baa0e0e 100644
--- a/xs/src/slic3r/GUI/PresetBundle.cpp
+++ b/xs/src/slic3r/GUI/PresetBundle.cpp
@@ -552,6 +552,8 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
std::string &inherits = Preset::inherits(config);
compatible_printers_condition_values.resize(num_extruders + 2, std::string());
inherits_values.resize(num_extruders + 2, std::string());
+ // The "default_filament_profile" will be later extracted into the printer profile.
+ config.option<ConfigOptionStrings>("default_filament_profile", true)->values.resize(num_extruders, std::string());
// 1) Create a name from the file name.
// Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles.
@@ -576,7 +578,6 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
// 3) Now load the filaments. If there are multiple filament presets, split them and load them.
auto old_filament_profile_names = config.option<ConfigOptionStrings>("filament_settings_id", true);
old_filament_profile_names->values.resize(num_extruders, std::string());
- config.option<ConfigOptionStrings>("default_filament_profile", true)->values.resize(num_extruders, std::string());
if (num_extruders <= 1) {
// Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets.
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 5d1c51eb3..187030f31 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -2728,7 +2728,7 @@ void SavePresetWindow::accept()
const char* unusable_symbols = "<>:/\\|?*\"";
bool is_unusable_symbol = false;
bool is_unusable_postfix = false;
- const std::string unusable_postfix = "(modified)";
+ const std::string unusable_postfix = PresetCollection::get_suffix_modified();//"(modified)";
for (size_t i = 0; i < std::strlen(unusable_symbols); i++){
if (m_chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos){
is_unusable_symbol = true;
@@ -2743,8 +2743,9 @@ void SavePresetWindow::accept()
_(L("the following characters are not allowed:")) + " <>:/\\|?*\"");
}
else if (is_unusable_postfix){
- show_error(this, _(L("The supplied name is not valid;")) + "\n" +
- _(L("the following postfix are not allowed:")) + "\n\t" + unusable_postfix);
+ show_error(this,_(L("The supplied name is not valid;")) + "\n" +
+ _(L("the following postfix are not allowed:")) + "\n\t" + //unusable_postfix);
+ wxString::FromUTF8(unusable_postfix.c_str()));
}
else if (m_chosen_name.compare("- default -") == 0) {
show_error(this, _(L("The supplied name is not available.")));
diff --git a/xs/src/slic3r/Utils/HexFile.cpp b/xs/src/slic3r/Utils/HexFile.cpp
new file mode 100644
index 000000000..ed26ddf37
--- /dev/null
+++ b/xs/src/slic3r/Utils/HexFile.cpp
@@ -0,0 +1,107 @@
+#include "HexFile.hpp"
+
+#include <sstream>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/ini_parser.hpp>
+
+namespace fs = boost::filesystem;
+namespace pt = boost::property_tree;
+
+
+namespace Slic3r {
+namespace Utils {
+
+
+static HexFile::DeviceKind parse_device_kind(const std::string &str)
+{
+ if (str == "mk2") { return HexFile::DEV_MK2; }
+ else if (str == "mk3") { return HexFile::DEV_MK3; }
+ else if (str == "mm-control") { return HexFile::DEV_MM_CONTROL; }
+ else { return HexFile::DEV_GENERIC; }
+}
+
+static size_t hex_num_sections(fs::ifstream &file)
+{
+ file.seekg(0);
+ if (! file.good()) {
+ return 0;
+ }
+
+ static const char *hex_terminator = ":00000001FF\r";
+ size_t res = 0;
+ std::string line;
+ while (getline(file, line, '\n').good()) {
+ // Account for LF vs CRLF
+ if (!line.empty() && line.back() != '\r') {
+ line.push_back('\r');
+ }
+
+ if (line == hex_terminator) {
+ res++;
+ }
+ }
+
+ return res;
+}
+
+HexFile::HexFile(fs::path path) :
+ path(std::move(path)),
+ device(DEV_GENERIC)
+{
+ fs::ifstream file(this->path);
+ if (! file.good()) {
+ return;
+ }
+
+ std::string line;
+ std::stringstream header_ini;
+ while (std::getline(file, line, '\n').good()) {
+ if (line.empty()) {
+ continue;
+ }
+
+ // Account for LF vs CRLF
+ if (!line.empty() && line.back() == '\r') {
+ line.pop_back();
+ }
+
+ if (line.front() == ';') {
+ line.front() = ' ';
+ header_ini << line << std::endl;
+ } else if (line.front() == ':') {
+ break;
+ }
+ }
+
+ pt::ptree ptree;
+ try {
+ pt::read_ini(header_ini, ptree);
+ } catch (std::exception &e) {
+ return;
+ }
+
+ bool has_device_meta = false;
+ const auto device = ptree.find("device");
+ if (device != ptree.not_found()) {
+ this->device = parse_device_kind(device->second.data());
+ has_device_meta = true;
+ }
+
+ const auto model_id = ptree.find("model_id");
+ if (model_id != ptree.not_found()) {
+ this->model_id = model_id->second.data();
+ }
+
+ if (! has_device_meta) {
+ // No device metadata, look at the number of 'sections'
+ if (hex_num_sections(file) == 2) {
+ // Looks like a pre-metadata l10n firmware for the MK3, assume that's the case
+ this->device = DEV_MK3;
+ }
+ }
+}
+
+
+}
+}
diff --git a/xs/src/slic3r/Utils/HexFile.hpp b/xs/src/slic3r/Utils/HexFile.hpp
new file mode 100644
index 000000000..d8d1e09ab
--- /dev/null
+++ b/xs/src/slic3r/Utils/HexFile.hpp
@@ -0,0 +1,32 @@
+#ifndef slic3r_Hex_hpp_
+#define slic3r_Hex_hpp_
+
+#include <string>
+#include <boost/filesystem/path.hpp>
+
+
+namespace Slic3r {
+namespace Utils {
+
+
+struct HexFile
+{
+ enum DeviceKind {
+ DEV_GENERIC,
+ DEV_MK2,
+ DEV_MK3,
+ DEV_MM_CONTROL,
+ };
+
+ boost::filesystem::path path;
+ DeviceKind device;
+ std::string model_id;
+
+ HexFile(boost::filesystem::path path);
+};
+
+
+}
+}
+
+#endif
diff --git a/xs/src/slic3r/Utils/Serial.cpp b/xs/src/slic3r/Utils/Serial.cpp
index 32b6bb160..c3c16b314 100644
--- a/xs/src/slic3r/Utils/Serial.cpp
+++ b/xs/src/slic3r/Utils/Serial.cpp
@@ -3,17 +3,22 @@
#include <algorithm>
#include <string>
#include <vector>
+#include <chrono>
+#include <thread>
#include <fstream>
+#include <stdexcept>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
+#include <boost/optional.hpp>
#if _WIN32
#include <Windows.h>
#include <Setupapi.h>
#include <initguid.h>
#include <devguid.h>
+ #include <regex>
// Undefine min/max macros incompatible with the standard library
// For example, std::numeric_limits<std::streamsize>::max()
// produces some weird errors
@@ -34,6 +39,23 @@
#include <sys/syslimits.h>
#endif
+#ifndef _WIN32
+ #include <sys/ioctl.h>
+ #include <sys/time.h>
+ #include <sys/unistd.h>
+ #include <sys/select.h>
+#endif
+
+#if defined(__APPLE__) || defined(__OpenBSD__)
+ #include <termios.h>
+#elif defined __linux__
+ #include <fcntl.h>
+ #include <asm-generic/ioctls.h>
+#endif
+
+using boost::optional;
+
+
namespace Slic3r {
namespace Utils {
@@ -42,15 +64,43 @@ static bool looks_like_printer(const std::string &friendly_name)
return friendly_name.find("Original Prusa") != std::string::npos;
}
+#if _WIN32
+void parse_hardware_id(const std::string &hardware_id, SerialPortInfo &spi)
+{
+ unsigned vid, pid;
+ std::regex pattern("USB\\\\.*VID_([[:xdigit:]]+)&PID_([[:xdigit:]]+).*");
+ std::smatch matches;
+ if (std::regex_match(hardware_id, matches, pattern)) {
+ try {
+ vid = std::stoul(matches[1].str(), 0, 16);
+ pid = std::stoul(matches[2].str(), 0, 16);
+ spi.id_vendor = vid;
+ spi.id_product = pid;
+ }
+ catch (...) {}
+ }
+}
+#endif
+
#ifdef __linux__
-static std::string get_tty_friendly_name(const std::string &path, const std::string &name)
+optional<std::string> sysfs_tty_prop(const std::string &tty_dev, const std::string &name)
{
- const auto sysfs_product = (boost::format("/sys/class/tty/%1%/device/../product") % name).str();
- std::ifstream file(sysfs_product);
- std::string product;
+ const auto prop_path = (boost::format("/sys/class/tty/%1%/device/../%2%") % tty_dev % name).str();
+ std::ifstream file(prop_path);
+ std::string res;
- std::getline(file, product);
- return file.good() ? (boost::format("%1% (%2%)") % product % path).str() : path;
+ std::getline(file, res);
+ if (file.good()) { return res; }
+ else { return boost::none; }
+}
+
+optional<unsigned long> sysfs_tty_prop_hex(const std::string &tty_dev, const std::string &name)
+{
+ auto prop = sysfs_tty_prop(tty_dev, name);
+ if (!prop) { return boost::none; }
+
+ try { return std::stoul(*prop, 0, 16); }
+ catch (...) { return boost::none; }
}
#endif
@@ -80,6 +130,7 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
if (port_info.port.empty())
continue;
}
+
// Find the size required to hold the device info.
DWORD regDataType;
DWORD reqSize = 0;
@@ -88,7 +139,8 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
// Now store it in a buffer.
if (! SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_HARDWAREID, &regDataType, (BYTE*)hardware_id.data(), reqSize, nullptr))
continue;
- port_info.hardware_id = boost::nowide::narrow(hardware_id.data());
+ parse_hardware_id(boost::nowide::narrow(hardware_id.data()), port_info);
+
// Find the size required to hold the friendly name.
reqSize = 0;
SetupDiGetDeviceRegistryProperty(hDeviceInfo, &devInfoData, SPDRP_FRIENDLYNAME, nullptr, nullptr, 0, &reqSize);
@@ -120,6 +172,8 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
if (result) {
SerialPortInfo port_info;
port_info.port = path;
+
+ // Attempt to read out the device friendly name
if ((cf_property = IORegistryEntrySearchCFProperty(port, kIOServicePlane,
CFSTR("USB Interface Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
@@ -141,6 +195,23 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
}
if (port_info.friendly_name.empty())
port_info.friendly_name = port_info.port;
+
+ // Attempt to read out the VID & PID
+ int vid, pid;
+ auto cf_vendor = IORegistryEntrySearchCFProperty(port, kIOServicePlane, CFSTR("idVendor"),
+ kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents);
+ auto cf_product = IORegistryEntrySearchCFProperty(port, kIOServicePlane, CFSTR("idProduct"),
+ kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents);
+ if (cf_vendor && cf_product) {
+ if (CFNumberGetValue((CFNumberRef)cf_vendor, kCFNumberIntType, &vid) &&
+ CFNumberGetValue((CFNumberRef)cf_product, kCFNumberIntType, &pid)) {
+ port_info.id_vendor = vid;
+ port_info.id_product = pid;
+ }
+ }
+ if (cf_vendor) { CFRelease(cf_vendor); }
+ if (cf_product) { CFRelease(cf_product); }
+
output.emplace_back(std::move(port_info));
}
}
@@ -158,9 +229,15 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
const auto path = dir_entry.path().string();
SerialPortInfo spi;
spi.port = path;
- spi.hardware_id = path;
#ifdef __linux__
- spi.friendly_name = get_tty_friendly_name(path, name);
+ auto friendly_name = sysfs_tty_prop(name, "product");
+ spi.friendly_name = friendly_name ? (boost::format("%1% (%2%)") % *friendly_name % path).str() : path;
+ auto vid = sysfs_tty_prop_hex(name, "idVendor");
+ auto pid = sysfs_tty_prop_hex(name, "idProduct");
+ if (vid && pid) {
+ spi.id_vendor = *vid;
+ spi.id_product = *pid;
+ }
#else
spi.friendly_name = path;
#endif
@@ -189,5 +266,224 @@ std::vector<std::string> scan_serial_ports()
return output;
}
+
+
+// Class Serial
+
+namespace asio = boost::asio;
+using boost::system::error_code;
+
+Serial::Serial(asio::io_service& io_service) :
+ asio::serial_port(io_service)
+{}
+
+Serial::Serial(asio::io_service& io_service, const std::string &name, unsigned baud_rate) :
+ asio::serial_port(io_service, name)
+{
+ set_baud_rate(baud_rate);
+}
+
+Serial::~Serial() {}
+
+void Serial::set_baud_rate(unsigned baud_rate)
+{
+ try {
+ // This does not support speeds > 115200
+ set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
+ } catch (boost::system::system_error &) {
+ auto handle = native_handle();
+
+ auto handle_errno = [](int retval) {
+ if (retval != 0) {
+ throw std::runtime_error(
+ (boost::format("Could not set baud rate: %1%") % strerror(errno)).str()
+ );
+ }
+ };
+
+#if __APPLE__
+ termios ios;
+ handle_errno(::tcgetattr(handle, &ios));
+ handle_errno(::cfsetspeed(&ios, baud_rate));
+ speed_t newSpeed = baud_rate;
+ handle_errno(::ioctl(handle, IOSSIOSPEED, &newSpeed));
+ handle_errno(::tcsetattr(handle, TCSANOW, &ios));
+#elif __linux
+
+ /* 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
+
+ termios2 ios;
+ handle_errno(::ioctl(handle, TCGETS2, &ios));
+ 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;
+ handle_errno(::ioctl(handle, TCSETS2, &ios));
+
+#elif __OpenBSD__
+ struct termios ios;
+ handle_errno(::tcgetattr(handle, &ios));
+ handle_errno(::cfsetspeed(&ios, baud_rate));
+ handle_errno(::tcsetattr(handle, TCSAFLUSH, &ios));
+#else
+ throw std::runtime_error("Custom baud rates are not currently supported on this OS");
+#endif
+ }
+}
+
+void Serial::set_DTR(bool on)
+{
+ auto handle = native_handle();
+#if defined(_WIN32) && !defined(__SYMBIAN32__)
+ if (! EscapeCommFunction(handle, on ? SETDTR : CLRDTR)) {
+ throw std::runtime_error("Could not set serial port DTR");
+ }
+#else
+ int status;
+ if (::ioctl(handle, TIOCMGET, &status) == 0) {
+ on ? status |= TIOCM_DTR : status &= ~TIOCM_DTR;
+ if (::ioctl(handle, TIOCMSET, &status) == 0) {
+ return;
+ }
+ }
+
+ throw std::runtime_error(
+ (boost::format("Could not set serial port DTR: %1%") % strerror(errno)).str()
+ );
+#endif
+}
+
+void Serial::reset_line_num()
+{
+ // See https://github.com/MarlinFirmware/Marlin/wiki/M110
+ printer_write_line("M110 N0", 0);
+ m_line_num = 0;
+}
+
+bool Serial::read_line(unsigned timeout, std::string &line, error_code &ec)
+{
+ auto &io_service = get_io_service();
+ asio::deadline_timer timer(io_service);
+ char c = 0;
+ bool fail = false;
+
+ while (true) {
+ io_service.reset();
+
+ asio::async_read(*this, boost::asio::buffer(&c, 1), [&](const error_code &read_ec, size_t size) {
+ if (ec || size == 0) {
+ fail = true;
+ ec = read_ec;
+ }
+ timer.cancel();
+ });
+
+ if (timeout > 0) {
+ timer.expires_from_now(boost::posix_time::milliseconds(timeout));
+ timer.async_wait([&](const error_code &ec) {
+ // Ignore timer aborts
+ if (!ec) {
+ fail = true;
+ this->cancel();
+ }
+ });
+ }
+
+ io_service.run();
+
+ if (fail) {
+ return false;
+ } else if (c != '\n') {
+ line += c;
+ } else {
+ return true;
+ }
+ }
+}
+
+void Serial::printer_setup()
+{
+ printer_reset();
+ write_string("\r\r\r\r\r\r\r\r\r\r"); // Gets rid of line noise, if any
+}
+
+size_t Serial::write_string(const std::string &str)
+{
+ // TODO: might be wise to timeout here as well
+ return asio::write(*this, asio::buffer(str));
+}
+
+bool Serial::printer_ready_wait(unsigned retries, unsigned timeout)
+{
+ std::string line;
+ error_code ec;
+
+ for (; retries > 0; retries--) {
+ reset_line_num();
+
+ while (read_line(timeout, line, ec)) {
+ if (line == "ok") {
+ return true;
+ }
+ line.clear();
+ }
+ line.clear();
+ }
+
+ return false;
+}
+
+size_t Serial::printer_write_line(const std::string &line, unsigned line_num)
+{
+ const auto formatted_line = Utils::Serial::printer_format_line(line, line_num);
+ return write_string(formatted_line);
+}
+
+size_t Serial::printer_write_line(const std::string &line)
+{
+ m_line_num++;
+ return printer_write_line(line, m_line_num);
+}
+
+void Serial::printer_reset()
+{
+ this->set_DTR(false);
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+ this->set_DTR(true);
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+ this->set_DTR(false);
+ std::this_thread::sleep_for(std::chrono::milliseconds(2000));
+}
+
+std::string Serial::printer_format_line(const std::string &line, unsigned line_num)
+{
+ const auto line_num_str = std::to_string(line_num);
+
+ unsigned checksum = 'N';
+ for (auto c : line_num_str) { checksum ^= c; }
+ checksum ^= ' ';
+ for (auto c : line) { checksum ^= c; }
+
+ return (boost::format("N%1% %2%*%3%\n") % line_num_str % line % checksum).str();
+}
+
+
} // namespace Utils
} // namespace Slic3r
diff --git a/xs/src/slic3r/Utils/Serial.hpp b/xs/src/slic3r/Utils/Serial.hpp
index 9381ab398..d15f249c0 100644
--- a/xs/src/slic3r/Utils/Serial.hpp
+++ b/xs/src/slic3r/Utils/Serial.hpp
@@ -1,31 +1,80 @@
#ifndef slic3r_GUI_Utils_Serial_hpp_
#define slic3r_GUI_Utils_Serial_hpp_
-#include <memory>
#include <vector>
#include <string>
-#include <vector>
+#include <boost/system/error_code.hpp>
+#include <boost/asio.hpp>
+
namespace Slic3r {
namespace Utils {
struct SerialPortInfo {
std::string port;
- std::string hardware_id;
+ unsigned id_vendor = -1;
+ unsigned id_product = -1;
std::string friendly_name;
- bool is_printer = false;
+ bool is_printer = false;
+
+ bool id_match(unsigned id_vendor, unsigned id_product) const { return id_vendor == this->id_vendor && id_product == this->id_product; }
};
-inline bool operator==(const SerialPortInfo &sp1, const SerialPortInfo &sp2)
+inline bool operator==(const SerialPortInfo &sp1, const SerialPortInfo &sp2)
{
- return sp1.port == sp2.port &&
- sp1.hardware_id == sp2.hardware_id &&
- sp1.is_printer == sp2.is_printer;
+ return
+ sp1.port == sp2.port &&
+ sp1.id_vendor == sp2.id_vendor &&
+ sp1.id_product == sp2.id_product &&
+ sp1.is_printer == sp2.is_printer;
}
extern std::vector<std::string> scan_serial_ports();
extern std::vector<SerialPortInfo> scan_serial_ports_extended();
+
+class Serial : public boost::asio::serial_port
+{
+public:
+ Serial(boost::asio::io_service &io_service);
+ Serial(boost::asio::io_service &io_service, const std::string &name, unsigned baud_rate);
+ Serial(const Serial &) = delete;
+ Serial &operator=(const Serial &) = delete;
+ ~Serial();
+
+ void set_baud_rate(unsigned baud_rate);
+ void set_DTR(bool on);
+
+ // Resets the line number both internally as well as with the firmware using M110
+ void reset_line_num();
+
+ // Reads a line or times out, the timeout is in milliseconds
+ bool read_line(unsigned timeout, std::string &line, boost::system::error_code &ec);
+
+ // Perform setup for communicating with a printer
+ void printer_setup();
+
+ // Write data from a string
+ size_t write_string(const std::string &str);
+
+ bool printer_ready_wait(unsigned retries, unsigned timeout);
+
+ // Write Marlin-formatted line, with a line number and a checksum
+ size_t printer_write_line(const std::string &line, unsigned line_num);
+
+ // Same as above, but with internally-managed line number
+ size_t printer_write_line(const std::string &line);
+
+ // Toggles DTR to reset the printer
+ void printer_reset();
+
+ // Formats a line Marlin-style, ie. with a sequential number and a checksum
+ static std::string printer_format_line(const std::string &line, unsigned line_num);
+private:
+ unsigned m_line_num = 0;
+};
+
+
} // Utils
} // Slic3r
diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp
index 5c2f7df85..a91f32d29 100644
--- a/xs/xsp/GUI_3DScene.xsp
+++ b/xs/xsp/GUI_3DScene.xsp
@@ -431,6 +431,13 @@ enable_force_zoom_to_bed(canvas, enable)
_3DScene::enable_force_zoom_to_bed((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable);
void
+enable_dynamic_background(canvas, enable)
+ SV *canvas;
+ bool enable;
+ CODE:
+ _3DScene::enable_dynamic_background((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), enable);
+
+void
allow_multisample(canvas, allow)
SV *canvas;
bool allow;
@@ -657,33 +664,19 @@ reload_scene(canvas, force)
CODE:
_3DScene::reload_scene((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), force);
-void
-load_print_toolpaths(canvas)
- SV *canvas;
- CODE:
- _3DScene::load_print_toolpaths((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
-
void
-load_print_object_toolpaths(canvas, print_object, tool_colors)
+load_gcode_preview(canvas, preview_data, str_tool_colors)
SV *canvas;
- PrintObject *print_object;
- std::vector<std::string> tool_colors;
+ GCodePreviewData *preview_data;
+ std::vector<std::string> str_tool_colors;
CODE:
- _3DScene::load_print_object_toolpaths((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), print_object, tool_colors);
+ _3DScene::load_gcode_preview((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), preview_data, str_tool_colors);
void
-load_wipe_tower_toolpaths(canvas, tool_colors)
+load_preview(canvas, str_tool_colors)
SV *canvas;
- std::vector<std::string> tool_colors;
- CODE:
- _3DScene::load_wipe_tower_toolpaths((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), tool_colors);
-
-void
-load_gcode_preview(canvas, preview_data, str_tool_colors)
- SV *canvas;
- GCodePreviewData *preview_data;
std::vector<std::string> str_tool_colors;
CODE:
- _3DScene::load_gcode_preview((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), preview_data, str_tool_colors);
-
+ _3DScene::load_preview((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), str_tool_colors);
+
%}