Merge branch 'master' into tm_colldetection_upgr

This commit is contained in:
tamasmeszaros 2019-03-07 12:02:28 +01:00
commit a7c07960ee
116 changed files with 5089 additions and 16124 deletions

View file

@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_definitions(-D_BSD_SOURCE -D_DEFAULT_SOURCE) # To enable various useful macros and functions on Unices
remove_definitions(-D_UNICODE -DUNICODE)
@ -68,18 +70,31 @@ set(AVRDUDE_SOURCES
)
if (MSVC)
set(AVRDUDE_SOURCES ${AVRDUDE_SOURCES}
windows/utf8.c
windows/unistd.cpp
windows/getopt.c
)
endif()
add_library(avrdude STATIC ${AVRDUDE_SOURCES})
set(STANDALONE_SOURCES
main-standalone.c
add_executable(avrdude-conf-gen conf-generate.cpp)
# Config file embedding
add_custom_command(
DEPENDS avrdude-conf-gen ${CMAKE_CURRENT_SOURCE_DIR}/avrdude-slic3r.conf
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/avrdude-slic3r.conf.h
COMMAND $<TARGET_FILE:avrdude-conf-gen> avrdude-slic3r.conf avrdude_slic3r_conf > avrdude-slic3r.conf.h
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_executable(avrdude-slic3r ${STANDALONE_SOURCES})
add_custom_target(gen_conf_h
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/avrdude-slic3r.conf.h
)
add_library(avrdude STATIC ${AVRDUDE_SOURCES})
add_dependencies(avrdude gen_conf_h)
add_executable(avrdude-slic3r main-standalone.cpp)
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)

View file

@ -158,8 +158,10 @@ static int arduino_open(PROGRAMMER * pgm, char * port)
return -1;
}
if (stk500_getsync(pgm) < 0)
if (stk500_getsync(pgm) < 0) {
serial_close(&pgm->fd);
return -1;
}
return 0;
}

View file

@ -0,0 +1,424 @@
#
# This is a basic minimal config file embedded into the avrdude-slic3r binary
# so that it can work in a standalone manner.
#
# Only the bits useful for Prusa3D devices were copied over from avrdude.conf
# If needed, more configuration can still be loaded into avrdude-slic3r
# via the -C command-line option.
#
programmer
id = "wiring";
desc = "Wiring";
type = "wiring";
connection_type = serial;
;
programmer
id = "arduino";
desc = "Arduino";
type = "arduino";
connection_type = serial;
;
programmer
id = "avr109";
desc = "Atmel AppNote AVR109 Boot Loader";
type = "butterfly";
connection_type = serial;
;
#------------------------------------------------------------
# ATmega2560
#------------------------------------------------------------
part
id = "m2560";
desc = "ATmega2560";
signature = 0x1e 0x98 0x01;
has_jtag = yes;
stk500_devcode = 0xB2;
# avr910_devcode = 0x43;
chip_erase_delay = 9000;
pagel = 0xD7;
bs2 = 0xA0;
reset = dedicated;
pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1",
"x x x x x x x x x x x x x x x x";
chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0",
"x x x x x x x x x x x x x x x x";
timeout = 200;
stabdelay = 100;
cmdexedelay = 25;
synchloops = 32;
bytedelay = 0;
pollindex = 3;
pollvalue = 0x53;
predelay = 1;
postdelay = 1;
pollmethod = 1;
pp_controlstack =
0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F,
0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F,
0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B,
0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02;
hventerstabdelay = 100;
progmodedelay = 0;
latchcycles = 5;
togglevtg = 1;
poweroffdelay = 15;
resetdelayms = 1;
resetdelayus = 0;
hvleavestabdelay = 15;
chiperasepulsewidth = 0;
chiperasepolltimeout = 10;
programfusepulsewidth = 0;
programfusepolltimeout = 5;
programlockpulsewidth = 0;
programlockpolltimeout = 5;
idr = 0x31;
spmcr = 0x57;
rampz = 0x3b;
allowfullpagebitstream = no;
ocdrev = 4;
memory "eeprom"
paged = no; /* leave this "no" */
page_size = 8; /* for parallel programming */
size = 4096;
min_write_delay = 9000;
max_write_delay = 9000;
readback_p1 = 0x00;
readback_p2 = 0x00;
read = " 1 0 1 0 0 0 0 0",
" x x x x a11 a10 a9 a8",
" a7 a6 a5 a4 a3 a2 a1 a0",
" o o o o o o o o";
write = " 1 1 0 0 0 0 0 0",
" x x x x a11 a10 a9 a8",
" a7 a6 a5 a4 a3 a2 a1 a0",
" i i i i i i i i";
loadpage_lo = " 1 1 0 0 0 0 0 1",
" 0 0 0 0 0 0 0 0",
" 0 0 0 0 0 a2 a1 a0",
" i i i i i i i i";
writepage = " 1 1 0 0 0 0 1 0",
" 0 0 x x a11 a10 a9 a8",
" a7 a6 a5 a4 a3 0 0 0",
" x x x x x x x x";
mode = 0x41;
delay = 10;
blocksize = 8;
readsize = 256;
;
memory "flash"
paged = yes;
size = 262144;
page_size = 256;
num_pages = 1024;
min_write_delay = 4500;
max_write_delay = 4500;
readback_p1 = 0x00;
readback_p2 = 0x00;
read_lo = " 0 0 1 0 0 0 0 0",
"a15 a14 a13 a12 a11 a10 a9 a8",
" a7 a6 a5 a4 a3 a2 a1 a0",
" o o o o o o o o";
read_hi = " 0 0 1 0 1 0 0 0",
"a15 a14 a13 a12 a11 a10 a9 a8",
" a7 a6 a5 a4 a3 a2 a1 a0",
" o o o o o o o o";
loadpage_lo = " 0 1 0 0 0 0 0 0",
" x x x x x x x x",
" x a6 a5 a4 a3 a2 a1 a0",
" i i i i i i i i";
loadpage_hi = " 0 1 0 0 1 0 0 0",
" x x x x x x x x",
" x a6 a5 a4 a3 a2 a1 a0",
" i i i i i i i i";
writepage = " 0 1 0 0 1 1 0 0",
"a15 a14 a13 a12 a11 a10 a9 a8",
" a7 x x x x x x x",
" x x x x x x x x";
load_ext_addr = " 0 1 0 0 1 1 0 1",
" 0 0 0 0 0 0 0 0",
" 0 0 0 0 0 0 0 a16",
" 0 0 0 0 0 0 0 0";
mode = 0x41;
delay = 10;
blocksize = 256;
readsize = 256;
;
memory "lfuse"
size = 1;
write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0",
"x x x x x x x x i i i i i i i i";
read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0",
"x x x x x x x x o o o o o o o o";
min_write_delay = 9000;
max_write_delay = 9000;
;
memory "hfuse"
size = 1;
write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0",
"x x x x x x x x i i i i i i i i";
read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0",
"x x x x x x x x o o o o o o o o";
min_write_delay = 9000;
max_write_delay = 9000;
;
memory "efuse"
size = 1;
write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0",
"x x x x x x x x x x x x x i i i";
read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0",
"x x x x x x x x o o o o o o o o";
min_write_delay = 9000;
max_write_delay = 9000;
;
memory "lock"
size = 1;
read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0",
"x x x x x x x x x x o o o o o o";
write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x",
"x x x x x x x x 1 1 i i i i i i";
min_write_delay = 9000;
max_write_delay = 9000;
;
memory "calibration"
size = 1;
read = "0 0 1 1 1 0 0 0 x x x x x x x x",
"0 0 0 0 0 0 0 0 o o o o o o o o";
;
memory "signature"
size = 3;
read = "0 0 1 1 0 0 0 0 x x x x x x x x",
"x x x x x x a1 a0 o o o o o o o o";
;
;
#------------------------------------------------------------
# ATmega32u4
#------------------------------------------------------------
part
id = "m32u4";
desc = "ATmega32U4";
signature = 0x1e 0x95 0x87;
usbpid = 0x2ff4;
has_jtag = yes;
# stk500_devcode = 0xB2;
# avr910_devcode = 0x43;
chip_erase_delay = 9000;
pagel = 0xD7;
bs2 = 0xA0;
reset = dedicated;
pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1",
"x x x x x x x x x x x x x x x x";
chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0",
"x x x x x x x x x x x x x x x x";
timeout = 200;
stabdelay = 100;
cmdexedelay = 25;
synchloops = 32;
bytedelay = 0;
pollindex = 3;
pollvalue = 0x53;
predelay = 1;
postdelay = 1;
pollmethod = 1;
pp_controlstack =
0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F,
0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F,
0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B,
0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00;
hventerstabdelay = 100;
progmodedelay = 0;
latchcycles = 5;
togglevtg = 1;
poweroffdelay = 15;
resetdelayms = 1;
resetdelayus = 0;
hvleavestabdelay = 15;
chiperasepulsewidth = 0;
chiperasepolltimeout = 10;
programfusepulsewidth = 0;
programfusepolltimeout = 5;
programlockpulsewidth = 0;
programlockpolltimeout = 5;
idr = 0x31;
spmcr = 0x57;
rampz = 0x3b;
allowfullpagebitstream = no;
ocdrev = 3;
memory "eeprom"
paged = no; /* leave this "no" */
page_size = 4; /* for parallel programming */
size = 1024;
min_write_delay = 9000;
max_write_delay = 9000;
readback_p1 = 0x00;
readback_p2 = 0x00;
read = " 1 0 1 0 0 0 0 0",
" x x x x x a10 a9 a8",
" a7 a6 a5 a4 a3 a2 a1 a0",
" o o o o o o o o";
write = " 1 1 0 0 0 0 0 0",
" x x x x x a10 a9 a8",
" a7 a6 a5 a4 a3 a2 a1 a0",
" i i i i i i i i";
loadpage_lo = " 1 1 0 0 0 0 0 1",
" 0 0 0 0 0 0 0 0",
" 0 0 0 0 0 a2 a1 a0",
" i i i i i i i i";
writepage = " 1 1 0 0 0 0 1 0",
" 0 0 x x x a10 a9 a8",
" a7 a6 a5 a4 a3 0 0 0",
" x x x x x x x x";
mode = 0x41;
delay = 20;
blocksize = 4;
readsize = 256;
;
memory "flash"
paged = yes;
size = 32768;
page_size = 128;
num_pages = 256;
min_write_delay = 4500;
max_write_delay = 4500;
readback_p1 = 0x00;
readback_p2 = 0x00;
read_lo = " 0 0 1 0 0 0 0 0",
" 0 a14 a13 a12 a11 a10 a9 a8",
" a7 a6 a5 a4 a3 a2 a1 a0",
" o o o o o o o o";
read_hi = " 0 0 1 0 1 0 0 0",
" 0 a14 a13 a12 a11 a10 a9 a8",
" a7 a6 a5 a4 a3 a2 a1 a0",
" o o o o o o o o";
loadpage_lo = " 0 1 0 0 0 0 0 0",
" x x x x x x x x",
" x x a5 a4 a3 a2 a1 a0",
" i i i i i i i i";
loadpage_hi = " 0 1 0 0 1 0 0 0",
" x x x x x x x x",
" x x a5 a4 a3 a2 a1 a0",
" i i i i i i i i";
writepage = " 0 1 0 0 1 1 0 0",
" a15 a14 a13 a12 a11 a10 a9 a8",
" a7 a6 x x x x x x",
" x x x x x x x x";
mode = 0x41;
delay = 6;
blocksize = 128;
readsize = 256;
;
memory "lfuse"
size = 1;
write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0",
"x x x x x x x x i i i i i i i i";
read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0",
"x x x x x x x x o o o o o o o o";
min_write_delay = 9000;
max_write_delay = 9000;
;
memory "hfuse"
size = 1;
write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0",
"x x x x x x x x i i i i i i i i";
read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0",
"x x x x x x x x o o o o o o o o";
min_write_delay = 9000;
max_write_delay = 9000;
;
memory "efuse"
size = 1;
write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0",
"x x x x x x x x x x x x i i i i";
read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0",
"x x x x x x x x o o o o o o o o";
min_write_delay = 9000;
max_write_delay = 9000;
;
memory "lock"
size = 1;
read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0",
"x x x x x x x x x x o o o o o o";
write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x",
"x x x x x x x x 1 1 i i i i i i";
min_write_delay = 9000;
max_write_delay = 9000;
;
memory "calibration"
size = 1;
read = "0 0 1 1 1 0 0 0 x x x x x x x x",
"0 0 0 0 0 0 0 0 o o o o o o o o";
;
memory "signature"
size = 3;
read = "0 0 1 1 0 0 0 0 x x x x x x x x",
"x x x x x x a1 a0 o o o o o o o o";
;
;

File diff suppressed because it is too large Load diff

View file

@ -42,7 +42,6 @@ static void avrdude_oom_handler(const char *context, void *user_p)
struct AvrDude::priv
{
std::string sys_config;
std::deque<std::vector<std::string>> args;
bool cancelled = false;
int exit_code = 0;
@ -54,8 +53,6 @@ struct AvrDude::priv
std::thread avrdude_thread;
priv(std::string &&sys_config) : sys_config(sys_config) {}
void set_handlers();
void unset_handlers();
int run_one(const std::vector<std::string> &args);
@ -110,7 +107,7 @@ int AvrDude::priv::run_one(const std::vector<std::string> &args) {
message_fn(command_line.c_str(), command_line.size());
const auto res = ::avrdude_main(static_cast<int>(c_args.size()), c_args.data(), sys_config.c_str());
const auto res = ::avrdude_main(static_cast<int>(c_args.size()), c_args.data());
return res;
}
@ -130,7 +127,7 @@ int AvrDude::priv::run() {
// Public
AvrDude::AvrDude(std::string sys_config) : p(new priv(std::move(sys_config))) {}
AvrDude::AvrDude() : p(new priv()) {}
AvrDude::AvrDude(AvrDude &&other) : p(std::move(other.p)) {}

View file

@ -22,8 +22,7 @@ public:
typedef std::function<void(const char * /* task */, unsigned /* progress */)> ProgressFn;
typedef std::function<void()> CompleteFn;
// Main c-tor, sys_config is the location of avrdude's main configuration file
AvrDude(std::string sys_config);
AvrDude();
AvrDude(AvrDude &&);
AvrDude(const AvrDude &) = delete;
AvrDude &operator=(AvrDude &&) = delete;

View file

@ -55,7 +55,7 @@ void avrdude_cancel();
#define MSG_TRACE (4) /* displayed with -vvvv, show trace commuication */
#define MSG_TRACE2 (5) /* displayed with -vvvvv */
int avrdude_main(int argc, char * argv [], const char *sys_config);
int avrdude_main(int argc, char * argv []);
#if defined(WIN32NATIVE)

View file

@ -0,0 +1,41 @@
#include <iostream>
#include <fstream>
#include <ios>
#include <iomanip>
int main(int argc, char const *argv[])
{
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " <file> <symbol name>" << std::endl;
return -1;
}
const char* filename = argv[1];
const char* symbol = argv[2];
size_t size = 0;
std::fstream file(filename);
if (!file.good()) {
std::cerr << "Cannot read file: " << filename << std::endl;
}
std::cout << "/* WARN: This file is auto-generated from `" << filename << "` */" << std::endl;
std::cout << "unsigned char " << symbol << "[] = {";
char c;
std::cout << std::hex;
std::cout.fill('0');
for (file.get(c); !file.eof(); size++, file.get(c)) {
if (size % 12 == 0) { std::cout << "\n "; }
std::cout << "0x" << std::setw(2) << (unsigned)c << ", ";
}
std::cout << "\n 0, 0\n};\n";
std::cout << std::dec;
std::cout << "size_t " << symbol << "_size = " << size << ";" << std::endl;
std::cout << "size_t " << symbol << "_size_yy = " << size + 2 << ";" << std::endl;
return 0;
}

View file

@ -32,6 +32,8 @@
#include "config_gram.h"
#include "avrdude-slic3r.conf.h" // Embedded config file
char default_programmer[MAX_STR_CONST];
char default_parallel[PATH_MAX];
char default_serial[PATH_MAX];
@ -325,7 +327,7 @@ int read_config(const char * file)
FILE * f;
int r;
f = fopen(file, "r");
f = fopen_utf8(file, "r");
if (f == NULL) {
avrdude_message(MSG_INFO, "%s: can't open config file \"%s\": %s\n",
progname, file, strerror(errno));
@ -347,3 +349,33 @@ int read_config(const char * file)
return r;
}
typedef struct yy_buffer_state *YY_BUFFER_STATE;
extern YY_BUFFER_STATE yy_scan_bytes(char *base, size_t size);
extern void yy_delete_buffer(YY_BUFFER_STATE b);
int read_config_builtin()
{
int r;
lineno = 1;
infile = "(builtin)";
// Note: Can't use yy_scan_buffer, it's buggy (?), leads to fread from a null FILE*
// and so unfortunatelly we have to use the copying variant here
YY_BUFFER_STATE buffer = yy_scan_bytes(avrdude_slic3r_conf, avrdude_slic3r_conf_size);
if (buffer == NULL) {
avrdude_message(MSG_INFO, "%s: read_config_builtin: Failed to initialize parsing buffer\n", progname);
return -1;
}
r = yyparse();
yy_delete_buffer(buffer);
#ifdef HAVE_YYLEX_DESTROY
/* reset lexer and free any allocated memory */
yylex_destroy();
#endif
return r;
}

View file

@ -40,6 +40,9 @@
#include "avrdude.h"
#include "libavrdude.h"
#if defined(WIN32NATIVE)
#include "windows/utf8.h"
#endif
#define IHEX_MAXDATA 256
@ -102,21 +105,25 @@ static int fmt_autodetect(char * fname, unsigned section);
static FILE *fopen_and_seek(const char *filename, const char *mode, unsigned section)
FILE *fopen_utf8(const char *filename, const char *mode)
{
FILE *file;
// On Windows we need to convert the filename to UTF-16
#if defined(WIN32NATIVE)
static wchar_t fname_buffer[PATH_MAX];
static wchar_t mode_buffer[MAX_MODE_LEN];
if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, fname_buffer, PATH_MAX) == 0) { return NULL; }
if (MultiByteToWideChar(CP_ACP, 0, mode, -1, mode_buffer, MAX_MODE_LEN) == 0) { return NULL; }
if (MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_buffer, MAX_MODE_LEN) == 0) { return NULL; }
file = _wfopen(fname_buffer, mode_buffer);
return _wfopen(fname_buffer, mode_buffer);
#else
file = fopen(filename, mode);
return fopen(filename, mode);
#endif
}
static FILE *fopen_and_seek(const char *filename, const char *mode, unsigned section)
{
FILE *file = fopen_utf8(filename, mode);
if (file == NULL) {
return NULL;

View file

@ -1527,8 +1527,10 @@ static int jtagmkII_open(PROGRAMMER * pgm, char * port)
*/
jtagmkII_drain(pgm, 0);
if (jtagmkII_getsync(pgm, EMULATOR_MODE_JTAG) < 0)
if (jtagmkII_getsync(pgm, EMULATOR_MODE_JTAG) < 0) {
serial_close(&pgm->fd);
return -1;
}
return 0;
}
@ -1579,8 +1581,10 @@ static int jtagmkII_open_dw(PROGRAMMER * pgm, char * port)
*/
jtagmkII_drain(pgm, 0);
if (jtagmkII_getsync(pgm, EMULATOR_MODE_DEBUGWIRE) < 0)
if (jtagmkII_getsync(pgm, EMULATOR_MODE_DEBUGWIRE) < 0) {
serial_close(&pgm->fd);
return -1;
}
return 0;
}
@ -1631,8 +1635,10 @@ static int jtagmkII_open_pdi(PROGRAMMER * pgm, char * port)
*/
jtagmkII_drain(pgm, 0);
if (jtagmkII_getsync(pgm, EMULATOR_MODE_PDI) < 0)
if (jtagmkII_getsync(pgm, EMULATOR_MODE_PDI) < 0) {
serial_close(&pgm->fd);
return -1;
}
return 0;
}
@ -1684,8 +1690,10 @@ static int jtagmkII_dragon_open(PROGRAMMER * pgm, char * port)
*/
jtagmkII_drain(pgm, 0);
if (jtagmkII_getsync(pgm, EMULATOR_MODE_JTAG) < 0)
if (jtagmkII_getsync(pgm, EMULATOR_MODE_JTAG) < 0) {
serial_close(&pgm->fd);
return -1;
}
return 0;
}
@ -1737,8 +1745,10 @@ static int jtagmkII_dragon_open_dw(PROGRAMMER * pgm, char * port)
*/
jtagmkII_drain(pgm, 0);
if (jtagmkII_getsync(pgm, EMULATOR_MODE_DEBUGWIRE) < 0)
if (jtagmkII_getsync(pgm, EMULATOR_MODE_DEBUGWIRE) < 0) {
serial_close(&pgm->fd);
return -1;
}
return 0;
}
@ -1790,8 +1800,10 @@ static int jtagmkII_dragon_open_pdi(PROGRAMMER * pgm, char * port)
*/
jtagmkII_drain(pgm, 0);
if (jtagmkII_getsync(pgm, EMULATOR_MODE_PDI) < 0)
if (jtagmkII_getsync(pgm, EMULATOR_MODE_PDI) < 0) {
serial_close(&pgm->fd);
return -1;
}
return 0;
}
@ -3370,6 +3382,8 @@ static int jtagmkII_open32(PROGRAMMER * pgm, char * port)
status = jtagmkII_getsync(pgm, -1);
if(status < 0) return -1;
// FIXME: Error handling is bad here: memory leak in resp (?) and port not closed
// AVR32 "special"
buf[0] = CMND_SET_PARAMETER;
buf[1] = 0x2D;

View file

@ -820,6 +820,8 @@ extern "C" {
char * fmtstr(FILEFMT format);
FILE *fopen_utf8(const char *filename, const char *mode);
int fileio(int op, char * filename, FILEFMT format,
struct avrpart * p, char * memtype, int size, unsigned section);
@ -939,6 +941,7 @@ int init_config(void);
void cleanup_config(void);
int read_config(const char * file);
int read_config_builtin();
#ifdef __cplusplus
}

View file

@ -1,9 +0,0 @@
#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);
}

View file

@ -0,0 +1,54 @@
extern "C" {
#include "avrdude.h"
}
#ifdef WIN32
#include <stdlib.h>
#include <vector>
extern "C" {
#include "windows/utf8.h"
}
struct ArgvUtf8 : std::vector<char*>
{
int argc;
ArgvUtf8(int argc_w, wchar_t *argv_w[]) : std::vector<char*>(argc_w + 1, nullptr), argc(0)
{
for (int i = 0; i < argc_w; i++) {
char *arg_utf8 = ::wstr_to_utf8(argv_w[i], -1);
if (arg_utf8 != nullptr) {
operator[](i) = arg_utf8;
argc = i + 1;
} else {
break;
}
}
}
~ArgvUtf8()
{
for (char *arg : *this) {
if (arg != nullptr) {
::free(arg);
}
}
}
};
int wmain(int argc_w, wchar_t *argv_w[])
{
ArgvUtf8 argv_utf8(argc_w, argv_w);
return ::avrdude_main(argv_utf8.argc, &argv_utf8[0]);
}
#else
int main(int argc, char *argv[])
{
return ::avrdude_main(argc, argv);
}
#endif

View file

@ -426,7 +426,7 @@ static int cleanup_main(int status)
/*
* main routine
*/
int avrdude_main(int argc, char * argv [], const char *sys_config)
int avrdude_main(int argc, char * argv [])
{
int rc; /* general return code checking */
int exitrc; /* exit code for main() */
@ -807,13 +807,15 @@ int avrdude_main(int argc, char * argv [], const char *sys_config)
"%sCopyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/\n"
"%sCopyright (c) 2007-2014 Joerg Wunsch\n\n",
progname, version, __DATE__, __TIME__, progbuf, progbuf);
avrdude_message(MSG_NOTICE, "%sSystem wide configuration file is \"%s\"\n",
progbuf, sys_config);
// avrdude_message(MSG_NOTICE, "%sSystem wide configuration file is \"%s\"\n",
// progbuf, sys_config);
rc = read_config(sys_config);
// rc = read_config(sys_config);
rc = read_config_builtin();
if (rc) {
avrdude_message(MSG_INFO, "%s: error reading system wide configuration file \"%s\"\n",
progname, sys_config);
// avrdude_message(MSG_INFO, "%s: error reading system wide configuration file \"%s\"\n",
// progname, sys_config);
avrdude_message(MSG_INFO, "%s: error reading built-in configuration file\n", progname);
return cleanup_main(1);
}

View file

@ -44,7 +44,7 @@
#include "avrdude.h"
#include "libavrdude.h"
long serial_recv_timeout = 5000; /* ms */
long serial_recv_timeout = 4000; /* ms */
#define MAX_ZERO_READS 512
struct baud_mapping {
@ -150,6 +150,68 @@ static int ser_setspeed(union filedescriptor *fd, long baud)
return 0;
}
// Timeout read & write variants
// Additionally to the regular -1 on I/O error, they return -2 on timeout
ssize_t read_timeout(int fd, void *buf, size_t count, long timeout)
{
struct timeval tm, tm2;
fd_set rfds;
int nfds;
tm.tv_sec = timeout / 1000L;
tm.tv_usec = (timeout % 1000L) * 1000;
while (1) {
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
tm2 = tm;
nfds = select(fd + 1, &rfds, NULL, NULL, &tm2);
if (nfds == 0) {
return -2;
} else if (nfds == -1) {
if (errno == EINTR || errno == EAGAIN) {
continue;
} else {
return -1;
}
} else {
return read(fd, buf, count);
}
}
}
ssize_t write_timeout(int fd, const void *buf, size_t count, long timeout)
{
struct timeval tm, tm2;
fd_set wfds;
int nfds;
tm.tv_sec = timeout / 1000L;
tm.tv_usec = (timeout % 1000L) * 1000;
while (1) {
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
tm2 = tm;
nfds = select(fd + 1, NULL, &wfds, NULL, &tm2);
if (nfds == 0) {
return -2;
} else if (nfds == -1) {
if (errno == EINTR || errno == EAGAIN) {
continue;
} else {
return -1;
}
} else {
return write(fd, buf, count);
}
}
}
/*
* Given a port description of the form <host>:<port>, open a TCP
* connection to the specified destination, which is assumed to be a
@ -314,6 +376,7 @@ static int ser_send(union filedescriptor *fd, const unsigned char * buf, size_t
int rc;
const unsigned char * p = buf;
size_t len = buflen;
unsigned zero_writes = 0;
if (!len)
return 0;
@ -341,14 +404,25 @@ static int ser_send(union filedescriptor *fd, const unsigned char * buf, size_t
while (len) {
RETURN_IF_CANCEL();
rc = write(fd->ifd, p, (len > 1024) ? 1024 : len);
if (rc < 0) {
avrdude_message(MSG_INFO, "%s: ser_send(): write error: %s\n",
progname, strerror(errno));
rc = write_timeout(fd->ifd, p, (len > 1024) ? 1024 : len, serial_recv_timeout);
if (rc == -2) {
avrdude_message(MSG_NOTICE2, "%s: ser_send(): programmer is not responding\n", progname);
return -1;
} else if (rc == -1) {
avrdude_message(MSG_INFO, "%s: ser_send(): write error: %s\n", progname, strerror(errno));
return -1;
} else if (rc == 0) {
zero_writes++;
if (zero_writes > MAX_ZERO_READS) {
avrdude_message(MSG_NOTICE2, "%s: ser_send(): programmer is not responding (too many zero writes)\n",
progname);
return -1;
}
} else {
zero_writes = 0;
p += rc;
len -= rc;
}
p += rc;
len -= rc;
}
return 0;
@ -357,51 +431,21 @@ static int ser_send(union filedescriptor *fd, const unsigned char * buf, size_t
static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen)
{
struct timeval timeout, to2;
fd_set rfds;
int nfds;
int rc;
unsigned char * p = buf;
size_t len = 0;
unsigned zero_reads = 0;
timeout.tv_sec = serial_recv_timeout / 1000L;
timeout.tv_usec = (serial_recv_timeout % 1000L) * 1000;
to2 = timeout;
while (len < buflen) {
reselect:
RETURN_IF_CANCEL();
FD_ZERO(&rfds);
FD_SET(fd->ifd, &rfds);
nfds = select(fd->ifd + 1, &rfds, NULL, NULL, &to2);
// FIXME: The timeout has different behaviour on Linux vs other Unices
// On Linux, the timeout is modified by subtracting the time spent,
// on OS X (for example), it is not modified.
// POSIX recommends re-initializing it before selecting.
if (nfds == 0) {
avrdude_message(MSG_NOTICE2, "%s: ser_recv(): programmer is not responding\n",
progname);
rc = read_timeout(fd->ifd, p, (buflen - len > 1024) ? 1024 : buflen - len, serial_recv_timeout);
if (rc == -2) {
avrdude_message(MSG_NOTICE2, "%s: ser_recv(): programmer is not responding\n", progname);
return -1;
}
else if (nfds == -1) {
if (errno == EINTR || errno == EAGAIN) {
avrdude_message(MSG_INFO, "%s: ser_recv(): programmer is not responding,reselecting\n",
progname);
goto reselect;
}
else {
avrdude_message(MSG_INFO, "%s: ser_recv(): select(): %s\n",
progname, strerror(errno));
return -1;
}
}
rc = read(fd->ifd, p, (buflen - len > 1024) ? 1024 : buflen - len);
if (rc < 0) {
avrdude_message(MSG_INFO, "%s: ser_recv(): read error: %s\n",
progname, strerror(errno));
} else if (rc == -1) {
avrdude_message(MSG_INFO, "%s: ser_recv(): read error: %s\n", progname, strerror(errno));
return -1;
} else if (rc == 0) {
zero_reads++;
@ -445,49 +489,26 @@ static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen
static int ser_drain(union filedescriptor *fd, int display)
{
struct timeval timeout;
fd_set rfds;
int nfds;
int rc;
unsigned char buf;
unsigned zero_reads = 0;
timeout.tv_sec = 0;
timeout.tv_usec = 250000;
if (display) {
avrdude_message(MSG_INFO, "drain>");
}
while (1) {
FD_ZERO(&rfds);
FD_SET(fd->ifd, &rfds);
reselect:
RETURN_IF_CANCEL();
nfds = select(fd->ifd + 1, &rfds, NULL, NULL, &timeout);
if (nfds == 0) {
rc = read_timeout(fd->ifd, &buf, 1, 250); // Note: timeout needs to be kept low to not timeout in programmers
if (rc == -2) {
if (display) {
avrdude_message(MSG_INFO, "<drain\n");
}
break;
}
else if (nfds == -1) {
if (errno == EINTR) {
goto reselect;
}
else {
avrdude_message(MSG_INFO, "%s: ser_drain(): select(): %s\n",
progname, strerror(errno));
return -1;
}
}
rc = read(fd->ifd, &buf, 1);
if (rc < 0) {
avrdude_message(MSG_INFO, "%s: ser_drain(): read error: %s\n",
progname, strerror(errno));
break;
} else if (rc == -1) {
avrdude_message(MSG_INFO, "%s: ser_drain(): read error: %s\n", progname, strerror(errno));
return -1;
} else if (rc == 0) {
zero_reads++;

View file

@ -34,16 +34,63 @@
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h> /* for isprint */
#include <errno.h> /* ENOTTY */
#include "avrdude.h"
#include "libavrdude.h"
#include "windows/utf8.h"
long serial_recv_timeout = 5000; /* ms */
#define W32SERBUFSIZE 1024
// Get last error message string in UTF-8
// Always return a valid null-terminated string
// The returned string should be freed by the caller
char* last_error_string(int wsa)
{
LPWSTR wbuffer = NULL;
(void)wsa;
DWORD wbuffer_len = FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
#ifdef HAVE_LIBWS2_32
wsa ? WSAGetLastError() : GetLastError(),
#else
GetLastError(),
#endif
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&wbuffer,
0,
NULL);
if (wbuffer_len == 0) {
return NULL;
}
char *res = wstr_to_utf8(wbuffer, wbuffer_len);
LocalFree(wbuffer);
if (res == NULL) {
// If we get here, conversion to UTF-8 failed
res = strdup("(could not get error message)");
if (res == NULL) {
avrdude_oom("last_error_string(): out of memory\n");
}
}
return res;
}
struct baud_mapping {
long baud;
DWORD speed;
@ -95,6 +142,7 @@ static BOOL serial_w32SetTimeOut(HANDLE hComPort, DWORD timeout) // in ms
// ctmo.ReadIntervalTimeout = timeout;
// ctmo.ReadTotalTimeoutMultiplier = timeout;
ctmo.ReadTotalTimeoutConstant = timeout;
ctmo.WriteTotalTimeoutConstant = timeout;
return SetCommTimeouts(hComPort, &ctmo);
}
@ -129,7 +177,6 @@ static int
net_open(const char *port, union filedescriptor *fdp)
{
WSADATA wsaData;
LPVOID lpMsgBuf;
char *hstr, *pstr, *end;
unsigned int pnum;
@ -175,18 +222,10 @@ net_open(const char *port, union filedescriptor *fdp)
free(hstr);
if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0,
NULL);
avrdude_message(MSG_INFO, "%s: net_open(): Cannot open socket: %s\n", progname, (char *)lpMsgBuf);
LocalFree(lpMsgBuf);
const char *error = last_error_string(1);
avrdude_message(MSG_INFO, "%s: net_open(): Cannot open socket: %s\n", progname, error);
free(error);
return -1;
}
@ -196,18 +235,9 @@ net_open(const char *port, union filedescriptor *fdp)
memcpy(&(sockaddr.sin_addr.s_addr), hp->h_addr, sizeof(struct in_addr));
if (connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0,
NULL);
avrdude_message(MSG_INFO, "%s: net_open(): Connect failed: %s\n", progname, (char *)lpMsgBuf);
LocalFree(lpMsgBuf);
const char *error = last_error_string(1);
avrdude_message(MSG_INFO, "%s: net_open(): Connect failed: %s\n", progname);
free(error);
return -1;
}
@ -221,7 +251,6 @@ net_open(const char *port, union filedescriptor *fdp)
static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
{
LPVOID lpMsgBuf;
HANDLE hComPort=INVALID_HANDLE_VALUE;
char *newname = 0;
@ -261,19 +290,9 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hComPort == INVALID_HANDLE_VALUE) {
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL);
avrdude_message(MSG_INFO, "%s: ser_open(): can't open device \"%s\": %s\n",
progname, port, (char*)lpMsgBuf);
LocalFree( lpMsgBuf );
const char *error = last_error_string(0);
avrdude_message(MSG_INFO, "%s: ser_open(): can't open device \"%s\": %s\n", progname, port, error);
free(error);
return -1;
}
@ -346,14 +365,13 @@ static int ser_set_dtr_rts(union filedescriptor *fd, int is_on)
#ifdef HAVE_LIBWS2_32
static int net_send(union filedescriptor *fd, const unsigned char * buf, size_t buflen)
{
LPVOID lpMsgBuf;
int rc;
const unsigned char *p = buf;
size_t len = buflen;
if (fd->ifd < 0) {
avrdude_message(MSG_NOTICE, "%s: net_send(): connection not open\n", progname);
exit(1);
return -1;
}
if (!len) {
@ -382,19 +400,10 @@ static int net_send(union filedescriptor *fd, const unsigned char * buf, size_t
while (len) {
rc = send(fd->ifd, p, (len > 1024) ? 1024 : len, 0);
if (rc < 0) {
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0,
NULL);
avrdude_message(MSG_INFO, "%s: net_send(): send error: %s\n", progname, (char *)lpMsgBuf);
LocalFree(lpMsgBuf);
exit(1);
const char *error = last_error_string(1);
avrdude_message(MSG_INFO, "%s: net_send(): send error: %s\n", progname, error);
free(error);
return -1;
}
p += rc;
len -= rc;
@ -423,8 +432,7 @@ static int ser_send(union filedescriptor *fd, const unsigned char * buf, size_t
HANDLE hComPort=(HANDLE)fd->pfd;
if (hComPort == INVALID_HANDLE_VALUE) {
avrdude_message(MSG_INFO, "%s: ser_send(): port not open\n",
progname);
avrdude_message(MSG_INFO, "%s: ser_send(): port not open\n", progname);
return -1;
}
@ -449,18 +457,18 @@ static int ser_send(union filedescriptor *fd, const unsigned char * buf, size_t
}
avrdude_message(MSG_INFO, "\n");
}
serial_w32SetTimeOut(hComPort,500);
if (!WriteFile (hComPort, buf, buflen, &written, NULL)) {
avrdude_message(MSG_INFO, "%s: ser_send(): write error: %s\n",
progname, "sorry no info avail"); // TODO
if (!WriteFile(hComPort, buf, buflen, &written, NULL)) {
const char *error = last_error_string(0);
avrdude_message(MSG_INFO, "%s: ser_send(): write error: %s\n", progname, error);
free(error);
return -1;
}
if (written != buflen) {
avrdude_message(MSG_INFO, "%s: ser_send(): size/send mismatch\n",
progname);
avrdude_message(MSG_INFO, "%s: ser_send(): size/send mismatch\n", progname);
return -1;
}
@ -471,7 +479,6 @@ static int ser_send(union filedescriptor *fd, const unsigned char * buf, size_t
#ifdef HAVE_LIBWS2_32
static int net_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen)
{
LPVOID lpMsgBuf;
struct timeval timeout, to2;
fd_set rfds;
int nfds;
@ -481,7 +488,7 @@ static int net_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen
if (fd->ifd < 0) {
avrdude_message(MSG_INFO, "%s: net_recv(): connection not open\n", progname);
exit(1);
return -1;
}
timeout.tv_sec = serial_recv_timeout / 1000L;
@ -504,37 +511,19 @@ reselect:
avrdude_message(MSG_NOTICE, "%s: ser_recv(): programmer is not responding, reselecting\n", progname);
goto reselect;
} else {
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0,
NULL);
avrdude_message(MSG_INFO, "%s: ser_recv(): select(): %s\n", progname, (char *)lpMsgBuf);
LocalFree(lpMsgBuf);
exit(1);
const char *error = last_error_string(1);
avrdude_message(MSG_INFO, "%s: ser_recv(): select(): %s\n", progname, error);
free(error);
return -1;
}
}
rc = recv(fd->ifd, p, (buflen - len > 1024) ? 1024 : buflen - len, 0);
if (rc < 0) {
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0,
NULL);
avrdude_message(MSG_INFO, "%s: ser_recv(): read error: %s\n", progname, (char *)lpMsgBuf);
LocalFree(lpMsgBuf);
exit(1);
const char *error = last_error_string(1);
avrdude_message(MSG_INFO, "%s: ser_recv(): read error: %s\n", progname, error);
free(error);
return -1;
}
p += rc;
len += rc;
@ -579,37 +568,24 @@ static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen
RETURN_IF_CANCEL();
HANDLE hComPort=(HANDLE)fd->pfd;
if (hComPort == INVALID_HANDLE_VALUE) {
avrdude_message(MSG_INFO, "%s: ser_read(): port not open\n",
progname);
avrdude_message(MSG_INFO, "%s: ser_read(): port not open\n", progname);
return -1;
}
serial_w32SetTimeOut(hComPort, serial_recv_timeout);
if (!ReadFile(hComPort, buf, buflen, &read, NULL)) {
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL );
avrdude_message(MSG_INFO, "%s: ser_recv(): read error: %s\n",
progname, (char*)lpMsgBuf);
LocalFree( lpMsgBuf );
const char *error = last_error_string(0);
avrdude_message(MSG_INFO, "%s: ser_recv(): read error: %s\n", progname, error);
free(error);
return -1;
}
/* time out detected */
if (read == 0) {
avrdude_message(MSG_NOTICE2, "%s: ser_recv(): programmer is not responding\n",
progname);
avrdude_message(MSG_NOTICE2, "%s: ser_recv(): programmer is not responding\n", progname);
return -1;
}
@ -664,20 +640,9 @@ static int ser_drain(union filedescriptor *fd, int display)
readres=ReadFile(hComPort, buf, 1, &read, NULL);
if (!readres) {
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL );
avrdude_message(MSG_INFO, "%s: ser_drain(): read error: %s\n",
progname, (char*)lpMsgBuf);
LocalFree( lpMsgBuf );
const char *error = last_error_string(0);
avrdude_message(MSG_INFO, "%s: ser_drain(): read error: %s\n", progname, error);
free(error);
return -1;
}

View file

@ -669,11 +669,15 @@ static int stk500_open(PROGRAMMER * pgm, char * port)
// MIB510 init
if (strcmp(ldata(lfirst(pgm->id)), "mib510") == 0 &&
mib510_isp(pgm, 1) != 0)
mib510_isp(pgm, 1) != 0) {
serial_close(&pgm->fd);
return -1;
}
if (stk500_getsync(pgm) < 0)
if (stk500_getsync(pgm) < 0) {
serial_close(&pgm->fd);
return -1;
}
return 0;
}

View file

@ -1695,8 +1695,10 @@ static int stk500v2_open(PROGRAMMER * pgm, char * port)
stk500v2_drain(pgm, 0);
if (pgm->bitclock != 0.0) {
if (pgm->set_sck_period(pgm, pgm->bitclock) != 0)
if (pgm->set_sck_period(pgm, pgm->bitclock) != 0) {
serial_close(&pgm->fd);
return -1;
}
}
return 0;
@ -1753,8 +1755,10 @@ static int stk600_open(PROGRAMMER * pgm, char * port)
stk500v2_drain(pgm, 0);
if (pgm->bitclock != 0.0) {
if (pgm->set_sck_period(pgm, pgm->bitclock) != 0)
if (pgm->set_sck_period(pgm, pgm->bitclock) != 0) {
serial_close(&pgm->fd);
return -1;
}
}
return 0;

View file

@ -0,0 +1,45 @@
#include "utf8.h"
char* wstr_to_utf8(LPWSTR wstr, int len)
{
char *res = NULL;
int res_size = WideCharToMultiByte(CP_UTF8, 0, wstr, len, NULL, 0, NULL, NULL);
if (res_size > 0) {
// Note: WideCharToMultiByte doesn't null-terminate if real (ie. > 0) buffer length is passed
res = malloc(len != - 1 ? res_size + 1 : res_size);
if (res == NULL) { return NULL; }
if (WideCharToMultiByte(CP_UTF8, 0, wstr, len, res, res_size, NULL, NULL) == res_size) {
if (len != -1) { res[res_size] = '\0'; }
} else {
free(res);
return NULL;
}
}
return res;
}
LPWSTR utf8_to_wstr(const char *str, int len)
{
LPWSTR res = NULL;
int res_size = MultiByteToWideChar(CP_UTF8, 0, str, len, NULL, 0);
if (res_size > 0) {
// Note: MultiByteToWideChar doesn't null-terminate if real (ie. > 0) buffer length is passed
res = malloc(len != - 1 ? res_size + 1 : res_size);
if (res == NULL) { return NULL; }
if (MultiByteToWideChar(CP_UTF8, 0, str, len, res, res_size) == res_size) {
if (len != -1) { res[res_size] = L'\0'; }
} else {
free(res);
return NULL;
}
}
return res;
}

View file

@ -0,0 +1,10 @@
#ifndef SLIC3R_AVRDUDE_UTF8_H
#define SLIC3R_AVRDUDE_UTF8_H
#include <windows.h>
extern char* wstr_to_utf8(LPWSTR wstr, int len);
extern LPWSTR utf8_to_wstr(const char *str, int len);
#endif // SLIC3R_AVRDUDE_UTF8_H

View file

@ -192,8 +192,10 @@ static int wiring_open(PROGRAMMER * pgm, char * port)
/* drain any extraneous input */
stk500v2_drain(pgm, 0);
if (stk500v2_getsync(pgm) < 0)
if (stk500v2_getsync(pgm) < 0) {
serial_close(&pgm->fd);
return -1;
}
return 0;
}

View file

@ -42,14 +42,20 @@ public:
bool bridge;
Flow(float _w, float _h, float _nd, bool _bridge = false) :
width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {};
width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {}
float spacing() const;
float spacing(const Flow &other) const;
double mm3_per_mm() const;
coord_t scaled_width() const { return coord_t(scale_(this->width)); };
coord_t scaled_spacing() const { return coord_t(scale_(this->spacing())); };
coord_t scaled_spacing(const Flow &other) const { return coord_t(scale_(this->spacing(other))); };
coord_t scaled_width() const { return coord_t(scale_(this->width)); }
coord_t scaled_spacing() const { return coord_t(scale_(this->spacing())); }
coord_t scaled_spacing(const Flow &other) const { return coord_t(scale_(this->spacing(other))); }
// Elephant foot compensation spacing to be used to detect narrow parts, where the elephant foot compensation cannot be applied.
// To be used on frExternalPerimeter only.
// Enable some perimeter squish (see INSET_OVERLAP_TOLERANCE).
// Here an overlap of 0.2x external perimeter spacing is allowed for by the elephant foot compensation.
coord_t scaled_elephant_foot_spacing() const { return coord_t(0.5f * float(this->scaled_width() + 0.6f * this->scaled_spacing())); }
static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio);
// Create a flow from the spacing of extrusion lines.

View file

@ -6,6 +6,8 @@
#include "3mf.hpp"
#include <limits>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/predicate.hpp>
@ -1749,6 +1751,11 @@ namespace Slic3r {
bool _3MF_Exporter::_add_model_file_to_archive(mz_zip_archive& archive, Model& model)
{
std::stringstream stream;
// https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10
// Conversion of a floating-point value to text and back is exact as long as at least max_digits10 were used (9 for float, 17 for double).
// It is guaranteed to produce the same floating-point value, even though the intermediate text representation is not exact.
// The default value of std::stream precision is 6 digits only!
stream << std::setprecision(std::numeric_limits<float>::max_digits10);
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n";
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "</" << METADATA_TAG << ">\n";
@ -1864,7 +1871,7 @@ namespace Slic3r {
for (int i = 0; i < stl.stats.shared_vertices; ++i)
{
stream << " <" << VERTEX_TAG << " ";
Vec3d v = matrix * stl.v_shared[i].cast<double>();
Vec3f v = (matrix * stl.v_shared[i].cast<double>()).cast<float>();
stream << "x=\"" << v(0) << "\" ";
stream << "y=\"" << v(1) << "\" ";
stream << "z=\"" << v(2) << "\" />\n";

View file

@ -1,3 +1,4 @@
#include <limits>
#include <string.h>
#include <map>
#include <string>
@ -856,6 +857,11 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
return false;
std::stringstream stream;
// https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10
// Conversion of a floating-point value to text and back is exact as long as at least max_digits10 were used (9 for float, 17 for double).
// It is guaranteed to produce the same floating-point value, even though the intermediate text representation is not exact.
// The default value of std::stream precision is 6 digits only!
stream << std::setprecision(std::numeric_limits<float>::max_digits10);
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
stream << "<amf unit=\"millimeter\">\n";
stream << "<metadata type=\"cad\">Slic3r " << SLIC3R_VERSION << "</metadata>\n";
@ -927,7 +933,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
for (size_t i = 0; i < stl.stats.shared_vertices; ++i) {
stream << " <vertex>\n";
stream << " <coordinates>\n";
Vec3d v = matrix * stl.v_shared[i].cast<double>();
Vec3f v = (matrix * stl.v_shared[i].cast<double>()).cast<float>();
stream << " <x>" << v(0) << "</x>\n";
stream << " <y>" << v(1) << "</y>\n";
stream << " <z>" << v(2) << "</z>\n";

View file

@ -1,3 +1,4 @@
#include "libslic3r.h"
#include "GCode.hpp"
#include "ExtrusionEntity.hpp"
#include "EdgeGrid.hpp"
@ -574,6 +575,16 @@ void GCode::_do_export(Print &print, FILE *file)
// resets analyzer
m_analyzer.reset();
// send extruder offset data to analyzer
GCodeAnalyzer::ExtruderOffsetsMap extruder_offsets;
for (unsigned int extruder_id : print.extruders())
{
Vec2d offset = print.config().extruder_offset.get_at(extruder_id);
if (!offset.isApprox(Vec2d::Zero()))
extruder_offsets[extruder_id] = offset;
}
m_analyzer.set_extruder_offsets(extruder_offsets);
// resets analyzer's tracking data
m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm;
m_last_width = GCodeAnalyzer::Default_Width;
@ -843,7 +854,7 @@ void GCode::_do_export(Print &print, FILE *file)
for (unsigned int extruder_id : print.extruders()) {
const Vec2d &extruder_offset = print.config().extruder_offset.get_at(extruder_id);
Polygon s(outer_skirt);
s.translate(Point::new_scale(- extruder_offset(0), - extruder_offset(1)));
s.translate(Point::new_scale(-extruder_offset(0), -extruder_offset(1)));
skirts.emplace_back(std::move(s));
}
m_ooze_prevention.enable = true;
@ -2488,6 +2499,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
// adds analyzer tags and updates analyzer's tracking data
if (m_enable_analyzer)
{
// PrusaMultiMaterial::Writer may generate GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines without updating m_last_height and m_last_width
// so, if the last role was erWipeTower we force export of GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines
bool last_was_wipe_tower = (m_last_analyzer_extrusion_role == erWipeTower);
if (path.role() != m_last_analyzer_extrusion_role)
{
m_last_analyzer_extrusion_role = path.role();
@ -2505,7 +2520,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
gcode += buf;
}
if (m_last_width != path.width)
if (last_was_wipe_tower || (m_last_width != path.width))
{
m_last_width = path.width;
@ -2514,7 +2529,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
gcode += buf;
}
if (m_last_height != path.height)
if (last_was_wipe_tower || (m_last_height != path.height))
{
m_last_height = path.height;

View file

@ -101,6 +101,11 @@ GCodeAnalyzer::GCodeAnalyzer()
reset();
}
void GCodeAnalyzer::set_extruder_offsets(const GCodeAnalyzer::ExtruderOffsetsMap& extruder_offsets)
{
m_extruder_offsets = extruder_offsets;
}
void GCodeAnalyzer::reset()
{
_set_units(Millimeters);
@ -118,6 +123,7 @@ void GCodeAnalyzer::reset()
_reset_axes_position();
m_moves_map.clear();
m_extruder_offsets.clear();
}
const std::string& GCodeAnalyzer::process_gcode(const std::string& gcode)
@ -654,7 +660,15 @@ void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type)
it = m_moves_map.insert(TypeToMovesMap::value_type(type, GCodeMovesList())).first;
// store move
it->second.emplace_back(type, _get_extrusion_role(), _get_extruder_id(), _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), _get_start_position(), _get_end_position(), _get_delta_extrusion(), _get_cp_color_id());
Vec3d extruder_offset = Vec3d::Zero();
unsigned int extruder_id = _get_extruder_id();
ExtruderOffsetsMap::iterator extr_it = m_extruder_offsets.find(extruder_id);
if (extr_it != m_extruder_offsets.end())
extruder_offset = Vec3d(extr_it->second(0), extr_it->second(1), 0.0);
Vec3d start_position = _get_start_position() + extruder_offset;
Vec3d end_position = _get_end_position() + extruder_offset;
it->second.emplace_back(type, _get_extrusion_role(), extruder_id, _get_mm3_per_mm(), _get_width(), _get_height(), _get_feedrate(), start_position, end_position, _get_delta_extrusion(), _get_cp_color_id());
}
bool GCodeAnalyzer::_is_valid_extrusion_role(int value) const

View file

@ -86,6 +86,7 @@ public:
typedef std::vector<GCodeMove> GCodeMovesList;
typedef std::map<GCodeMove::EType, GCodeMovesList> TypeToMovesMap;
typedef std::map<unsigned int, Vec2d> ExtruderOffsetsMap;
private:
struct State
@ -104,6 +105,7 @@ private:
State m_state;
GCodeReader m_parser;
TypeToMovesMap m_moves_map;
ExtruderOffsetsMap m_extruder_offsets;
// The output of process_layer()
std::string m_process_output;
@ -111,6 +113,8 @@ private:
public:
GCodeAnalyzer();
void set_extruder_offsets(const ExtruderOffsetsMap& extruder_offsets);
// Reinitialize the analyzer
void reset();

View file

@ -242,6 +242,7 @@ public:
void set_scaling_factor(const Vec3d& scaling_factor);
void set_scaling_factor(Axis axis, double scaling_factor);
bool is_scaling_uniform() const { return std::abs(m_scaling_factor.x() - m_scaling_factor.y()) < 1e-8 && std::abs(m_scaling_factor.x() - m_scaling_factor.z()) < 1e-8; }
const Vec3d& get_mirror() const { return m_mirror; }
double get_mirror(Axis axis) const { return m_mirror(axis); }

View file

@ -65,6 +65,7 @@ void Layer::make_slices()
this->slices.expolygons.push_back(std::move(slices[i]));
}
// Merge typed slices into untyped slices. This method is used to revert the effects of detect_surfaces_type() called for posPrepareInfill.
void Layer::merge_slices()
{
if (m_regions.size() == 1) {
@ -78,6 +79,24 @@ void Layer::merge_slices()
}
}
ExPolygons Layer::merged(float offset_scaled) const
{
assert(offset_scaled >= 0.f);
// If no offset is set, apply EPSILON offset before union, and revert it afterwards.
float offset_scaled2 = 0;
if (offset_scaled == 0.f) {
offset_scaled = float( EPSILON);
offset_scaled2 = float(- EPSILON);
}
Polygons polygons;
for (LayerRegion *layerm : m_regions)
append(polygons, offset(to_expolygons(layerm->slices.surfaces), offset_scaled));
ExPolygons out = union_ex(polygons);
if (offset_scaled2 != 0.f)
out = offset_ex(out, offset_scaled2);
return out;
}
// Here the perimeters are created cummulatively for all layer regions sharing the same parameters influencing the perimeters.
// The perimeter paths and the thin fills (ExtrusionEntityCollection) are assigned to the first compatible layer region.
// The resulting fill surface is split back among the originating regions.
@ -86,13 +105,14 @@ void Layer::make_perimeters()
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id();
// keep track of regions whose perimeters we have already generated
std::set<size_t> done;
std::vector<unsigned char> done(m_regions.size(), false);
for (LayerRegionPtrs::iterator layerm = m_regions.begin(); layerm != m_regions.end(); ++ layerm) {
size_t region_id = layerm - m_regions.begin();
if (done.find(region_id) != done.end()) continue;
if (done[region_id])
continue;
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id;
done.insert(region_id);
done[region_id] = true;
const PrintRegionConfig &config = (*layerm)->region()->config();
// find compatible regions
@ -112,7 +132,7 @@ void Layer::make_perimeters()
&& config.thin_walls == other_config.thin_walls
&& config.external_perimeters_first == other_config.external_perimeters_first) {
layerms.push_back(other_layerm);
done.insert(it - m_regions.begin());
done[it - m_regions.begin()] = true;
}
}
@ -124,15 +144,13 @@ void Layer::make_perimeters()
SurfaceCollection new_slices;
{
// group slices (surfaces) according to number of extra perimeters
std::map<unsigned short,Surfaces> slices; // extra_perimeters => [ surface, surface... ]
for (LayerRegionPtrs::iterator l = layerms.begin(); l != layerms.end(); ++l) {
for (Surfaces::iterator s = (*l)->slices.surfaces.begin(); s != (*l)->slices.surfaces.end(); ++s) {
slices[s->extra_perimeters].push_back(*s);
}
}
std::map<unsigned short, Surfaces> slices; // extra_perimeters => [ surface, surface... ]
for (LayerRegion *layerm : layerms)
for (Surface &surface : layerm->slices.surfaces)
slices[surface.extra_perimeters].emplace_back(surface);
// merge the surfaces assigned to each group
for (std::map<unsigned short,Surfaces>::const_iterator it = slices.begin(); it != slices.end(); ++it)
new_slices.append(union_ex(it->second, true), it->second.front());
for (std::pair<const unsigned short,Surfaces> &surfaces_with_extra_perimeters : slices)
new_slices.append(union_ex(surfaces_with_extra_perimeters.second, true), surfaces_with_extra_perimeters.second.front());
}
// make perimeters

View file

@ -63,7 +63,12 @@ public:
void prepare_fill_surfaces();
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
void process_external_surfaces(const Layer* lower_layer);
double infill_area_threshold() const;
double infill_area_threshold() const;
// Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer.
void trim_surfaces(const Polygons &trimming_polygons);
// Single elephant foot compensation step, used by the elephant foor compensation at the 1st layer.
// Trim surfaces by trimming polygons (shrunk by an elephant foot compensation step), but don't shrink narrow parts so much that no perimeter would fit.
void elephant_foot_compensation_step(const float elephant_foot_compensation_perimeter_step, const Polygons &trimming_polygons);
void export_region_slices_to_svg(const char *path) const;
void export_region_fill_surfaces_to_svg(const char *path) const;
@ -117,7 +122,10 @@ public:
// Test whether whether there are any slices assigned to this layer.
bool empty() const;
void make_slices();
// Merge typed slices into untyped slices. This method is used to revert the effects of detect_surfaces_type() called for posPrepareInfill.
void merge_slices();
// Slices merged into islands, to be used by the elephant foot compensation to trim the individual surfaces with the shrunk merged slices.
ExPolygons merged(float offset) const;
template <class T> bool any_internal_region_slice_contains(const T &item) const {
for (const LayerRegion *layerm : m_regions) if (layerm->slices.any_internal_contains(item)) return true;
return false;

View file

@ -74,7 +74,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
// Cummulative sum of polygons over all the regions.
g.lower_slices = &this->layer()->lower_layer->slices;
g.layer_id = this->layer()->id();
g.layer_id = (int)this->layer()->id();
g.ext_perimeter_flow = this->flow(frExternalPerimeter);
g.overhang_flow = this->region()->flow(frPerimeter, -1, true, false, -1, *this->layer()->object());
g.solid_infill_flow = this->flow(frSolidInfill);
@ -385,6 +385,28 @@ double LayerRegion::infill_area_threshold() const
return ss*ss;
}
void LayerRegion::trim_surfaces(const Polygons &trimming_polygons)
{
#ifndef NDEBUG
for (const Surface &surface : this->slices.surfaces)
assert(surface.surface_type == stInternal);
#endif /* NDEBUG */
this->slices.set(intersection_ex(to_polygons(std::move(this->slices.surfaces)), trimming_polygons, false), stInternal);
}
void LayerRegion::elephant_foot_compensation_step(const float elephant_foot_compensation_perimeter_step, const Polygons &trimming_polygons)
{
#ifndef NDEBUG
for (const Surface &surface : this->slices.surfaces)
assert(surface.surface_type == stInternal);
#endif /* NDEBUG */
ExPolygons slices_expolygons = to_expolygons(std::move(this->slices.surfaces));
Polygons slices_polygons = to_polygons(slices_expolygons);
Polygons tmp = intersection(slices_polygons, trimming_polygons, false);
append(tmp, diff(slices_polygons, offset(offset_ex(slices_expolygons, -elephant_foot_compensation_perimeter_step), elephant_foot_compensation_perimeter_step)));
this->slices.set(std::move(union_ex(tmp)), stInternal);
}
void LayerRegion::export_region_slices_to_svg(const char *path) const
{
BoundingBox bbox;

View file

@ -592,6 +592,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
this->input_file = rhs.input_file;
this->config = rhs.config;
this->sla_support_points = rhs.sla_support_points;
this->sla_points_status = rhs.sla_points_status;
this->layer_height_ranges = rhs.layer_height_ranges;
this->layer_height_profile = rhs.layer_height_profile;
this->origin_translation = rhs.origin_translation;
@ -625,6 +626,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
this->input_file = std::move(rhs.input_file);
this->config = std::move(rhs.config);
this->sla_support_points = std::move(rhs.sla_support_points);
this->sla_points_status = std::move(rhs.sla_points_status);
this->layer_height_ranges = std::move(rhs.layer_height_ranges);
this->layer_height_profile = std::move(rhs.layer_height_profile);
this->origin_translation = std::move(rhs.origin_translation);
@ -1130,6 +1132,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
if (keep_upper) {
upper->set_model(nullptr);
upper->sla_support_points.clear();
upper->sla_points_status = sla::PointsStatus::None;
upper->clear_volumes();
upper->input_file = "";
}
@ -1137,6 +1140,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
if (keep_lower) {
lower->set_model(nullptr);
lower->sla_support_points.clear();
lower->sla_points_status = sla::PointsStatus::None;
lower->clear_volumes();
lower->input_file = "";
}

View file

@ -180,6 +180,9 @@ public:
// saved in mesh coordinates to allow using them for several instances.
// The format is (x, y, z, point_size, supports_island)
std::vector<sla::SupportPoint> sla_support_points;
// To keep track of where the points came from (used for synchronization between
// the SLA gizmo and the backend).
sla::PointsStatus sla_points_status = sla::PointsStatus::None;
/* This vector accumulates the total translation applied to the object by the
center_around_origin() method. Callers might want to apply the same translation

View file

@ -523,6 +523,9 @@ exit_for_rearrange_regions:
invalidated = true;
}
for (PrintObject *object : m_objects)
object->update_slicing_parameters();
return invalidated;
}
@ -1098,6 +1101,12 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
}
}
// Update SlicingParameters for each object where the SlicingParameters is not valid.
// If it is not valid, then it is ensured that PrintObject.m_slicing_params is not in use
// (posSlicing and posSupportMaterial was invalidated).
for (PrintObject *object : m_objects)
object->update_slicing_parameters();
#ifdef _DEBUG
check_model_ids_equal(m_model, model);
#endif /* _DEBUG */
@ -1202,13 +1211,13 @@ std::string Print::validate() const
break;
}
}
SlicingParameters slicing_params0 = m_objects.front()->slicing_parameters();
const SlicingParameters &slicing_params0 = m_objects.front()->slicing_parameters();
size_t tallest_object_idx = 0;
if (has_custom_layering)
PrintObject::update_layer_height_profile(*m_objects.front()->model_object(), slicing_params0, layer_height_profiles.front());
for (size_t i = 1; i < m_objects.size(); ++ i) {
const PrintObject *object = m_objects[i];
const SlicingParameters slicing_params = object->slicing_parameters();
const PrintObject *object = m_objects[i];
const SlicingParameters &slicing_params = object->slicing_parameters();
if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON)
return L("The Wipe Tower is only supported for multiple objects if they have equal layer heigths");

View file

@ -93,7 +93,7 @@ public:
const LayerPtrs& layers() const { return m_layers; }
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
const Transform3d& trafo() const { return m_trafo; }
const Points& copies() const { return m_copies; }
const Points& copies() const { return m_copies; }
// since the object is aligned to origin, bounding box coincides with size
BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); }
@ -131,7 +131,7 @@ public:
// by the interactive layer height editor and by the printing process itself.
// The slicing parameters are dependent on various configuration values
// (layer height, first layer height, raft settings, print nozzle diameter etc).
SlicingParameters slicing_parameters() const;
const SlicingParameters& slicing_parameters() const { return m_slicing_params; }
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object);
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
@ -161,6 +161,8 @@ protected:
bool invalidate_all_steps();
// Invalidate steps based on a set of parameters changed.
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
// If ! m_slicing_params.valid, recalculate.
void update_slicing_parameters();
static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders);
static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders);
@ -195,11 +197,13 @@ private:
// for external callers)
Point m_copies_shift;
SlicingParameters m_slicing_params;
LayerPtrs m_layers;
SupportLayerPtrs m_support_layers;
std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
std::vector<ExPolygons> _slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
std::vector<ExPolygons> _slice_volume(const std::vector<float> &z, const ModelVolume &volume) const;
};
struct WipeTowerData

View file

@ -59,6 +59,41 @@ void PrintConfigDef::init_common_params()
def->cli = "max-print-height=f";
def->mode = comAdvanced;
def->default_value = new ConfigOptionFloat(200.0);
def = this->add("slice_closing_radius", coFloat);
def->label = L("Slice gap closing radius");
def->category = L("Advanced");
def->tooltip = L("Cracks smaller than 2x gap closing radius are being filled during the triangle mesh slicing. "
"The gap closing operation may reduce the final print resolution, therefore it is advisable to keep the value reasonably low.");
def->sidetext = L("mm");
def->cli = "slice-closing-radius=f";
def->min = 0;
def->mode = comAdvanced;
def->default_value = new ConfigOptionFloat(0.049);
def = this->add("print_host", coString);
def->label = L("Hostname, IP or URL");
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
"the hostname, IP address or URL of the printer host instance.");
def->cli = "print-host=s";
def->mode = comAdvanced;
def->default_value = new ConfigOptionString("");
def = this->add("printhost_apikey", coString);
def->label = L("API Key / Password");
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
"the API Key or the password required for authentication.");
def->cli = "printhost-apikey=s";
def->mode = comAdvanced;
def->default_value = new ConfigOptionString("");
def = this->add("printhost_cafile", coString);
def->label = "HTTPS CA File";
def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. "
"If left blank, the default OS CA certificate repository is used.";
def->cli = "printhost-cafile=s";
def->mode = comAdvanced;
def->default_value = new ConfigOptionString("");
}
void PrintConfigDef::init_fff_params()
@ -1320,30 +1355,6 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->default_value = new ConfigOptionEnum<PrintHostType>(htOctoPrint);
def = this->add("printhost_apikey", coString);
def->label = L("API Key / Password");
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
"the API Key or the password required for authentication.");
def->cli = "printhost-apikey=s";
def->mode = comAdvanced;
def->default_value = new ConfigOptionString("");
def = this->add("printhost_cafile", coString);
def->label = "HTTPS CA File";
def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. "
"If left blank, the default OS CA certificate repository is used.";
def->cli = "printhost-cafile=s";
def->mode = comAdvanced;
def->default_value = new ConfigOptionString("");
def = this->add("print_host", coString);
def->label = L("Hostname, IP or URL");
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
"the hostname, IP address or URL of the printer host instance.");
def->cli = "print-host=s";
def->mode = comAdvanced;
def->default_value = new ConfigOptionString("");
def = this->add("only_retract_when_crossing_perimeters", coBool);
def->label = L("Only retract when crossing perimeters");
def->tooltip = L("Disables retraction when the travel path does not exceed the upper layer's perimeters "

View file

@ -38,7 +38,7 @@ enum GCodeFlavor {
};
enum PrintHostType {
htOctoPrint, htDuet, htSL1,
htOctoPrint, htDuet
};
enum InfillPattern {
@ -384,6 +384,7 @@ public:
ConfigOptionEnum<SeamPosition> seam_position;
// ConfigOptionFloat seam_preferred_direction;
// ConfigOptionFloat seam_preferred_direction_jitter;
ConfigOptionFloat slice_closing_radius;
ConfigOptionBool support_material;
// Automatic supports (generated based on support_material_threshold).
ConfigOptionBool support_material_auto;
@ -425,6 +426,7 @@ protected:
OPT_PTR(layer_height);
OPT_PTR(raft_layers);
OPT_PTR(seam_position);
OPT_PTR(slice_closing_radius);
// OPT_PTR(seam_preferred_direction);
// OPT_PTR(seam_preferred_direction_jitter);
OPT_PTR(support_material);
@ -963,6 +965,8 @@ public:
//Number of the layers needed for the exposure time fade [3;20]
ConfigOptionInt faded_layers /*= 10*/;
ConfigOptionFloat slice_closing_radius;
// Enabling or disabling support creation
ConfigOptionBool supports_enable;
@ -1036,6 +1040,7 @@ protected:
{
OPT_PTR(layer_height);
OPT_PTR(faded_layers);
OPT_PTR(slice_closing_radius);
OPT_PTR(supports_enable);
OPT_PTR(support_head_front_diameter);
OPT_PTR(support_head_penetration);

View file

@ -157,8 +157,6 @@ template<> class FilePrinter<FilePrinterFormat::SLA_PNGZIP>
"expTimeFirst = " + expt_first_str + "\n"
"numFade = " + cnt_fade_layers + "\n"
"layerHeight = " + layerh_str + "\n"
"expTime = "+expt_str+" + resinType = generic+layerHeight = "
+layerh_str+" + printer = DWARF3\n"
"usedMaterial = " + used_material + "\n"
"numSlow = " + cnt_slow_layers + "\n"
"numFast = " + cnt_fast_layers + "\n";

View file

@ -104,7 +104,7 @@ void PrintObject::slice()
return;
m_print->set_status(10, "Processing triangulated mesh");
std::vector<coordf_t> layer_height_profile;
this->update_layer_height_profile(*this->model_object(), this->slicing_parameters(), layer_height_profile);
this->update_layer_height_profile(*this->model_object(), m_slicing_params, layer_height_profile);
m_print->throw_if_canceled();
this->_slice(layer_height_profile);
m_print->throw_if_canceled();
@ -453,7 +453,8 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
} else if (
opt_key == "layer_height"
|| opt_key == "first_layer_height"
|| opt_key == "raft_layers") {
|| opt_key == "raft_layers"
|| opt_key == "slice_closing_radius") {
steps.emplace_back(posSlice);
}
else if (
@ -567,8 +568,11 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
} else if (step == posSlice) {
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posSupportMaterial });
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
} else if (step == posSupportMaterial)
this->m_slicing_params.valid = false;
} else if (step == posSupportMaterial) {
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
this->m_slicing_params.valid = false;
}
// Wipe tower depends on the ordering of extruders, which in turn depends on everything.
// It also decides about what the wipe_into_infill / wipe_into_object features will do,
@ -806,7 +810,7 @@ void PrintObject::process_external_surfaces()
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled();
// BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z;
m_layers[layer_idx]->get_region(region_id)->process_external_surfaces((layer_idx == 0) ? NULL : m_layers[layer_idx - 1]);
m_layers[layer_idx]->get_region((int)region_id)->process_external_surfaces((layer_idx == 0) ? NULL : m_layers[layer_idx - 1]);
}
}
);
@ -1359,11 +1363,11 @@ PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegion
return config;
}
SlicingParameters PrintObject::slicing_parameters() const
void PrintObject::update_slicing_parameters()
{
return SlicingParameters::create_from_config(
this->print()->config(), m_config,
unscale<double>(this->size(2)), this->object_extruders());
if (! m_slicing_params.valid)
m_slicing_params = SlicingParameters::create_from_config(
this->print()->config(), m_config, unscale<double>(this->size(2)), this->object_extruders());
}
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object)
@ -1450,23 +1454,21 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
tbb_init = new tbb::task_scheduler_init(1);
#endif
SlicingParameters slicing_params = this->slicing_parameters();
// 1) Initialize layers and their slice heights.
std::vector<float> slice_zs;
{
this->clear_layers();
// Object layers (pairs of bottom/top Z coordinate), without the raft.
std::vector<coordf_t> object_layers = generate_object_layers(slicing_params, layer_height_profile);
std::vector<coordf_t> object_layers = generate_object_layers(m_slicing_params, layer_height_profile);
// Reserve object layers for the raft. Last layer of the raft is the contact layer.
int id = int(slicing_params.raft_layers());
int id = int(m_slicing_params.raft_layers());
slice_zs.reserve(object_layers.size());
Layer *prev = nullptr;
for (size_t i_layer = 0; i_layer < object_layers.size(); i_layer += 2) {
coordf_t lo = object_layers[i_layer];
coordf_t hi = object_layers[i_layer + 1];
coordf_t slice_z = 0.5 * (lo + hi);
Layer *layer = this->add_layer(id ++, hi - lo, hi + slicing_params.object_print_z_min, slice_z);
Layer *layer = this->add_layer(id ++, hi - lo, hi + m_slicing_params.object_print_z_min, slice_z);
slice_zs.push_back(float(slice_z));
if (prev != nullptr) {
prev->upper_layer = layer;
@ -1478,46 +1480,151 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
prev = layer;
}
}
// Count model parts and modifier meshes, check whether the model parts are of the same region.
int single_volume_region = -2; // not set yet
size_t num_volumes = 0;
size_t num_modifiers = 0;
std::vector<int> map_volume_to_region(this->model_object()->volumes.size());
for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) {
for (int volume_id : this->region_volumes[region_id]) {
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_model_part()) {
map_volume_to_region[volume_id] = region_id;
if (single_volume_region == -2)
// first model volume met
single_volume_region = region_id;
else if (single_volume_region != region_id)
// multiple volumes met and they are not equal
single_volume_region = -1;
++ num_volumes;
} else if (model_volume->is_modifier())
++ num_modifiers;
}
}
assert(num_volumes > 0);
// Slice all non-modifier volumes.
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start";
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
m_layers[layer_id]->regions()[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " end";
bool clipped = false;
bool upscaled = false;
if (! m_config.clip_multipart_objects.value || single_volume_region >= 0) {
// Cheap path: Slice regions without mutual clipping.
// The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region.
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
// slicing in parallel
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start";
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
m_layers[layer_id]->regions()[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " end";
}
} else {
// Expensive path: Slice one volume after the other in the order they are presented at the user interface,
// clip the last volumes with the first.
// First slice the volumes.
struct SlicedVolume {
SlicedVolume(int volume_id, int region_id, std::vector<ExPolygons> &&expolygons_by_layer) :
volume_id(volume_id), region_id(region_id), expolygons_by_layer(std::move(expolygons_by_layer)) {}
int volume_id;
int region_id;
std::vector<ExPolygons> expolygons_by_layer;
};
std::vector<SlicedVolume> sliced_volumes;
sliced_volumes.reserve(num_volumes);
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
for (int volume_id : this->region_volumes[region_id]) {
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_model_part()) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id;
// slicing in parallel
sliced_volumes.emplace_back(volume_id, map_volume_to_region[volume_id], this->_slice_volume(slice_zs, *model_volume));
}
}
// Second clip the volumes in the order they are presented at the user interface.
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - start";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, slice_zs.size()),
[this, &sliced_volumes, num_modifiers](const tbb::blocked_range<size_t>& range) {
float delta = float(scale_(m_config.xy_size_compensation.value));
// Only upscale together with clipping if there are no modifiers, as the modifiers shall be applied before upscaling
// (upscaling may grow the object outside of the modifier mesh).
bool upscale = delta > 0 && num_modifiers == 0;
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
m_print->throw_if_canceled();
// Trim volumes in a single layer, one by the other, possibly apply upscaling.
{
Polygons processed;
for (SlicedVolume &sliced_volume : sliced_volumes) {
ExPolygons slices = std::move(sliced_volume.expolygons_by_layer[layer_id]);
if (upscale)
slices = offset_ex(std::move(slices), delta);
if (! processed.empty())
// Trim by the slices of already processed regions.
slices = diff_ex(to_polygons(std::move(slices)), processed);
if (size_t(&sliced_volume - &sliced_volumes.front()) + 1 < sliced_volumes.size())
// Collect the already processed regions to trim the to be processed regions.
polygons_append(processed, slices);
sliced_volume.expolygons_by_layer[layer_id] = std::move(slices);
}
}
// Collect and union volumes of a single region.
for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) {
ExPolygons expolygons;
size_t num_volumes = 0;
for (SlicedVolume &sliced_volume : sliced_volumes)
if (sliced_volume.region_id == region_id && ! sliced_volume.expolygons_by_layer[layer_id].empty()) {
++ num_volumes;
append(expolygons, std::move(sliced_volume.expolygons_by_layer[layer_id]));
}
if (num_volumes > 1)
// Merge the islands using a positive / negative offset.
expolygons = offset_ex(offset_ex(expolygons, float(scale_(EPSILON))), -float(scale_(EPSILON)));
m_layers[layer_id]->regions()[region_id]->slices.append(std::move(expolygons), stInternal);
}
}
});
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - end";
clipped = true;
upscaled = m_config.xy_size_compensation.value > 0 && num_modifiers == 0;
}
// Slice all modifier volumes.
if (this->region_volumes.size() > 1) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id;
// slicing in parallel
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, true);
m_print->throw_if_canceled();
if (expolygons_by_layer.empty())
continue;
// loop through the other regions and 'steal' the slices belonging to this one
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " start";
for (size_t other_region_id = 0; other_region_id < this->region_volumes.size(); ++ other_region_id) {
if (region_id == other_region_id)
continue;
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) {
Layer *layer = m_layers[layer_id];
LayerRegion *layerm = layer->m_regions[region_id];
LayerRegion *other_layerm = layer->m_regions[other_region_id];
if (layerm == nullptr || other_layerm == nullptr)
continue;
Polygons other_slices = to_polygons(other_layerm->slices);
ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
if (my_parts.empty())
continue;
// Remove such parts from original region.
other_layerm->slices.set(diff_ex(other_slices, to_polygons(my_parts)), stInternal);
// Append new parts to our region.
layerm->slices.append(std::move(my_parts), stInternal);
}
}
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
[this, &expolygons_by_layer, region_id](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
for (size_t other_region_id = 0; other_region_id < this->region_volumes.size(); ++ other_region_id) {
if (region_id == other_region_id)
continue;
Layer *layer = m_layers[layer_id];
LayerRegion *layerm = layer->m_regions[region_id];
LayerRegion *other_layerm = layer->m_regions[other_region_id];
if (layerm == nullptr || other_layerm == nullptr)
continue;
Polygons other_slices = to_polygons(other_layerm->slices);
ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
if (my_parts.empty())
continue;
// Remove such parts from original region.
other_layerm->slices.set(diff_ex(other_slices, to_polygons(my_parts)), stInternal);
// Append new parts to our region.
layerm->slices.append(std::move(my_parts), stInternal);
}
}
});
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " end";
}
@ -1540,38 +1647,94 @@ end:
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - begin";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
[this](const tbb::blocked_range<size_t>& range) {
[this, upscaled, clipped](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
m_print->throw_if_canceled();
Layer *layer = m_layers[layer_id];
// Apply size compensation and perform clipping of multi-part objects.
float delta = float(scale_(m_config.xy_size_compensation.value));
float elephant_foot_compensation = 0.f;
if (layer_id == 0)
delta -= float(scale_(m_config.elefant_foot_compensation.value));
bool scale = delta != 0.f;
bool clip = m_config.clip_multipart_objects.value || delta > 0.f;
elephant_foot_compensation = float(scale_(m_config.elefant_foot_compensation.value));
if (layer->m_regions.size() == 1) {
if (scale) {
// Optimized version for a single region layer.
if (layer_id == 0) {
if (delta > elephant_foot_compensation) {
delta -= elephant_foot_compensation;
elephant_foot_compensation = 0.f;
} else if (delta > 0)
elephant_foot_compensation -= delta;
}
if (delta != 0.f || elephant_foot_compensation > 0.f) {
// Single region, growing or shrinking.
LayerRegion *layerm = layer->m_regions.front();
layerm->slices.set(offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta), stInternal);
// Apply the XY compensation.
ExPolygons expolygons = (delta == 0.f) ?
to_expolygons(std::move(layerm->slices.surfaces)) :
offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta);
// Apply the elephant foot compensation.
if (elephant_foot_compensation > 0) {
float elephant_foot_spacing = float(layerm->flow(frExternalPerimeter).scaled_elephant_foot_spacing());
float external_perimeter_nozzle = float(scale_(this->print()->config().nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder.value - 1)));
// Apply the elephant foot compensation by steps of 1/10 nozzle diameter.
float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle));
size_t nsteps = size_t(steps);
float step = elephant_foot_compensation / steps;
for (size_t i = 0; i < nsteps; ++ i) {
Polygons tmp = offset(expolygons, - step);
append(tmp, diff(to_polygons(expolygons), offset(offset_ex(expolygons, -elephant_foot_spacing - step), elephant_foot_spacing + step)));
expolygons = union_ex(tmp);
}
}
layerm->slices.set(std::move(expolygons), stInternal);
}
} else if (scale || clip) {
// Multiple regions, growing, shrinking or just clipping one region by the other.
// When clipping the regions, priority is given to the first regions.
Polygons processed;
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) {
LayerRegion *layerm = layer->m_regions[region_id];
ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces));
if (scale)
slices = offset_ex(slices, delta);
if (region_id > 0 && clip)
// Trim by the slices of already processed regions.
slices = diff_ex(to_polygons(std::move(slices)), processed);
if (clip && region_id + 1 < layer->m_regions.size())
// Collect the already processed regions to trim the to be processed regions.
polygons_append(processed, slices);
layerm->slices.set(std::move(slices), stInternal);
} else {
bool upscale = ! upscaled && delta > 0.f;
bool clip = ! clipped && m_config.clip_multipart_objects.value;
if (upscale || clip) {
// Multiple regions, growing or just clipping one region by the other.
// When clipping the regions, priority is given to the first regions.
Polygons processed;
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) {
LayerRegion *layerm = layer->m_regions[region_id];
ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces));
if (upscale)
slices = offset_ex(std::move(slices), delta);
if (region_id > 0 && clip)
// Trim by the slices of already processed regions.
slices = diff_ex(to_polygons(std::move(slices)), processed);
if (clip && (region_id + 1 < layer->m_regions.size()))
// Collect the already processed regions to trim the to be processed regions.
polygons_append(processed, slices);
layerm->slices.set(std::move(slices), stInternal);
}
}
if (delta < 0.f) {
// Apply the negative XY compensation.
Polygons trimming = offset(layer->merged(float(EPSILON)), delta - float(EPSILON));
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id)
layer->m_regions[region_id]->trim_surfaces(trimming);
}
if (elephant_foot_compensation > 0.f) {
// Apply the elephant foot compensation.
std::vector<float> elephant_foot_spacing;
elephant_foot_spacing.reserve(layer->m_regions.size());
float external_perimeter_nozzle = 0.f;
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) {
LayerRegion *layerm = layer->m_regions[region_id];
elephant_foot_spacing.emplace_back(float(layerm->flow(frExternalPerimeter).scaled_elephant_foot_spacing()));
external_perimeter_nozzle += float(scale_(this->print()->config().nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder.value - 1)));
}
external_perimeter_nozzle /= (float)layer->m_regions.size();
// Apply the elephant foot compensation by steps of 1/10 nozzle diameter.
float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle));
size_t nsteps = size_t(steps);
float step = elephant_foot_compensation / steps;
for (size_t i = 0; i < nsteps; ++ i) {
Polygons trimming_polygons = offset(layer->merged(float(EPSILON)), - step - float(EPSILON));
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id)
layer->m_regions[region_id]->elephant_foot_compensation_step(elephant_foot_spacing[region_id] + step, trimming_polygons);
}
}
}
// Merge all regions' slices to get islands, chain them by a shortest path.
@ -1643,13 +1806,35 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
mslicer.init(&mesh, callback);
mslicer.slice(z, &layers, callback);
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled();
}
}
return layers;
}
std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z, const ModelVolume &volume) const
{
std::vector<ExPolygons> layers;
// Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh(volume.mesh);
mesh.transform(volume.get_matrix());
if (mesh.stl.stats.number_of_facets > 0) {
mesh.transform(m_trafo);
// apply XY shift
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
// perform actual slicing
TriangleMeshSlicer mslicer;
const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
mslicer.init(&mesh, callback);
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled();
}
return layers;
}
std::string PrintObject::_fix_slicing_errors()
{
// Collect layers with slicing errors.
@ -2245,7 +2430,7 @@ void PrintObject::combine_infill()
void PrintObject::_generate_support_material()
{
PrintObjectSupportMaterial support_material(this, PrintObject::slicing_parameters());
PrintObjectSupportMaterial support_material(this, m_slicing_params);
support_material.generate(*this);
}

View file

@ -144,6 +144,8 @@ static std::vector<SLAAutoSupports::MyLayer> make_layers(
const float layer_height = (layer_id!=0 ? heights[layer_id]-heights[layer_id-1] : heights[0]);
const float safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports
const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle)));
const float slope_angle = 75.f * (float(M_PI)/180.f); // smaller number - less supports
const float slope_offset = float(scale_(layer_height / std::tan(slope_angle)));
//FIXME This has a quadratic time complexity, it will be excessively slow for many tiny islands.
for (SLAAutoSupports::Structure &top : layer_above.islands) {
for (SLAAutoSupports::Structure &bottom : layer_below.islands) {
@ -173,6 +175,7 @@ static std::vector<SLAAutoSupports::MyLayer> make_layers(
overhangs_sorted.emplace_back(std::move(*p.first));
top.overhangs = std::move(overhangs_sorted);
top.overhangs_area *= float(SCALING_FACTOR * SCALING_FACTOR);
top.overhangs_slopes = diff_ex(top_polygons, offset(bottom_polygons, slope_offset));
top.dangling_areas = diff_ex(top_polygons, offset(bottom_polygons, between_layers_offset));
}
}
@ -241,9 +244,9 @@ void SLAAutoSupports::process(const std::vector<ExPolygons>& slices, const std::
// What we now have in polygons needs support, regardless of what the forces are, so we can add them.
//FIXME is it an island point or not? Vojtech thinks it is.
uniformly_cover(s.dangling_areas, s, point_grid);
} else if (! s.overhangs.empty()) {
} else if (! s.overhangs_slopes.empty()) {
//FIXME add the support force deficit as a parameter, only cover until the defficiency is covered.
uniformly_cover(s.overhangs, s, point_grid);
uniformly_cover(s.overhangs_slopes, s, point_grid);
}
}

View file

@ -65,8 +65,12 @@ public:
std::vector<Link> islands_above;
std::vector<Link> islands_below;
#endif
// Overhangs, that are dangling considerably.
ExPolygons dangling_areas;
// Complete overhands.
ExPolygons overhangs;
// Overhangs, where the surface must slope.
ExPolygons overhangs_slopes;
float overhangs_area;
bool overlaps(const Structure &rhs) const {

View file

@ -561,7 +561,7 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
heights.emplace_back(hi);
std::vector<ExPolygons> out; out.reserve(size_t(std::ceil(h/layerh)));
slicer.slice(heights, &out, thrfn);
slicer.slice(heights, 0.f, &out, thrfn);
size_t count = 0; for(auto& o : out) count += o.size();

View file

@ -16,6 +16,14 @@ class TriangleMesh;
namespace sla {
// An enum to keep track of where the current points on the ModelObject came from.
enum class PointsStatus {
None, // No points were generated so far.
Generating, // The autogeneration algorithm triggered, but not yet finished.
AutoGenerated, // Points were autogenerated (i.e. copied from the backend).
UserModified // User has done some edits.
};
struct SupportPoint {
Vec3f pos;
float head_front_radius;

View file

@ -2129,7 +2129,7 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
fullmesh.merge(get_pad());
TriangleMeshSlicer slicer(&fullmesh);
SlicedSupports ret;
slicer.slice(heights, &ret, get().ctl().cancelfn);
slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn);
return ret;
}

View file

@ -337,11 +337,34 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
}
}
}
if (model_object.sla_support_points != model_object_new.sla_support_points) {
/*if (model_object.sla_support_points != model_object_new.sla_support_points) {
model_object.sla_support_points = model_object_new.sla_support_points;
if (it_print_object_status != print_object_status.end())
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
}
if (model_object.sla_points_status != model_object_new.sla_points_status) {
// Change of this status should invalidate support points. The points themselves are not enough, there are none
// in case that nothing was generated OR that points were autogenerated already and not copied to the front-end.
// These cases can only be differentiated by checking the status change. However, changing from 'Generating' should NOT
// invalidate - that would keep stopping the background processing without a reason.
if (model_object.sla_points_status != sla::PointsStatus::Generating)
if (it_print_object_status != print_object_status.end())
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
model_object.sla_points_status = model_object_new.sla_points_status;
}*/
bool old_user_modified = model_object.sla_points_status == sla::PointsStatus::UserModified;
bool new_user_modified = model_object_new.sla_points_status == sla::PointsStatus::UserModified;
if ((old_user_modified && ! new_user_modified) || // switching to automatic supports from manual supports
(! old_user_modified && new_user_modified) || // switching to manual supports from automatic supports
(new_user_modified && model_object.sla_support_points != model_object_new.sla_support_points)) {
if (it_print_object_status != print_object_status.end())
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
model_object.sla_points_status = model_object_new.sla_points_status;
model_object.sla_support_points = model_object_new.sla_support_points;
}
// Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step.
model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file;
@ -613,7 +636,7 @@ void SLAPrint::process()
ilh, float(lh));
auto& layers = po.m_model_slices; layers.clear();
slicer.slice(heights, &layers, [this](){ throw_if_canceled(); });
slicer.slice(heights, float(po.config().slice_closing_radius.value), &layers, [this](){ throw_if_canceled(); });
};
// In this step we check the slices, identify island and cover them with
@ -629,10 +652,11 @@ void SLAPrint::process()
BOOST_LOG_TRIVIAL(debug) << "Support point count "
<< mo.sla_support_points.size();
// If there are no points on the front-end, we will do the
// autoplacement. Otherwise we will just blindly copy the frontend data
// Unless the user modified the points or we already did the calculation, we will do
// the autoplacement. Otherwise we will just blindly copy the frontend data
// into the backend cache.
if(mo.sla_support_points.empty()) {
if (mo.sla_points_status != sla::PointsStatus::UserModified) {
// calculate heights of slices (slices are calculated already)
double lh = po.m_config.layer_height.getFloat();
@ -644,7 +668,9 @@ void SLAPrint::process()
this->throw_if_canceled();
SLAAutoSupports::Config config;
const SLAPrintObjectConfig& cfg = po.config();
config.density_relative = float(cfg.support_points_density_relative / 100.f); // the config value is in percents
// the density config value is in percents:
config.density_relative = float(cfg.support_points_density_relative / 100.f);
config.minimal_distance = float(cfg.support_points_minimal_distance);
// Construction of this object does the calculation.
@ -668,7 +694,7 @@ void SLAPrint::process()
report_status(*this, -1, L("Generating support points"), SlicingStatus::RELOAD_SLA_SUPPORT_POINTS);
}
else {
// There are some points on the front-end, no calculation will be done.
// There are either some points on the front-end, or the user removed them on purpose. No calculation will be done.
po.m_supportdata->support_points = po.transformed_support_points();
}
};
@ -1341,7 +1367,8 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf
bool invalidated = false;
for (const t_config_option_key &opt_key : opt_keys) {
if ( opt_key == "layer_height"
|| opt_key == "faded_layers") {
|| opt_key == "faded_layers"
|| opt_key == "slice_closing_radius") {
steps.emplace_back(slaposObjectSlice);
} else if (
opt_key == "supports_enable"

View file

@ -149,6 +149,7 @@ SlicingParameters SlicingParameters::create_from_config(
params.object_print_z_max += print_z;
}
params.valid = true;
return params;
}

View file

@ -42,6 +42,8 @@ struct SlicingParameters
// Height of the object to be printed. This value does not contain the raft height.
coordf_t object_print_z_height() const { return object_print_z_max - object_print_z_min; }
bool valid;
// Number of raft layers.
size_t base_raft_layers;
// Number of interface layers including the contact layer.
@ -100,6 +102,8 @@ static_assert(IsTriviallyCopyable<SlicingParameters>::value, "SlicingParameters
// The two slicing parameters lead to the same layering as long as the variable layer thickness is not in action.
inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters &sp2)
{
assert(sp1.valid);
assert(sp2.valid);
return sp1.base_raft_layers == sp2.base_raft_layers &&
sp1.interface_raft_layers == sp2.interface_raft_layers &&
sp1.base_raft_layer_height == sp2.base_raft_layer_height &&

View file

@ -42,15 +42,6 @@
#define ENABLE_VOLUMES_CENTERING_FIXES (1 && ENABLE_1_42_0_ALPHA4)
//====================
// 1.42.0.alpha5 techs
//====================
#define ENABLE_1_42_0_ALPHA5 1
// Toolbar items hidden/shown in dependence of the user mode
#define ENABLE_MODE_AWARE_TOOLBAR_ITEMS (1 && ENABLE_1_42_0_ALPHA5)
//====================
// 1.42.0.alpha7 techs
//====================
@ -59,4 +50,13 @@
// Printbed textures generated from svg files
#define ENABLE_TEXTURES_FROM_SVG (1 && ENABLE_1_42_0_ALPHA7)
//====================
// 1.42.0.alpha8 techs
//====================
#define ENABLE_1_42_0_ALPHA8 1
// Toolbars and Gizmos use icons imported from svg files
#define ENABLE_SVG_ICONS (1 && ENABLE_1_42_0_ALPHA8 && ENABLE_TEXTURES_FROM_SVG)
#endif // _technologies_h_

View file

@ -852,7 +852,7 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
}
}
void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
void TriangleMeshSlicer::slice(const std::vector<float> &z, const float closing_radius, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
{
std::vector<Polygons> layers_p;
this->slice(z, &layers_p, throw_on_cancel);
@ -861,13 +861,13 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygo
layers->resize(z.size());
tbb::parallel_for(
tbb::blocked_range<size_t>(0, z.size()),
[&layers_p, layers, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) {
[&layers_p, closing_radius, layers, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]);
#endif
throw_on_cancel();
this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]);
this->make_expolygons(layers_p[layer_id], closing_radius, &(*layers)[layer_id]);
}
});
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end";
@ -1600,7 +1600,7 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &l
#endif
}
void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const
void TriangleMeshSlicer::make_expolygons(const Polygons &loops, const float closing_radius, ExPolygons* slices) const
{
/*
Input loops are not suitable for evenodd nor nonzero fill types, as we might get
@ -1655,7 +1655,7 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic
// 0.0499 comes from https://github.com/slic3r/Slic3r/issues/959
// double safety_offset = scale_(0.0499);
// 0.0001 is set to satisfy GH #520, #1029, #1364
double safety_offset = scale_(0.0001);
double safety_offset = scale_(closing_radius);
/* The following line is commented out because it can generate wrong polygons,
see for example issue #661 */
@ -1670,17 +1670,17 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic
#endif
// append to the supplied collection
/* Fix for issue #661 { */
expolygons_append(*slices, offset2_ex(union_(loops, false), +safety_offset, -safety_offset));
//expolygons_append(*slices, ex_slices);
/* } */
if (safety_offset > 0)
expolygons_append(*slices, offset2_ex(union_(loops, false), +safety_offset, -safety_offset));
else
expolygons_append(*slices, union_ex(loops, false));
}
void TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
void TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, const float closing_radius, ExPolygons* slices) const
{
Polygons pp;
this->make_loops(lines, &pp);
this->make_expolygons(pp, slices);
this->make_expolygons(pp, closing_radius, slices);
}
void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const

View file

@ -165,7 +165,7 @@ public:
TriangleMeshSlicer(TriangleMesh* mesh) { this->init(mesh, [](){}); }
void init(TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel);
void slice(const std::vector<float> &z, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
void slice(const std::vector<float> &z, const float closing_radius, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
enum FacetSliceType {
NoSlice = 0,
Slicing = 1,
@ -184,9 +184,9 @@ private:
void _slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex, const std::vector<float> &z) const;
void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const;
void make_expolygons(const Polygons &loops, ExPolygons* slices) const;
void make_expolygons(const Polygons &loops, const float closing_radius, ExPolygons* slices) const;
void make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const;
void make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const;
void make_expolygons(std::vector<IntersectionLine> &lines, const float closing_radius, ExPolygons* slices) const;
};
TriangleMesh make_cube(double x, double y, double z);

View file

@ -516,9 +516,9 @@ void Bed3D::render_prusa(const std::string &key, bool bottom) const
if (max_anisotropy > 0.0f)
{
::glBindTexture(GL_TEXTURE_2D, m_texture.get_id());
::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
::glBindTexture(GL_TEXTURE_2D, 0);
glsafe(::glBindTexture(GL_TEXTURE_2D, m_texture.get_id()));
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
}
}
@ -542,9 +542,9 @@ void Bed3D::render_prusa(const std::string &key, bool bottom) const
if (!m_model.get_filename().empty())
{
::glEnable(GL_LIGHTING);
glsafe(::glEnable(GL_LIGHTING));
m_model.render();
::glDisable(GL_LIGHTING);
glsafe(::glDisable(GL_LIGHTING));
}
}
@ -553,39 +553,32 @@ void Bed3D::render_prusa(const std::string &key, bool bottom) const
{
if (m_vbo_id == 0)
{
::glGenBuffers(1, &m_vbo_id);
::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id);
::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW);
::glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_position_offset());
::glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_tex_coords_offset());
::glBindBuffer(GL_ARRAY_BUFFER, 0);
glsafe(::glGenBuffers(1, &m_vbo_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
::glEnable(GL_DEPTH_TEST);
::glDepthMask(GL_FALSE);
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDepthMask(GL_FALSE));
::glEnable(GL_BLEND);
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
::glEnable(GL_TEXTURE_2D);
::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
if (bottom)
::glFrontFace(GL_CW);
glsafe(::glFrontFace(GL_CW));
render_prusa_shader(triangles_vcount, bottom);
render_prusa_shader(bottom);
if (bottom)
::glFrontFace(GL_CCW);
glsafe(::glFrontFace(GL_CCW));
::glDisable(GL_TEXTURE_2D);
::glDisable(GL_BLEND);
::glDepthMask(GL_TRUE);
glsafe(::glDisable(GL_BLEND));
glsafe(::glDepthMask(GL_TRUE));
}
}
void Bed3D::render_prusa_shader(unsigned int vertices_count, bool transparent) const
void Bed3D::render_prusa_shader(bool transparent) const
{
if (m_shader.get_shader_program_id() == 0)
m_shader.init("printbed.vs", "printbed.fs");
@ -595,15 +588,35 @@ void Bed3D::render_prusa_shader(unsigned int vertices_count, bool transparent) c
m_shader.start_using();
m_shader.set_uniform("transparent_background", transparent);
::glBindTexture(GL_TEXTURE_2D, (GLuint)m_texture.get_id());
::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id);
::glEnableVertexAttribArray(0);
::glEnableVertexAttribArray(1);
::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertices_count);
::glDisableVertexAttribArray(1);
::glDisableVertexAttribArray(0);
::glBindBuffer(GL_ARRAY_BUFFER, 0);
::glBindTexture(GL_TEXTURE_2D, 0);
unsigned int stride = m_triangles.get_vertex_data_size();
GLint position_id = m_shader.get_attrib_location("v_position");
GLint tex_coords_id = m_shader.get_attrib_location("v_tex_coords");
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_texture.get_id()));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
if (position_id != -1)
{
glsafe(::glEnableVertexAttribArray(position_id));
glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)m_triangles.get_position_offset()));
}
if (tex_coords_id != -1)
{
glsafe(::glEnableVertexAttribArray(tex_coords_id));
glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)m_triangles.get_tex_coords_offset()));
}
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_triangles.get_vertices_count()));
if (tex_coords_id != -1)
glsafe(::glDisableVertexAttribArray(tex_coords_id));
if (position_id != -1)
glsafe(::glDisableVertexAttribArray(position_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
m_shader.stop_using();
}
@ -754,7 +767,7 @@ void Bed3D::render_custom() const
glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f));
glsafe(::glNormal3d(0.0f, 0.0f, 1.0f));
#if ENABLE_TEXTURES_FROM_SVG
::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data());
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data()));
#else
glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()));
#endif // ENABLE_TEXTURES_FROM_SVG
@ -768,7 +781,7 @@ void Bed3D::render_custom() const
glsafe(::glLineWidth(3.0f * m_scale_factor));
glsafe(::glColor4f(0.2f, 0.2f, 0.2f, 0.4f));
#if ENABLE_TEXTURES_FROM_SVG
::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data());
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data()));
#else
glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_vertices()));
#endif // ENABLE_TEXTURES_FROM_SVG
@ -786,7 +799,7 @@ void Bed3D::reset()
{
if (m_vbo_id > 0)
{
::glDeleteBuffers(1, &m_vbo_id);
glsafe(::glDeleteBuffers(1, &m_vbo_id));
m_vbo_id = 0;
}
}

View file

@ -131,7 +131,7 @@ private:
EType detect_type(const Pointfs& shape) const;
#if ENABLE_TEXTURES_FROM_SVG
void render_prusa(const std::string& key, bool bottom) const;
void render_prusa_shader(unsigned int vertices_count, bool transparent) const;
void render_prusa_shader(bool transparent) const;
#else
void render_prusa(const std::string &key, float theta, bool useVBOs) const;
#endif // ENABLE_TEXTURES_FROM_SVG

View file

@ -92,8 +92,8 @@ AboutDialog::AboutDialog()
"<html>"
"<body bgcolor= %s link= %s>"
"<font color=%s>"
"Copyright &copy; 2016-2018 Prusa Research. <br />"
"Copyright &copy; 2011-2017 Alessandro Ranellucci. <br />"
"Copyright &copy; 2016-2019 Prusa Research. <br />"
"Copyright &copy; 2011-2018 Alessandro Ranellucci. <br />"
"<a href=\"http://slic3r.org/\">Slic3r</a> is licensed under the "
"<a href=\"http://www.gnu.org/licenses/agpl-3.0.html\">GNU Affero General Public License, version 3</a>."
"<br /><br />"

View file

@ -1004,7 +1004,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason)
p->btn_prev = new wxButton(this, wxID_ANY, _(L("< &Back")));
p->btn_next = new wxButton(this, wxID_ANY, _(L("&Next >")));
p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish")));
p->btn_cancel = new wxButton(this, wxID_CANCEL);
p->btn_cancel = new wxButton(this, wxID_CANCEL, _(L("Cancel"))); // Note: The label needs to be present, otherwise we get accelerator bugs on Mac
p->btnsizer->AddStretchSpacer();
p->btnsizer->Add(p->btn_prev, 0, wxLEFT, BTN_SPACING);
p->btnsizer->Add(p->btn_next, 0, wxLEFT, BTN_SPACING);

View file

@ -464,8 +464,14 @@ void SpinCtrl::BUILD() {
// # As a workaround, we get the new value from $event->GetString and store
// # here temporarily so that we can return it from $self->get_value
std::string value = e.GetString().utf8_str().data();
if (is_matched(value, "^\\-?\\d+$"))
tmp_value = std::stoi(value);
if (is_matched(value, "^\\-?\\d+$")) {
try {
tmp_value = std::stoi(value);
}
catch (const std::exception & /* e */) {
tmp_value = -9999;
}
}
else tmp_value = -9999;
#ifdef __WXOSX__
propagate_value();

View file

@ -122,7 +122,6 @@ struct FirmwareDialog::priv
// This is a shared pointer holding the background AvrDude task
// also serves as a status indication (it is set _iff_ the background task is running, otherwise it is reset).
AvrDude::Ptr avrdude;
std::string avrdude_config;
unsigned progress_tasks_done;
unsigned progress_tasks_bar;
bool user_cancelled;
@ -134,7 +133,6 @@ struct FirmwareDialog::priv
btn_flash_label_flashing(_(L("Cancel"))),
label_status_flashing(_(L("Flashing in progress. Please do not disconnect the printer!"))),
timer_pulse(q),
avrdude_config((fs::path(::Slic3r::resources_dir()) / "avrdude" / "avrdude.conf").string()),
progress_tasks_done(0),
progress_tasks_bar(0),
user_cancelled(false),
@ -553,7 +551,7 @@ void FirmwareDialog::priv::perform_upload()
flashing_start(hex_file.device == HexFile::DEV_MK3 ? 2 : 1);
// Init the avrdude object
AvrDude avrdude(avrdude_config);
AvrDude avrdude;
// 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.
@ -722,7 +720,7 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
panel->SetSizer(vsizer);
auto *label_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:")));
p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY, wxEmptyString, wxFileSelectorPromptStr,
p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY, wxEmptyString, wxFileSelectorPromptStr,
"Hex files (*.hex)|*.hex|All files|*.*");
auto *label_port_picker = new wxStaticText(panel, wxID_ANY, _(L("Serial port:")));
@ -770,7 +768,7 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
// Experience says it needs to be 1, otherwise things won't get sized properly.
vsizer->Add(p->spoiler, 1, wxEXPAND | wxBOTTOM, SPACING);
p->btn_close = new wxButton(panel, wxID_CLOSE);
p->btn_close = new wxButton(panel, wxID_CLOSE, _(L("Close"))); // Note: The label needs to be present, otherwise we get accelerator bugs on Mac
p->btn_flash = new wxButton(panel, wxID_ANY, p->btn_flash_label_ready);
p->btn_flash->Disable();
auto *bsizer = new wxBoxSizer(wxHORIZONTAL);

File diff suppressed because it is too large Load diff

View file

@ -375,6 +375,59 @@ class GLCanvas3D
};
public:
class TransformationType
{
public:
enum Enum {
// Transforming in a world coordinate system
World = 0,
// Transforming in a local coordinate system
Local = 1,
// Absolute transformations, allowed in local coordinate system only.
Absolute = 0,
// Relative transformations, allowed in both local and world coordinate system.
Relative = 2,
// For group selection, the transformation is performed as if the group made a single solid body.
Joint = 0,
// For group selection, the transformation is performed on each object independently.
Independent = 4,
World_Relative_Joint = World | Relative | Joint,
World_Relative_Independent = World | Relative | Independent,
Local_Absolute_Joint = Local | Absolute | Joint,
Local_Absolute_Independent = Local | Absolute | Independent,
Local_Relative_Joint = Local | Relative | Joint,
Local_Relative_Independent = Local | Relative | Independent,
};
TransformationType() : m_value(World) {}
TransformationType(Enum value) : m_value(value) {}
TransformationType& operator=(Enum value) { m_value = value; return *this; }
Enum operator()() const { return m_value; }
bool has(Enum v) const { return ((unsigned int)m_value & (unsigned int)v) != 0; }
void set_world() { this->remove(Local); }
void set_local() { this->add(Local); }
void set_absolute() { this->remove(Relative); }
void set_relative() { this->add(Relative); }
void set_joint() { this->remove(Independent); }
void set_independent() { this->add(Independent); }
bool world() const { return ! this->has(Local); }
bool local() const { return this->has(Local); }
bool absolute() const { return ! this->has(Relative); }
bool relative() const { return this->has(Relative); }
bool joint() const { return ! this->has(Independent); }
bool independent() const { return this->has(Independent); }
private:
void add(Enum v) { m_value = Enum((unsigned int)m_value | (unsigned int)v); }
void remove(Enum v) { m_value = Enum((unsigned int)m_value & (~(unsigned int)v)); }
Enum m_value;
};
class Selection
{
public:
@ -415,6 +468,7 @@ public:
Transform3d rotation_matrix;
Transform3d scale_matrix;
Transform3d mirror_matrix;
Transform3d full_matrix;
TransformCache();
explicit TransformCache(const Geometry::Transformation& transform);
@ -434,6 +488,7 @@ public:
const Transform3d& get_volume_rotation_matrix() const { return m_volume.rotation_matrix; }
const Transform3d& get_volume_scale_matrix() const { return m_volume.scale_matrix; }
const Transform3d& get_volume_mirror_matrix() const { return m_volume.mirror_matrix; }
const Transform3d& get_volume_full_matrix() const { return m_volume.full_matrix; }
const Vec3d& get_instance_position() const { return m_instance.position; }
const Vec3d& get_instance_rotation() const { return m_instance.rotation; }
@ -442,6 +497,7 @@ public:
const Transform3d& get_instance_rotation_matrix() const { return m_instance.rotation_matrix; }
const Transform3d& get_instance_scale_matrix() const { return m_instance.scale_matrix; }
const Transform3d& get_instance_mirror_matrix() const { return m_instance.mirror_matrix; }
const Transform3d& get_instance_full_matrix() const { return m_instance.full_matrix; }
};
typedef std::map<unsigned int, VolumeCache> VolumesCache;
@ -553,7 +609,7 @@ public:
void start_dragging();
void translate(const Vec3d& displacement, bool local = false);
void rotate(const Vec3d& rotation, bool local);
void rotate(const Vec3d& rotation, TransformationType transformation_type);
void flattening_rotate(const Vec3d& normal);
void scale(const Vec3d& scale, bool local);
void mirror(Axis axis);
@ -635,6 +691,10 @@ private:
class Gizmos
{
public:
#if ENABLE_SVG_ICONS
static const float Default_Icons_Size;
#endif // ENABLE_SVG_ICONS
enum EType : unsigned char
{
Undefined,
@ -651,10 +711,21 @@ private:
bool m_enabled;
typedef std::map<EType, GLGizmoBase*> GizmosMap;
GizmosMap m_gizmos;
#if ENABLE_SVG_ICONS
mutable GLTexture m_icons_texture;
mutable bool m_icons_texture_dirty;
#else
ItemsIconsTexture m_icons_texture;
#endif // ENABLE_SVG_ICONS
BackgroundTexture m_background_texture;
EType m_current;
#if ENABLE_SVG_ICONS
float m_overlay_icons_size;
float m_overlay_scale;
#else
float m_overlay_icons_scale;
#endif // ENABLE_SVG_ICONS
float m_overlay_border;
float m_overlay_gap_y;
@ -667,6 +738,9 @@ private:
bool is_enabled() const;
void set_enabled(bool enable);
#if ENABLE_SVG_ICONS
void set_overlay_icon_size(float size);
#endif // ENABLE_SVG_ICONS
void set_overlay_scale(float scale);
std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection);
@ -716,15 +790,19 @@ private:
#endif // not ENABLE_IMGUI
private:
void _reset();
void reset();
void _render_overlay(const GLCanvas3D& canvas, const Selection& selection) const;
void _render_current_gizmo(const Selection& selection) const;
void do_render_overlay(const GLCanvas3D& canvas, const Selection& selection) const;
void do_render_current_gizmo(const Selection& selection) const;
float _get_total_overlay_height() const;
float _get_total_overlay_width() const;
float get_total_overlay_height() const;
float get_total_overlay_width() const;
GLGizmoBase* _get_current() const;
GLGizmoBase* get_current() const;
#if ENABLE_SVG_ICONS
bool generate_icons_texture() const;
#endif // ENABLE_SVG_ICONS
};
struct SlaCap
@ -793,7 +871,7 @@ private:
void fill_color_print_legend_values(const GCodePreviewData& preview_data, const GLCanvas3D& canvas,
std::vector<std::pair<double, double>>& cp_legend_values);
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas, bool use_error_colors);
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas);
void render(const GLCanvas3D& canvas) const;
};
@ -931,9 +1009,7 @@ public:
void update_volumes_colors_by_extruder();
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
void update_toolbar_items_visibility();
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
#if !ENABLE_IMGUI
Rect get_gizmo_reset_rect(const GLCanvas3D& canvas, bool viewport) const;
@ -1095,7 +1171,9 @@ private:
bool _is_any_volume_outside() const;
#if !ENABLE_SVG_ICONS
void _resize_toolbars() const;
#endif // !ENABLE_SVG_ICONS
static std::vector<float> _parse_colors(const std::vector<std::string>& colors);

View file

@ -25,6 +25,8 @@
#include "GUI.hpp"
#include "GUI_Utils.hpp"
#include "GUI_App.hpp"
#include "GUI_ObjectSettings.hpp"
#include "GUI_ObjectList.hpp"
#include "I18N.hpp"
#include "PresetBundle.hpp"
@ -157,11 +159,19 @@ void GLGizmoBase::Grabber::render_face(float half_size) const
::glEnd();
}
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent)
#if ENABLE_SVG_ICONS
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
#else
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, unsigned int sprite_id)
#endif // ENABLE_SVG_ICONS
: m_parent(parent)
, m_group_id(-1)
, m_state(Off)
, m_shortcut_key(0)
#if ENABLE_SVG_ICONS
, m_icon_filename(icon_filename)
#endif // ENABLE_SVG_ICONS
, m_sprite_id(sprite_id)
, m_hover_id(-1)
, m_dragging(false)
#if ENABLE_IMGUI
@ -304,7 +314,11 @@ const unsigned int GLGizmoRotate::SnapRegionsCount = 8;
const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius
GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis)
: GLGizmoBase(parent)
#if ENABLE_SVG_ICONS
: GLGizmoBase(parent, "", -1)
#else
: GLGizmoBase(parent, -1)
#endif // ENABLE_SVG_ICONS
, m_axis(axis)
, m_angle(0.0)
, m_quadric(nullptr)
@ -321,7 +335,11 @@ GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis)
}
GLGizmoRotate::GLGizmoRotate(const GLGizmoRotate& other)
: GLGizmoBase(other.m_parent)
#if ENABLE_SVG_ICONS
: GLGizmoBase(other.m_parent, other.m_icon_filename, other.m_sprite_id)
#else
: GLGizmoBase(other.m_parent, other.m_sprite_id)
#endif // ENABLE_SVG_ICONS
, m_axis(other.m_axis)
, m_angle(other.m_angle)
, m_quadric(nullptr)
@ -628,7 +646,7 @@ void GLGizmoRotate::transform_to_local(const GLCanvas3D::Selection& selection) c
{
::glTranslated(m_center(0), m_center(1), m_center(2));
if (selection.is_single_volume() || selection.is_single_modifier())
if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes())
{
Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true);
::glMultMatrixd(orient_matrix.data());
@ -685,7 +703,7 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, cons
}
}
if (selection.is_single_volume() || selection.is_single_modifier())
if (selection.is_single_volume() || selection.is_single_modifier() || selection.requires_local_axes())
m = m * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true).inverse();
m.translate(-m_center);
@ -693,8 +711,13 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, cons
return transform(mouse_ray, m).intersect_plane(0.0);
}
GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent)
: GLGizmoBase(parent)
#if ENABLE_SVG_ICONS
GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
{
m_gizmos.emplace_back(parent, GLGizmoRotate::X);
m_gizmos.emplace_back(parent, GLGizmoRotate::Y);
@ -719,17 +742,6 @@ bool GLGizmoRotate3D::on_init()
m_gizmos[i].set_highlight_color(AXES_COLOR[i]);
}
std::string path = resources_dir() + "/icons/overlay/";
if (!m_textures[Off].load_from_file(path + "rotate_off.png", false))
return false;
if (!m_textures[Hover].load_from_file(path + "rotate_hover.png", false))
return false;
if (!m_textures[On].load_from_file(path + "rotate_on.png", false))
return false;
m_shortcut_key = WXK_CONTROL_R;
return true;
@ -767,7 +779,7 @@ void GLGizmoRotate3D::on_render(const GLCanvas3D::Selection& selection) const
}
#if ENABLE_IMGUI
void GLGizmoRotate3D::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection)
void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection)
{
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
Vec3d rotation(Geometry::rad2deg(m_gizmos[0].get_angle()), Geometry::rad2deg(m_gizmos[1].get_angle()), Geometry::rad2deg(m_gizmos[2].get_angle()));
@ -784,8 +796,13 @@ void GLGizmoRotate3D::on_render_input_window(float x, float y, const GLCanvas3D:
const float GLGizmoScale3D::Offset = 5.0f;
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent)
: GLGizmoBase(parent)
#if ENABLE_SVG_ICONS
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_scale(Vec3d::Ones())
, m_snap_step(0.05)
, m_starting_scale(Vec3d::Ones())
@ -794,17 +811,6 @@ GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent)
bool GLGizmoScale3D::on_init()
{
std::string path = resources_dir() + "/icons/overlay/";
if (!m_textures[Off].load_from_file(path + "scale_off.png", false))
return false;
if (!m_textures[Hover].load_from_file(path + "scale_hover.png", false))
return false;
if (!m_textures[On].load_from_file(path + "scale_on.png", false))
return false;
for (int i = 0; i < 10; ++i)
{
m_grabbers.push_back(Grabber());
@ -975,7 +981,7 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const
::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f);
float grabber_max_size = (float)std::max(grabber_size(0), std::max(grabber_size(1), grabber_size(2)));
float grabber_mean_size = (float)(grabber_size(0) + grabber_size(1) + grabber_size(2)) / 3.0f;
if (m_hover_id == -1)
{
@ -1001,7 +1007,7 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const
render_grabbers_connection(8, 9);
render_grabbers_connection(9, 6);
// draw grabbers
render_grabbers(grabber_max_size);
render_grabbers(grabber_mean_size);
}
else if ((m_hover_id == 0) || (m_hover_id == 1))
{
@ -1009,8 +1015,8 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const
::glColor3fv(m_grabbers[0].color);
render_grabbers_connection(0, 1);
// draw grabbers
m_grabbers[0].render(true, grabber_max_size);
m_grabbers[1].render(true, grabber_max_size);
m_grabbers[0].render(true, grabber_mean_size);
m_grabbers[1].render(true, grabber_mean_size);
}
else if ((m_hover_id == 2) || (m_hover_id == 3))
{
@ -1018,8 +1024,8 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const
::glColor3fv(m_grabbers[2].color);
render_grabbers_connection(2, 3);
// draw grabbers
m_grabbers[2].render(true, grabber_max_size);
m_grabbers[3].render(true, grabber_max_size);
m_grabbers[2].render(true, grabber_mean_size);
m_grabbers[3].render(true, grabber_mean_size);
}
else if ((m_hover_id == 4) || (m_hover_id == 5))
{
@ -1027,8 +1033,8 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const
::glColor3fv(m_grabbers[4].color);
render_grabbers_connection(4, 5);
// draw grabbers
m_grabbers[4].render(true, grabber_max_size);
m_grabbers[5].render(true, grabber_max_size);
m_grabbers[4].render(true, grabber_mean_size);
m_grabbers[5].render(true, grabber_mean_size);
}
else if (m_hover_id >= 6)
{
@ -1041,7 +1047,7 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const
// draw grabbers
for (int i = 6; i < 10; ++i)
{
m_grabbers[i].render(true, grabber_max_size);
m_grabbers[i].render(true, grabber_mean_size);
}
}
}
@ -1054,7 +1060,7 @@ void GLGizmoScale3D::on_render_for_picking(const GLCanvas3D::Selection& selectio
}
#if ENABLE_IMGUI
void GLGizmoScale3D::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection)
void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection)
{
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
bool single_instance = selection.is_single_full_instance();
@ -1141,8 +1147,13 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const
const double GLGizmoMove3D::Offset = 10.0;
GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent)
: GLGizmoBase(parent)
#if ENABLE_SVG_ICONS
GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_displacement(Vec3d::Zero())
, m_snap_step(1.0)
, m_starting_drag_position(Vec3d::Zero())
@ -1163,17 +1174,6 @@ GLGizmoMove3D::~GLGizmoMove3D()
bool GLGizmoMove3D::on_init()
{
std::string path = resources_dir() + "/icons/overlay/";
if (!m_textures[Off].load_from_file(path + "move_off.png", false))
return false;
if (!m_textures[Hover].load_from_file(path + "move_hover.png", false))
return false;
if (!m_textures[On].load_from_file(path + "move_on.png", false))
return false;
for (int i = 0; i < 3; ++i)
{
m_grabbers.push_back(Grabber());
@ -1305,7 +1305,7 @@ void GLGizmoMove3D::on_render_for_picking(const GLCanvas3D::Selection& selection
}
#if ENABLE_IMGUI
void GLGizmoMove3D::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection)
void GLGizmoMove3D::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection)
{
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
bool show_position = selection.is_single_full_instance();
@ -1389,8 +1389,13 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box
::glDisable(GL_LIGHTING);
}
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent)
: GLGizmoBase(parent)
#if ENABLE_SVG_ICONS
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_normal(Vec3d::Zero())
, m_starting_center(Vec3d::Zero())
{
@ -1398,19 +1403,7 @@ GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent)
bool GLGizmoFlatten::on_init()
{
std::string path = resources_dir() + "/icons/overlay/";
if (!m_textures[Off].load_from_file(path + "layflat_off.png", false))
return false;
if (!m_textures[Hover].load_from_file(path + "layflat_hover.png", false))
return false;
if (!m_textures[On].load_from_file(path + "layflat_on.png", false))
return false;
m_shortcut_key = WXK_CONTROL_F;
return true;
}
@ -1740,8 +1733,14 @@ Vec3d GLGizmoFlatten::get_flattening_normal() const
return out;
}
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent)
: GLGizmoBase(parent), m_starting_center(Vec3d::Zero()), m_quadric(nullptr)
#if ENABLE_SVG_ICONS
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_starting_center(Vec3d::Zero()), m_quadric(nullptr)
{
m_quadric = ::gluNewQuadric();
if (m_quadric != nullptr)
@ -1758,19 +1757,7 @@ GLGizmoSlaSupports::~GLGizmoSlaSupports()
bool GLGizmoSlaSupports::on_init()
{
std::string path = resources_dir() + "/icons/overlay/";
if (!m_textures[Off].load_from_file(path + "sla_support_points_off.png", false))
return false;
if (!m_textures[Hover].load_from_file(path + "sla_support_points_hover.png", false))
return false;
if (!m_textures[On].load_from_file(path + "sla_support_points_on.png", false))
return false;
m_shortcut_key = WXK_CONTROL_L;
return true;
}
@ -1789,12 +1776,12 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const G
if (is_mesh_update_necessary())
update_mesh();
// If there are no points, let's ask the backend if it calculated some.
if (m_editing_mode_cache.empty())
get_data_from_backend();
if (m_model_object != m_old_model_object)
m_editing_mode = false;
if (m_editing_mode_cache.empty() && m_model_object->sla_points_status != sla::PointsStatus::UserModified)
get_data_from_backend();
if (m_state == On) {
m_parent.toggle_model_objects_visibility(false);
m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance);
@ -1937,7 +1924,7 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b
bool GLGizmoSlaSupports::is_mesh_update_necessary() const
{
return (m_state == On) && (m_model_object != nullptr) && (m_model_object != m_old_model_object) && !m_model_object->instances.empty();
return (m_state == On) && (m_model_object != m_old_model_object) && (m_model_object != nullptr) && !m_model_object->instances.empty();
//if (m_state != On || !m_model_object || m_model_object->instances.empty() || ! m_instance_matrix.isApprox(m_source_data.matrix))
// return false;
@ -2176,13 +2163,10 @@ bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mous
return false;
}
void GLGizmoSlaSupports::delete_selected_points()
void GLGizmoSlaSupports::delete_selected_points(bool force)
{
if (!m_editing_mode)
return;
for (unsigned int idx=0; idx<m_editing_mode_cache.size(); ++idx) {
if (m_editing_mode_cache[idx].second && (!m_editing_mode_cache[idx].first.is_new_island || !m_lock_unique_islands)) {
if (m_editing_mode_cache[idx].second && (!m_editing_mode_cache[idx].first.is_new_island || !m_lock_unique_islands || force)) {
m_editing_mode_cache.erase(m_editing_mode_cache.begin() + (idx--));
m_unsaved_changes = true;
}
@ -2243,20 +2227,59 @@ void GLGizmoSlaSupports::render_tooltip_texture() const {
#endif // not ENABLE_IMGUI
#if ENABLE_IMGUI
void GLGizmoSlaSupports::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection)
std::vector<ConfigOption*> GLGizmoSlaSupports::get_config_options(const std::vector<std::string>& keys) const
{
std::vector<ConfigOption*> out;
if (!m_model_object)
return out;
DynamicPrintConfig& object_cfg = m_model_object->config;
DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
std::unique_ptr<DynamicPrintConfig> default_cfg = nullptr;
for (const std::string& key : keys) {
if (object_cfg.has(key))
out.push_back(object_cfg.option(key));
else
if (print_cfg.has(key))
out.push_back(print_cfg.option(key));
else { // we must get it from defaults
if (default_cfg == nullptr)
default_cfg.reset(DynamicPrintConfig::new_from_defaults_keys(keys));
out.push_back(default_cfg->option(key));
}
}
return out;
}
#if ENABLE_IMGUI
void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection)
{
if (!m_model_object)
return;
bool first_run = true; // This is a hack to redraw the button when all points are removed,
// so it is not delayed until the background process finishes.
RENDER_AGAIN:
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
const float scaling = m_imgui->get_style_scaling();
const ImVec2 window_size(285.f * scaling, 260.f * scaling);
ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) ));
ImGui::SetNextWindowSize(ImVec2(window_size));
m_imgui->set_next_window_bg_alpha(0.5f);
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove |/* ImGuiWindowFlags_NoResize | */ImGuiWindowFlags_NoCollapse);
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
ImGui::PushItemWidth(100.0f);
bool force_refresh = false;
bool remove_selected = false;
bool remove_all = false;
if (m_editing_mode) {
m_imgui->text(_(L("Left mouse click - add point")));
@ -2264,16 +2287,20 @@ RENDER_AGAIN:
m_imgui->text(_(L("Shift + Left (+ drag) - select point(s)")));
m_imgui->text(" "); // vertical gap
std::vector<wxString> options = {"0.2", "0.4", "0.6", "0.8", "1.0"};
std::stringstream ss;
ss << std::setprecision(1) << m_new_point_head_diameter;
wxString str = ss.str();
static const std::vector<float> options = {0.2f, 0.4f, 0.6f, 0.8f, 1.0f};
static const std::vector<std::string> options_str = {"0.2", "0.4", "0.6", "0.8", "1.0"};
int selection = -1;
for (size_t i = 0; i < options.size(); i++) {
if (options[i] == m_new_point_head_diameter) { selection = (int)i; }
}
bool old_combo_state = m_combo_box_open;
m_combo_box_open = m_imgui->combo(_(L("Head diameter")), options, str);
// The combo is commented out for now, until the feature is supported by backend.
// m_combo_box_open = m_imgui->combo(_(L("Head diameter")), options_str, selection);
force_refresh |= (old_combo_state != m_combo_box_open);
float current_number = atof(str);
// float current_number = atof(str);
const float current_number = selection < options.size() ? options[selection] : m_new_point_head_diameter;
if (old_combo_state && !m_combo_box_open) // closing the combo must always change the sizes (even if the selection did not change)
for (auto& point_and_selection : m_editing_mode_cache)
if (point_and_selection.second) {
@ -2294,10 +2321,13 @@ RENDER_AGAIN:
remove_selected = m_imgui->button(_(L("Remove selected points")));
m_imgui->disabled_end();
m_imgui->disabled_begin(m_editing_mode_cache.empty());
remove_all = m_imgui->button(_(L("Remove all points")));
m_imgui->disabled_end();
m_imgui->text(" "); // vertical gap
bool apply_changes = m_imgui->button(_(L("Apply changes")));
if (apply_changes) {
if (m_imgui->button(_(L("Apply changes")))) {
editing_mode_apply_changes();
force_refresh = true;
}
@ -2308,24 +2338,52 @@ RENDER_AGAIN:
force_refresh = true;
}
}
else {
/* ImGui::PushItemWidth(50.0f);
else { // not in editing mode:
ImGui::PushItemWidth(100.0f);
m_imgui->text(_(L("Minimal points distance: ")));
ImGui::SameLine();
bool value_changed = ImGui::InputDouble("mm", &m_minimal_point_distance, 0.0f, 0.0f, "%.2f");
std::vector<ConfigOption*> opts = get_config_options({"support_points_density_relative", "support_points_minimal_distance"});
float density = static_cast<ConfigOptionInt*>(opts[0])->value;
float minimal_point_distance = static_cast<ConfigOptionFloat*>(opts[1])->value;
bool value_changed = ImGui::SliderFloat("", &minimal_point_distance, 0.f, 20.f, "%.f mm");
if (value_changed)
m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
m_imgui->text(_(L("Support points density: ")));
ImGui::SameLine();
value_changed |= ImGui::InputDouble("%", &m_density, 0.0f, 0.0f, "%.f");*/
if (ImGui::SliderFloat(" ", &density, 0.f, 200.f, "%.f %%")) {
value_changed = true;
m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
}
if (value_changed) { // Update side panel
wxTheApp->CallAfter([]() {
wxGetApp().obj_settings()->UpdateAndShow(true);
wxGetApp().obj_list()->update_settings_items();
});
}
bool generate = m_imgui->button(_(L("Auto-generate points [A]")));
if (generate)
auto_generate();
m_imgui->text("");
m_imgui->text("");
if (m_imgui->button(_(L("Manual editing [M]"))))
switch_to_editing_mode();
m_imgui->disabled_begin(m_editing_mode_cache.empty());
remove_all = m_imgui->button(_(L("Remove all points")));
m_imgui->disabled_end();
m_imgui->text("");
m_imgui->text(m_model_object->sla_points_status == sla::PointsStatus::None ? "No points (will be autogenerated)" :
(m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated ? "Autogenerated points (no modifications)" :
(m_model_object->sla_points_status == sla::PointsStatus::UserModified ? "User-modified points" :
(m_model_object->sla_points_status == sla::PointsStatus::Generating ? "Generation in progress..." : "UNKNOWN STATUS"))));
}
m_imgui->end();
@ -2336,10 +2394,14 @@ RENDER_AGAIN:
}
m_old_editing_state = m_editing_mode;
if (remove_selected) {
if (remove_selected || remove_all) {
force_refresh = false;
m_parent.reload_scene(true);
delete_selected_points();
m_parent.set_as_dirty();
if (remove_all)
select_point(AllPoints);
delete_selected_points(remove_all);
if (remove_all && !m_editing_mode)
editing_mode_apply_changes();
if (first_run) {
first_run = false;
goto RENDER_AGAIN;
@ -2347,7 +2409,7 @@ RENDER_AGAIN:
}
if (force_refresh)
m_parent.reload_scene(true);
m_parent.set_as_dirty();
}
#endif // ENABLE_IMGUI
@ -2387,19 +2449,22 @@ void GLGizmoSlaSupports::on_set_state()
m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance);
}
if (m_state == Off) {
if (m_old_state != Off && m_model_object) { // the gizmo was just turned Off
if (m_old_state != Off) { // the gizmo was just turned Off
if (m_unsaved_changes) {
wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L("Do you want to save your manually edited support points ?\n")),
_(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
if (dlg.ShowModal() == wxID_YES)
editing_mode_apply_changes();
else
editing_mode_discard_changes();
if (m_model_object) {
if (m_unsaved_changes) {
wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L("Do you want to save your manually edited support points ?\n")),
_(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
if (dlg.ShowModal() == wxID_YES)
editing_mode_apply_changes();
else
editing_mode_discard_changes();
}
}
m_parent.toggle_model_objects_visibility(true);
m_editing_mode = false; // so it is not active next time the gizmo opens
m_editing_mode_cache.clear();
}
}
m_old_state = m_state;
@ -2448,16 +2513,18 @@ void GLGizmoSlaSupports::editing_mode_apply_changes()
// If there are no changes, don't touch the front-end. The data in the cache could have been
// taken from the backend and copying them to ModelObject would needlessly invalidate them.
if (m_unsaved_changes) {
m_model_object->sla_points_status = sla::PointsStatus::UserModified;
m_model_object->sla_support_points.clear();
for (const std::pair<sla::SupportPoint, bool>& point_and_selection : m_editing_mode_cache)
m_model_object->sla_support_points.push_back(point_and_selection.first);
// Recalculate support structures once the editing mode is left.
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
wxGetApp().plater()->reslice_SLA_supports(*m_model_object);
}
m_editing_mode = false;
m_unsaved_changes = false;
// Recalculate support structures once the editing mode is left.
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
wxGetApp().plater()->reslice_SLA_supports(*m_model_object);
}
@ -2467,6 +2534,7 @@ void GLGizmoSlaSupports::editing_mode_reload_cache()
m_editing_mode_cache.clear();
for (const sla::SupportPoint& point : m_model_object->sla_support_points)
m_editing_mode_cache.push_back(std::make_pair(point, false));
m_unsaved_changes = false;
}
@ -2476,10 +2544,15 @@ void GLGizmoSlaSupports::get_data_from_backend()
{
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
if (po->model_object()->id() == m_model_object->id() && po->is_step_done(slaposSupportPoints)) {
m_editing_mode_cache.clear();
const std::vector<sla::SupportPoint>& points = po->get_support_points();
auto mat = po->trafo().inverse().cast<float>();
for (unsigned int i=0; i<points.size();++i)
m_editing_mode_cache.emplace_back(sla::SupportPoint(mat * points[i].pos, points[i].head_front_radius, points[i].is_new_island), false);
if (m_model_object->sla_points_status != sla::PointsStatus::UserModified)
m_model_object->sla_points_status = sla::PointsStatus::AutoGenerated;
break;
}
}
@ -2497,8 +2570,9 @@ void GLGizmoSlaSupports::auto_generate()
"Are you sure you want to do it?\n"
)), _(L("Warning")), wxICON_WARNING | wxYES | wxNO);
if (m_model_object->sla_support_points.empty() || dlg.ShowModal() == wxID_YES) {
if (m_model_object->sla_points_status != sla::PointsStatus::UserModified || m_editing_mode_cache.empty() || dlg.ShowModal() == wxID_YES) {
m_model_object->sla_support_points.clear();
m_model_object->sla_points_status = sla::PointsStatus::Generating;
m_editing_mode_cache.clear();
wxGetApp().plater()->reslice_SLA_supports(*m_model_object);
}
@ -2508,7 +2582,9 @@ void GLGizmoSlaSupports::auto_generate()
void GLGizmoSlaSupports::switch_to_editing_mode()
{
editing_mode_reload_cache();
if (m_model_object->sla_points_status != sla::PointsStatus::AutoGenerated)
editing_mode_reload_cache();
m_unsaved_changes = false;
m_editing_mode = true;
}
@ -2564,8 +2640,13 @@ const double GLGizmoCut::Offset = 10.0;
const double GLGizmoCut::Margin = 20.0;
const std::array<float, 3> GLGizmoCut::GrabberColor = { 1.0, 0.5, 0.0 };
GLGizmoCut::GLGizmoCut(GLCanvas3D& parent)
: GLGizmoBase(parent)
#if ENABLE_SVG_ICONS
GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_cut_z(0.0)
, m_max_z(0.0)
#if !ENABLE_IMGUI
@ -2598,26 +2679,8 @@ void GLGizmoCut::create_external_gizmo_widgets(wxWindow *parent)
bool GLGizmoCut::on_init()
{
// TODO: icon
std::string path = resources_dir() + "/icons/overlay/";
if (!m_textures[Off].load_from_file(path + "cut_off.png", false)) {
return false;
}
if (!m_textures[Hover].load_from_file(path + "cut_hover.png", false)) {
return false;
}
if (!m_textures[On].load_from_file(path + "cut_on.png", false)) {
return false;
}
m_grabbers.emplace_back();
m_shortcut_key = WXK_CONTROL_C;
return true;
}
@ -2724,7 +2787,7 @@ void GLGizmoCut::on_render_for_picking(const GLCanvas3D::Selection& selection) c
}
#if ENABLE_IMGUI
void GLGizmoCut::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection)
void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection)
{
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
m_imgui->set_next_window_bg_alpha(0.5f);

View file

@ -87,8 +87,10 @@ protected:
int m_group_id;
EState m_state;
int m_shortcut_key;
// textures are assumed to be square and all with the same size in pixels, no internal check is done
GLTexture m_textures[Num_States];
#if ENABLE_SVG_ICONS
std::string m_icon_filename;
#endif // ENABLE_SVG_ICONS
unsigned int m_sprite_id;
int m_hover_id;
bool m_dragging;
float m_base_color[3];
@ -100,7 +102,11 @@ protected:
#endif // ENABLE_IMGUI
public:
explicit GLGizmoBase(GLCanvas3D& parent);
#if ENABLE_SVG_ICONS
GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoBase(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoBase() {}
bool init() { return on_init(); }
@ -116,11 +122,14 @@ public:
int get_shortcut_key() const { return m_shortcut_key; }
void set_shortcut_key(int key) { m_shortcut_key = key; }
#if ENABLE_SVG_ICONS
const std::string& get_icon_filename() const { return m_icon_filename; }
#endif // ENABLE_SVG_ICONS
bool is_activable(const GLCanvas3D::Selection& selection) const { return on_is_activable(selection); }
bool is_selectable() const { return on_is_selectable(); }
unsigned int get_texture_id() const { return m_textures[m_state].get_id(); }
int get_textures_size() const { return m_textures[Off].get_width(); }
unsigned int get_sprite_id() const { return m_sprite_id; }
int get_hover_id() const { return m_hover_id; }
void set_hover_id(int id);
@ -144,7 +153,7 @@ public:
#endif // not ENABLE_IMGUI
#if ENABLE_IMGUI
void render_input_window(float x, float y, const GLCanvas3D::Selection& selection) { on_render_input_window(x, y, selection); }
void render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) { on_render_input_window(x, y, bottom_limit, selection); }
#endif // ENABLE_IMGUI
protected:
@ -163,7 +172,7 @@ protected:
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const = 0;
#if ENABLE_IMGUI
virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) {}
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) {}
#endif // ENABLE_IMGUI
float picking_color_component(unsigned int id) const;
@ -244,7 +253,11 @@ class GLGizmoRotate3D : public GLGizmoBase
std::vector<GLGizmoRotate> m_gizmos;
public:
explicit GLGizmoRotate3D(GLCanvas3D& parent);
#if ENABLE_SVG_ICONS
GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); }
void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); }
@ -296,7 +309,7 @@ protected:
}
#if ENABLE_IMGUI
virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection);
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection);
#endif // ENABLE_IMGUI
};
@ -315,7 +328,11 @@ class GLGizmoScale3D : public GLGizmoBase
BoundingBoxf3 m_starting_box;
public:
explicit GLGizmoScale3D(GLCanvas3D& parent);
#if ENABLE_SVG_ICONS
GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
double get_snap_step(double step) const { return m_snap_step; }
void set_snap_step(double step) { m_snap_step = step; }
@ -333,7 +350,7 @@ protected:
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
#if ENABLE_IMGUI
virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection);
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection);
#endif // ENABLE_IMGUI
private:
@ -362,7 +379,11 @@ class GLGizmoMove3D : public GLGizmoBase
GLUquadricObj* m_quadric;
public:
explicit GLGizmoMove3D(GLCanvas3D& parent);
#if ENABLE_SVG_ICONS
GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoMove3D();
double get_snap_step(double step) const { return m_snap_step; }
@ -380,7 +401,7 @@ protected:
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
#if ENABLE_IMGUI
virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection);
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection);
#endif // ENABLE_IMGUI
private:
@ -417,7 +438,11 @@ private:
bool is_plane_update_necessary() const;
public:
explicit GLGizmoFlatten(GLCanvas3D& parent);
#if ENABLE_SVG_ICONS
GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
void set_flattening_data(const ModelObject* model_object);
Vec3d get_flattening_normal() const;
@ -437,7 +462,7 @@ protected:
}
};
#define SLAGIZMO_IMGUI_MODAL 0
class GLGizmoSlaSupports : public GLGizmoBase
{
@ -465,11 +490,15 @@ private:
mutable Vec3d m_starting_center;
public:
explicit GLGizmoSlaSupports(GLCanvas3D& parent);
#if ENABLE_SVG_ICONS
GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoSlaSupports();
void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection);
bool mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down);
void delete_selected_points();
void delete_selected_points(bool force = false);
private:
bool on_init();
@ -489,24 +518,26 @@ private:
#endif // not ENABLE_IMGUI
bool m_lock_unique_islands = false;
bool m_editing_mode = false;
bool m_old_editing_state = false;
float m_new_point_head_diameter = 0.4f;
double m_minimal_point_distance = 20.;
double m_density = 100.;
bool m_editing_mode = false; // Is editing mode active?
bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes).
float m_new_point_head_diameter = 0.4f; // Size of a new point.
float m_minimal_point_distance = 20.f;
float m_density = 100.f;
std::vector<std::pair<sla::SupportPoint, bool>> m_editing_mode_cache; // a support point and whether it is currently selected
bool m_selection_rectangle_active = false;
Vec2d m_selection_rectangle_start_corner;
Vec2d m_selection_rectangle_end_corner;
bool m_ignore_up_event = false;
bool m_combo_box_open = false;
bool m_unsaved_changes = false;
bool m_combo_box_open = false; // To ensure proper rendering of the imgui combobox.
bool m_unsaved_changes = false; // Are there unsaved changes in manual mode?
bool m_selection_empty = true;
EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
int m_canvas_width;
int m_canvas_height;
std::vector<ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
// Methods that do the model_object and editing cache synchronization,
// editing mode selection, etc:
enum {
@ -526,7 +557,7 @@ protected:
void on_start_dragging(const GLCanvas3D::Selection& selection) override;
#if ENABLE_IMGUI
virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) override;
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection) override;
#endif // ENABLE_IMGUI
virtual std::string on_get_name() const;
@ -558,7 +589,11 @@ class GLGizmoCut : public GLGizmoBase
#endif // not ENABLE_IMGUI
public:
explicit GLGizmoCut(GLCanvas3D& parent);
#if ENABLE_SVG_ICONS
GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
#if !ENABLE_IMGUI
virtual void create_external_gizmo_widgets(wxWindow *parent);
@ -577,7 +612,7 @@ protected:
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
#if ENABLE_IMGUI
virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection);
virtual void on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection);
#endif // ENABLE_IMGUI
private:
void update_max_z(const GLCanvas3D::Selection& selection) const;

View file

@ -225,6 +225,17 @@ bool GLShader::set_uniform(const char* name, const float* matrix) const
return false;
}
bool GLShader::set_uniform(const char* name, int value) const
{
int id = get_uniform_location(name);
if (id >= 0)
{
::glUniform1i(id, value);
return true;
}
return false;
}
/*
# Set shader vector
sub SetVector
@ -306,6 +317,16 @@ void Shader::stop_using() const
m_shader->disable();
}
int Shader::get_attrib_location(const std::string& name) const
{
return (m_shader != nullptr) ? m_shader->get_attrib_location(name.c_str()) : -1;
}
int Shader::get_uniform_location(const std::string& name) const
{
return (m_shader != nullptr) ? m_shader->get_uniform_location(name.c_str()) : -1;
}
void Shader::set_uniform(const std::string& name, float value) const
{
if (m_shader != nullptr)
@ -321,7 +342,7 @@ void Shader::set_uniform(const std::string& name, const float* matrix) const
void Shader::set_uniform(const std::string& name, bool value) const
{
if (m_shader != nullptr)
m_shader->set_uniform(name.c_str(), value);
m_shader->set_uniform(name.c_str(), value ? 1 : 0);
}
unsigned int Shader::get_shader_program_id() const

View file

@ -26,6 +26,7 @@ public:
bool set_uniform(const char *name, float value) const;
bool set_uniform(const char* name, const float* matrix) const;
bool set_uniform(const char* name, int value) const;
void enable() const;
void disable() const;
@ -52,6 +53,9 @@ public:
bool start_using() const;
void stop_using() const;
int get_attrib_location(const std::string& name) const;
int get_uniform_location(const std::string& name) const;
void set_uniform(const std::string& name, float value) const;
void set_uniform(const std::string& name, const float* matrix) const;
void set_uniform(const std::string& name, bool value) const;

View file

@ -6,19 +6,19 @@
#include <wx/image.h>
#include <boost/filesystem.hpp>
#if ENABLE_TEXTURES_FROM_SVG
#include <boost/algorithm/string/predicate.hpp>
#endif // ENABLE_TEXTURES_FROM_SVG
#include <vector>
#include <algorithm>
#if ENABLE_TEXTURES_FROM_SVG
#define NANOSVG_IMPLEMENTATION
#include "nanosvg/nanosvg.h"
#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvg/nanosvgrast.h"
#endif // ENABLE_TEXTURES_FROM_SVG
#include "libslic3r/Utils.hpp"
#include "libslic3r/Utils.hpp"
namespace Slic3r {
namespace GUI {
@ -38,7 +38,6 @@ GLTexture::~GLTexture()
reset();
}
#if ENABLE_TEXTURES_FROM_SVG
bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps)
{
reset();
@ -64,25 +63,20 @@ bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps
else
return false;
}
#else
bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps)
bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::string>& filenames, const std::vector<std::pair<int, bool>>& states, unsigned int sprite_size_px)
{
reset();
if (!boost::filesystem::exists(filename))
if (filenames.empty() || states.empty() || (sprite_size_px == 0))
return false;
// Load a PNG with an alpha channel.
wxImage image;
if (!image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_PNG))
{
reset();
return false;
}
m_width = image.GetWidth();
m_height = image.GetHeight();
m_width = (int)(sprite_size_px * states.size());
m_height = (int)(sprite_size_px * filenames.size());
int n_pixels = m_width * m_height;
int sprite_n_pixels = sprite_size_px * sprite_size_px;
int sprite_bytes = sprite_n_pixels * 4;
int sprite_stride = sprite_size_px * 4;
if (n_pixels <= 0)
{
@ -90,52 +84,136 @@ bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps)
return false;
}
// Get RGB & alpha raw data from wxImage, pack them into an array.
unsigned char* img_rgb = image.GetData();
if (img_rgb == nullptr)
std::vector<unsigned char> data(n_pixels * 4, 0);
std::vector<unsigned char> sprite_data(sprite_bytes, 0);
std::vector<unsigned char> sprite_white_only_data(sprite_bytes, 0);
std::vector<unsigned char> sprite_gray_only_data(sprite_bytes, 0);
std::vector<unsigned char> output_data(sprite_bytes, 0);
NSVGrasterizer* rast = nsvgCreateRasterizer();
if (rast == nullptr)
{
reset();
return false;
}
unsigned char* img_alpha = image.GetAlpha();
std::vector<unsigned char> data(n_pixels * 4, 0);
for (int i = 0; i < n_pixels; ++i)
int sprite_id = -1;
for (const std::string& filename : filenames)
{
int data_id = i * 4;
int img_id = i * 3;
data[data_id + 0] = img_rgb[img_id + 0];
data[data_id + 1] = img_rgb[img_id + 1];
data[data_id + 2] = img_rgb[img_id + 2];
data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
++sprite_id;
if (!boost::filesystem::exists(filename))
continue;
if (!boost::algorithm::iends_with(filename, ".svg"))
continue;
NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f);
if (image == nullptr)
continue;
float scale = (float)sprite_size_px / std::max(image->width, image->height);
nsvgRasterize(rast, image, 0, 0, scale, sprite_data.data(), sprite_size_px, sprite_size_px, sprite_stride);
// makes white only copy of the sprite
::memcpy((void*)sprite_white_only_data.data(), (const void*)sprite_data.data(), sprite_bytes);
for (int i = 0; i < sprite_n_pixels; ++i)
{
int offset = i * 4;
if (sprite_white_only_data.data()[offset] != 0)
::memset((void*)&sprite_white_only_data.data()[offset], 255, 3);
}
// makes gray only copy of the sprite
::memcpy((void*)sprite_gray_only_data.data(), (const void*)sprite_data.data(), sprite_bytes);
for (int i = 0; i < sprite_n_pixels; ++i)
{
int offset = i * 4;
if (sprite_gray_only_data.data()[offset] != 0)
::memset((void*)&sprite_gray_only_data.data()[offset], 128, 3);
}
int sprite_offset_px = sprite_id * sprite_size_px * m_width;
int state_id = -1;
for (const std::pair<int, bool>& state : states)
{
++state_id;
// select the sprite variant
std::vector<unsigned char>* src = nullptr;
switch (state.first)
{
case 1: { src = &sprite_white_only_data; break; }
case 2: { src = &sprite_gray_only_data; break; }
default: { src = &sprite_data; break; }
}
::memcpy((void*)output_data.data(), (const void*)src->data(), sprite_bytes);
// applies background, if needed
if (state.second)
{
for (int i = 0; i < sprite_n_pixels; ++i)
{
int offset = i * 4;
float alpha = (float)output_data.data()[offset + 3] / 255.0f;
output_data.data()[offset + 0] = (unsigned char)(output_data.data()[offset + 0] * alpha);
output_data.data()[offset + 1] = (unsigned char)(output_data.data()[offset + 1] * alpha);
output_data.data()[offset + 2] = (unsigned char)(output_data.data()[offset + 2] * alpha);
output_data.data()[offset + 3] = (unsigned char)(128 * (1.0f - alpha) + output_data.data()[offset + 3] * alpha);
}
}
int state_offset_px = sprite_offset_px + state_id * sprite_size_px;
for (int j = 0; j < (int)sprite_size_px; ++j)
{
::memcpy((void*)&data.data()[(state_offset_px + j * m_width) * 4], (const void*)&output_data.data()[j * sprite_stride], sprite_stride);
}
}
nsvgDelete(image);
}
nsvgDeleteRasterizer(rast);
// sends data to gpu
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
::glGenTextures(1, &m_id);
::glBindTexture(GL_TEXTURE_2D, m_id);
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
if (use_mipmaps)
{
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
unsigned int levels_count = generate_mipmaps(image);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1 + levels_count);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
else
{
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
}
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
::glBindTexture(GL_TEXTURE_2D, 0);
m_source = filename;
m_source = filenames.front();
#if 0
// debug output
static int pass = 0;
++pass;
wxImage output(m_width, m_height);
output.InitAlpha();
for (int h = 0; h < m_height; ++h)
{
int px_h = h * m_width;
for (int w = 0; w < m_width; ++w)
{
int offset = (px_h + w) * 4;
output.SetRGB(w, h, data.data()[offset + 0], data.data()[offset + 1], data.data()[offset + 2]);
output.SetAlpha(w, h, data.data()[offset + 3]);
}
}
std::string out_filename = resources_dir() + "/icons/test_" + std::to_string(pass) + ".png";
output.SaveFile(out_filename, wxBITMAP_TYPE_PNG);
#endif // 0
return true;
}
#endif // ENABLE_TEXTURES_FROM_SVG
void GLTexture::reset()
{
@ -214,7 +292,6 @@ unsigned int GLTexture::generate_mipmaps(wxImage& image)
return (unsigned int)level;
}
#if ENABLE_TEXTURES_FROM_SVG
bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps)
{
// Load a PNG with an alpha channel.
@ -265,13 +342,13 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps)
{
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
unsigned int levels_count = generate_mipmaps(image);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1 + levels_count);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels_count);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
else
{
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
}
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@ -340,13 +417,13 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
}
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1 + level);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
else
{
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
}
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@ -359,7 +436,6 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
return true;
}
#endif // ENABLE_TEXTURES_FROM_SVG
} // namespace GUI
} // namespace Slic3r

View file

@ -38,9 +38,16 @@ namespace GUI {
virtual ~GLTexture();
bool load_from_file(const std::string& filename, bool use_mipmaps);
#if ENABLE_TEXTURES_FROM_SVG
bool load_from_svg_file(const std::string& filename, bool use_mipmaps, unsigned int max_size_px);
#endif // ENABLE_TEXTURES_FROM_SVG
// meanings of states: (std::pair<int, bool>)
// first field (int):
// 0 -> no changes
// 1 -> use white only color variant
// 2 -> use gray only color variant
// second field (bool):
// false -> no changes
// true -> add background color
bool load_from_svg_files_as_sprites_array(const std::vector<std::string>& filenames, const std::vector<std::pair<int, bool>>& states, unsigned int sprite_size_px);
void reset();
unsigned int get_id() const { return m_id; }
@ -54,11 +61,9 @@ namespace GUI {
protected:
unsigned int generate_mipmaps(wxImage& image);
#if ENABLE_TEXTURES_FROM_SVG
private:
bool load_from_png(const std::string& filename, bool use_mipmaps);
bool load_from_svg(const std::string& filename, bool use_mipmaps, unsigned int max_size_px);
#endif // ENABLE_TEXTURES_FROM_SVG
};
} // namespace GUI

View file

@ -32,12 +32,13 @@ wxDEFINE_EVENT(EVT_GLVIEWTOOLBAR_PREVIEW, SimpleEvent);
GLToolbarItem::Data::Data()
: name("")
#if ENABLE_SVG_ICONS
, icon_filename("")
#endif // ENABLE_SVG_ICONS
, tooltip("")
, sprite_id(-1)
, is_toggable(false)
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
, visible(true)
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
{
}
@ -53,26 +54,24 @@ void GLToolbarItem::do_action(wxEvtHandler *target)
wxPostEvent(target, SimpleEvent(m_data.action_event));
}
void GLToolbarItem::render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const
void GLToolbarItem::render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const
{
GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(texture_size, border_size, icon_size, gap_size));
GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(tex_width, tex_height, icon_size));
}
GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const
GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const
{
GLTexture::Quad_UVs uvs;
float inv_texture_size = (texture_size != 0) ? 1.0f / (float)texture_size : 0.0f;
float inv_tex_width = (tex_width != 0) ? 1.0f / (float)tex_width : 0.0f;
float inv_tex_height = (tex_height != 0) ? 1.0f / (float)tex_height : 0.0f;
float scaled_icon_size = (float)icon_size * inv_texture_size;
float scaled_border_size = (float)border_size * inv_texture_size;
float scaled_gap_size = (float)gap_size * inv_texture_size;
float stride = scaled_icon_size + scaled_gap_size;
float left = scaled_border_size + (float)m_state * stride;
float right = left + scaled_icon_size;
float top = scaled_border_size + (float)m_data.sprite_id * stride;
float bottom = top + scaled_icon_size;
float scaled_icon_width = (float)icon_size * inv_tex_width;
float scaled_icon_height = (float)icon_size * inv_tex_height;
float left = (float)m_state * scaled_icon_width;
float right = left + scaled_icon_width;
float top = (float)m_data.sprite_id * scaled_icon_height;
float bottom = top + scaled_icon_height;
uvs.left_top = { left, top };
uvs.left_bottom = { left, bottom };
@ -82,13 +81,13 @@ GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int texture_size, unsigned i
return uvs;
}
#if !ENABLE_SVG_ICONS
ItemsIconsTexture::Metadata::Metadata()
: filename("")
, icon_size(0)
, icon_border_size(0)
, icon_gap_size(0)
{
}
#endif // !ENABLE_SVG_ICONS
BackgroundTexture::Metadata::Metadata()
: filename("")
@ -99,6 +98,10 @@ BackgroundTexture::Metadata::Metadata()
{
}
#if ENABLE_SVG_ICONS
const float GLToolbar::Default_Icons_Size = 64.0f;
#endif // ENABLE_SVG_ICONS
GLToolbar::Layout::Layout()
: type(Horizontal)
, orientation(Center)
@ -107,16 +110,31 @@ GLToolbar::Layout::Layout()
, border(0.0f)
, separator_size(0.0f)
, gap_size(0.0f)
#if ENABLE_SVG_ICONS
, icons_size(Default_Icons_Size)
, scale(1.0f)
#else
, icons_scale(1.0f)
#endif // ENABLE_SVG_ICONS
, width(0.0f)
, height(0.0f)
, dirty(true)
{
}
#if ENABLE_SVG_ICONS
GLToolbar::GLToolbar(GLToolbar::EType type, const std::string& name)
#else
GLToolbar::GLToolbar(GLToolbar::EType type)
#endif // ENABLE_SVG_ICONS
: m_type(type)
#if ENABLE_SVG_ICONS
, m_name(name)
#endif // ENABLE_SVG_ICONS
, m_enabled(false)
#if ENABLE_SVG_ICONS
, m_icons_texture_dirty(true)
#endif // ENABLE_SVG_ICONS
{
}
@ -128,8 +146,19 @@ GLToolbar::~GLToolbar()
}
}
#if ENABLE_SVG_ICONS
bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture)
#else
bool GLToolbar::init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture)
#endif // ENABLE_SVG_ICONS
{
#if ENABLE_SVG_ICONS
if (m_background_texture.texture.get_id() != 0)
return true;
std::string path = resources_dir() + "/icons/";
bool res = false;
#else
if (m_icons_texture.texture.get_id() != 0)
return true;
@ -137,6 +166,7 @@ bool GLToolbar::init(const ItemsIconsTexture::Metadata& icons_texture, const Bac
bool res = !icons_texture.filename.empty() && m_icons_texture.texture.load_from_file(path + icons_texture.filename, false);
if (res)
m_icons_texture.metadata = icons_texture;
#endif // ENABLE_SVG_ICONS
if (!background_texture.filename.empty())
res = m_background_texture.texture.load_from_file(path + background_texture.filename, false);
@ -192,11 +222,33 @@ void GLToolbar::set_gap_size(float size)
m_layout.dirty = true;
}
#if ENABLE_SVG_ICONS
void GLToolbar::set_icons_size(float size)
{
if (m_layout.icons_size != size)
{
m_layout.icons_size = size;
m_layout.dirty = true;
m_icons_texture_dirty = true;
}
}
void GLToolbar::set_scale(float scale)
{
if (m_layout.scale != scale)
{
m_layout.scale = scale;
m_layout.dirty = true;
m_icons_texture_dirty = true;
}
}
#else
void GLToolbar::set_icons_scale(float scale)
{
m_layout.icons_scale = scale;
m_layout.dirty = true;
}
#endif // ENABLE_SVG_ICONS
bool GLToolbar::is_enabled() const
{
@ -308,7 +360,6 @@ bool GLToolbar::is_item_disabled(const std::string& name) const
return false;
}
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
bool GLToolbar::is_item_visible(const std::string& name) const
{
for (GLToolbarItem* item : m_items)
@ -346,7 +397,6 @@ void GLToolbar::set_item_visible(const std::string& name, bool visible)
}
}
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
std::string GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent)
{
@ -417,6 +467,11 @@ void GLToolbar::render(const GLCanvas3D& parent) const
if (!m_enabled || m_items.empty())
return;
#if ENABLE_SVG_ICONS
if (m_icons_texture_dirty)
generate_icons_texture();
#endif // ENABLE_SVG_ICONS
::glDisable(GL_DEPTH_TEST);
::glPushMatrix();
@ -461,12 +516,20 @@ float GLToolbar::get_width_horizontal() const
float GLToolbar::get_width_vertical() const
{
#if ENABLE_SVG_ICONS
return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale;
#else
return 2.0f * m_layout.border * m_layout.icons_scale + m_icons_texture.metadata.icon_size * m_layout.icons_scale;
#endif // ENABLE_SVG_ICONS
}
float GLToolbar::get_height_horizontal() const
{
#if ENABLE_SVG_ICONS
return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale;
#else
return 2.0f * m_layout.border * m_layout.icons_scale + m_icons_texture.metadata.icon_size * m_layout.icons_scale;
#endif // ENABLE_SVG_ICONS
}
float GLToolbar::get_height_vertical() const
@ -476,13 +539,29 @@ float GLToolbar::get_height_vertical() const
float GLToolbar::get_main_size() const
{
#if ENABLE_SVG_ICONS
float size = 2.0f * m_layout.border;
for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i)
{
if (!m_items[i]->is_visible())
continue;
if (m_items[i]->is_separator())
size += m_layout.separator_size;
else
size += (float)m_layout.icons_size;
}
if (m_items.size() > 1)
size += ((float)m_items.size() - 1.0f) * m_layout.gap_size;
size *= m_layout.scale;
#else
float size = 2.0f * m_layout.border * m_layout.icons_scale;
for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i)
{
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
if (!m_items[i]->is_visible())
continue;
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
if (m_items[i]->is_separator())
size += m_layout.separator_size * m_layout.icons_scale;
@ -492,6 +571,7 @@ float GLToolbar::get_main_size() const
if (m_items.size() > 1)
size += ((float)m_items.size() - 1.0f) * m_layout.gap_size * m_layout.icons_scale;
#endif // ENABLE_SVG_ICONS
return size;
}
@ -502,12 +582,20 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC
float zoom = parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
#else
float factor = m_layout.icons_scale * inv_zoom;
#endif // ENABLE_SVG_ICONS
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
#else
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@ -522,10 +610,8 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC
for (GLToolbarItem* item : m_items)
{
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
if (!item->is_visible())
continue;
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
if (item->is_separator())
left += separator_stride;
@ -601,16 +687,23 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan
float zoom = parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
#else
float factor = m_layout.icons_scale * inv_zoom;
#endif // ENABLE_SVG_ICONS
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
#else
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
@ -621,10 +714,8 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan
for (GLToolbarItem* item : m_items)
{
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
if (!item->is_visible())
continue;
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
if (item->is_separator())
top -= separator_stride;
@ -700,16 +791,23 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3
float zoom = parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
#else
float factor = m_layout.icons_scale * inv_zoom;
#endif // ENABLE_SVG_ICONS
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
#else
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
@ -722,10 +820,8 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3
{
++id;
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
if (!item->is_visible())
continue;
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
if (item->is_separator())
left += separator_stride;
@ -733,7 +829,7 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3
{
float right = left + scaled_icons_size;
float bottom = top - scaled_icons_size;
if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top))
return id;
@ -750,12 +846,20 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D&
float zoom = parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
#else
float factor = m_layout.icons_scale * inv_zoom;
#endif // ENABLE_SVG_ICONS
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
#else
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@ -772,10 +876,8 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D&
{
++id;
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
if (!item->is_visible())
continue;
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
if (item->is_separator())
top -= separator_stride;
@ -796,17 +898,34 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D&
void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
{
#if ENABLE_SVG_ICONS
unsigned int tex_id = m_icons_texture.get_id();
int tex_width = m_icons_texture.get_width();
int tex_height = m_icons_texture.get_height();
#else
unsigned int tex_id = m_icons_texture.texture.get_id();
int tex_size = m_icons_texture.texture.get_width();
int tex_width = m_icons_texture.texture.get_width();
int tex_height = m_icons_texture.texture.get_height();
#endif // ENABLE_SVG_ICONS
if ((tex_id == 0) || (tex_size <= 0))
#if !ENABLE_SVG_ICONS
if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
#endif // !ENABLE_SVG_ICONS
float zoom = parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = inv_zoom * m_layout.scale;
#else
float factor = inv_zoom * m_layout.icons_scale;
#endif // ENABLE_SVG_ICONS
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
#else
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@ -907,19 +1026,26 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
left += scaled_border;
top -= scaled_border;
#if ENABLE_SVG_ICONS
if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
#endif // ENABLE_SVG_ICONS
// renders icons
for (const GLToolbarItem* item : m_items)
{
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
if (!item->is_visible())
continue;
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
if (item->is_separator())
left += separator_stride;
else
{
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.metadata.icon_border_size, m_icons_texture.metadata.icon_size, m_icons_texture.metadata.icon_gap_size);
#if ENABLE_SVG_ICONS
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale));
#else
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, m_icons_texture.metadata.icon_size);
#endif // ENABLE_SVG_ICONS
left += icon_stride;
}
}
@ -927,17 +1053,34 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
void GLToolbar::render_vertical(const GLCanvas3D& parent) const
{
#if ENABLE_SVG_ICONS
unsigned int tex_id = m_icons_texture.get_id();
int tex_width = m_icons_texture.get_width();
int tex_height = m_icons_texture.get_height();
#else
unsigned int tex_id = m_icons_texture.texture.get_id();
int tex_size = m_icons_texture.texture.get_width();
int tex_width = m_icons_texture.texture.get_width();
int tex_height = m_icons_texture.texture.get_height();
#endif // ENABLE_SVG_ICONS
if ((tex_id == 0) || (tex_size <= 0))
#if !ENABLE_SVG_ICONS
if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
#endif // !ENABLE_SVG_ICONS
float zoom = parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = inv_zoom * m_layout.scale;
#else
float factor = inv_zoom * m_layout.icons_scale;
#endif // ENABLE_SVG_ICONS
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
#else
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * factor;
#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@ -1038,23 +1181,68 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const
left += scaled_border;
top -= scaled_border;
#if ENABLE_SVG_ICONS
if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
#endif // ENABLE_SVG_ICONS
// renders icons
for (const GLToolbarItem* item : m_items)
{
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
if (!item->is_visible())
continue;
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
if (item->is_separator())
top -= separator_stride;
else
{
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.metadata.icon_border_size, m_icons_texture.metadata.icon_size, m_icons_texture.metadata.icon_gap_size);
#if ENABLE_SVG_ICONS
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale));
#else
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, m_icons_texture.metadata.icon_size);
#endif // ENABLE_SVG_ICONS
top -= icon_stride;
}
}
}
#if ENABLE_SVG_ICONS
bool GLToolbar::generate_icons_texture() const
{
std::string path = resources_dir() + "/icons/";
std::vector<std::string> filenames;
for (GLToolbarItem* item : m_items)
{
const std::string& icon_filename = item->get_icon_filename();
if (!icon_filename.empty())
filenames.push_back(path + icon_filename);
}
std::vector<std::pair<int, bool>> states;
if (m_name == "Top")
{
states.push_back(std::make_pair(1, false));
states.push_back(std::make_pair(0, false));
states.push_back(std::make_pair(2, false));
states.push_back(std::make_pair(0, false));
states.push_back(std::make_pair(0, false));
}
else if (m_name == "View")
{
states.push_back(std::make_pair(1, false));
states.push_back(std::make_pair(1, true));
states.push_back(std::make_pair(1, false));
states.push_back(std::make_pair(0, false));
states.push_back(std::make_pair(1, true));
}
bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, (unsigned int)(m_layout.icons_size * m_layout.scale));
if (res)
m_icons_texture_dirty = false;
return res;
}
#endif // ENABLE_SVG_ICONS
} // namespace GUI
} // namespace Slic3r

View file

@ -51,13 +51,14 @@ public:
struct Data
{
std::string name;
#if ENABLE_SVG_ICONS
std::string icon_filename;
#endif // ENABLE_SVG_ICONS
std::string tooltip;
unsigned int sprite_id;
bool is_toggable;
wxEventType action_event;
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
bool visible;
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
Data();
};
@ -74,6 +75,9 @@ public:
void set_state(EState state) { m_state = state; }
const std::string& get_name() const { return m_data.name; }
#if ENABLE_SVG_ICONS
const std::string& get_icon_filename() const { return m_data.icon_filename; }
#endif // ENABLE_SVG_ICONS
const std::string& get_tooltip() const { return m_data.tooltip; }
void do_action(wxEvtHandler *target);
@ -84,18 +88,17 @@ public:
bool is_pressed() const { return (m_state == Pressed) || (m_state == HoverPressed); }
bool is_toggable() const { return m_data.is_toggable; }
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
bool is_visible() const { return m_data.visible; }
void set_visible(bool visible) { m_data.visible = visible; }
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
bool is_separator() const { return m_type == Separator; }
void render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const;
void render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const;
private:
GLTexture::Quad_UVs get_uvs(unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const;
GLTexture::Quad_UVs get_uvs(unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const;
};
#if !ENABLE_SVG_ICONS
// items icon textures are assumed to be square and all with the same size in pixels, no internal check is done
// icons are layed-out into the texture starting from the top-left corner in the same order as enum GLToolbarItem::EState
// from left to right
@ -107,10 +110,6 @@ struct ItemsIconsTexture
std::string filename;
// size of the square icons, in pixels
unsigned int icon_size;
// size of the border, in pixels
unsigned int icon_border_size;
// distance between two adjacent icons (to avoid filtering artifacts), in pixels
unsigned int icon_gap_size;
Metadata();
};
@ -118,6 +117,7 @@ struct ItemsIconsTexture
GLTexture texture;
Metadata metadata;
};
#endif // !ENABLE_SVG_ICONS
struct BackgroundTexture
{
@ -144,6 +144,10 @@ struct BackgroundTexture
class GLToolbar
{
public:
#if ENABLE_SVG_ICONS
static const float Default_Icons_Size;
#endif // ENABLE_SVG_ICONS
enum EType : unsigned char
{
Normal,
@ -177,7 +181,12 @@ public:
float border;
float separator_size;
float gap_size;
#if ENABLE_SVG_ICONS
float icons_size;
float scale;
#else
float icons_scale;
#endif // ENABLE_SVG_ICONS
float width;
float height;
@ -190,18 +199,34 @@ private:
typedef std::vector<GLToolbarItem*> ItemsList;
EType m_type;
#if ENABLE_SVG_ICONS
std::string m_name;
#endif // ENABLE_SVG_ICONS
bool m_enabled;
#if ENABLE_SVG_ICONS
mutable GLTexture m_icons_texture;
mutable bool m_icons_texture_dirty;
#else
ItemsIconsTexture m_icons_texture;
#endif // ENABLE_SVG_ICONS
BackgroundTexture m_background_texture;
mutable Layout m_layout;
ItemsList m_items;
public:
#if ENABLE_SVG_ICONS
GLToolbar(EType type, const std::string& name);
#else
explicit GLToolbar(EType type);
#endif // ENABLE_SVG_ICONS
~GLToolbar();
#if ENABLE_SVG_ICONS
bool init(const BackgroundTexture::Metadata& background_texture);
#else
bool init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture);
#endif // ENABLE_SVG_ICONS
Layout::EType get_layout_type() const;
void set_layout_type(Layout::EType type);
@ -212,7 +237,12 @@ public:
void set_border(float border);
void set_separator_size(float size);
void set_gap_size(float size);
#if ENABLE_SVG_ICONS
void set_icons_size(float size);
void set_scale(float scale);
#else
void set_icons_scale(float scale);
#endif // ENABLE_SVG_ICONS
bool is_enabled() const;
void set_enabled(bool enable);
@ -229,10 +259,8 @@ public:
bool is_item_pressed(const std::string& name) const;
bool is_item_disabled(const std::string& name) const;
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
bool is_item_visible(const std::string& name) const;
void set_item_visible(const std::string& name, bool visible);
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent);
@ -257,6 +285,10 @@ private:
void render_horizontal(const GLCanvas3D& parent) const;
void render_vertical(const GLCanvas3D& parent) const;
#if ENABLE_SVG_ICONS
bool generate_icons_texture() const;
#endif // ENABLE_SVG_ICONS
};
} // namespace GUI

View file

@ -30,6 +30,8 @@
#include "libslic3r/Utils.hpp"
#include "libslic3r/Print.hpp"
#include "Tab.hpp"
#include "GUI_ObjectList.hpp"
namespace Slic3r { namespace GUI {
@ -123,6 +125,9 @@ void config_wizard(int reason)
if (! wxGetApp().check_unsaved_changes())
return;
// save selected preset before config wizard running
const auto printer_preset_name = wxGetApp().preset_bundle->printers.get_edited_preset().name;
try {
ConfigWizard wizard(nullptr, static_cast<ConfigWizard::RunReason>(reason));
wizard.run(wxGetApp().preset_bundle, wxGetApp().preset_updater);
@ -131,8 +136,21 @@ void config_wizard(int reason)
show_error(nullptr, e.what());
}
// Load the currently selected preset into the GUI, update the preset selection box.
wxGetApp().load_current_presets();
// select old(before config wizard running) preset
wxGetApp().get_tab(Preset::TYPE_PRINTER)->select_preset(printer_preset_name);
// If old preset if invisible now, then first visible preset will be selected
// So, let control the case if multi-part object is on the scene and first visible preset is SLA
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA &&
wxGetApp().obj_list()->has_multi_part_objects())
{
show_info(nullptr,
_(L("It's impossible to print multi-part object(s) with SLA technology.")) + "\n\n" +
_(L("Please check and fix your object list.")),
_(L("Attention!")));
}
// Load the currently selected preset into the GUI, update the preset selection box.
// wxGetApp().load_current_presets(); // #ys_FIXME_to_delete presets are loaded now in select_preset function
}
// opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element)

View file

@ -177,33 +177,40 @@ bool GUI_App::OnInit()
if (this->plater() != nullptr)
this->obj_manipul()->update_if_dirty();
});
// On OS X the UI tends to freeze in weird ways if modal dialogs(config wizard, update notifications, ...)
// are shown before or in the same event callback with the main frame creation.
// Therefore we schedule them for later using CallAfter.
CallAfter([this]() {
try {
if (!preset_updater->config_update())
mainframe->Close();
} catch (const std::exception &ex) {
show_error(nullptr, ex.what());
// Preset updating & Configwizard are done after the above initializations,
// and after MainFrame is created & shown.
// The extra CallAfter() is needed because of Mac, where this is the only way
// to popup a modal dialog on start without screwing combo boxes.
// This is ugly but I honestly found not better way to do it.
// Neither wxShowEvent nor wxWindowCreateEvent work reliably.
static bool once = true;
if (once) {
once = false;
try {
if (!preset_updater->config_update()) {
mainframe->Close();
}
} catch (const std::exception &ex) {
show_error(nullptr, ex.what());
}
CallAfter([this] {
if (!config_wizard_startup(app_conf_exists)) {
// Only notify if there was not wizard so as not to bother too much ...
preset_updater->slic3r_update_notify();
}
preset_updater->sync(preset_bundle);
});
load_current_presets();
}
});
CallAfter([this]() {
if (!config_wizard_startup(app_conf_exists)) {
// Only notify if there was not wizard so as not to bother too much ...
preset_updater->slic3r_update_notify();
}
preset_updater->sync(preset_bundle);
load_current_presets();
});
mainframe->Show(true);
return m_initialized = true;
m_initialized = true;
return true;
}
unsigned GUI_App::get_colour_approx_luma(const wxColour &colour)

View file

@ -38,6 +38,11 @@ FreqSettingsBundle FREQ_SETTINGS_BUNDLE_SLA =
{ L("Pad and Support") , { "supports_enable", "pad_enable" } }
};
static PrinterTechnology printer_technology()
{
return wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology();
}
ObjectList::ObjectList(wxWindow* parent) :
wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE),
m_parent(parent)
@ -259,7 +264,7 @@ void ObjectList::update_extruder_values_for_items(const int max_extruder)
void ObjectList::update_objects_list_extruder_column(int extruders_count)
{
if (!this) return; // #ys_FIXME
if (wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA)
if (printer_technology() == ptSLA)
extruders_count = 1;
wxDataViewChoiceRenderer* ch_render = dynamic_cast<wxDataViewChoiceRenderer*>(GetColumn(1)->GetRenderer());
@ -376,7 +381,8 @@ void ObjectList::selection_changed()
fix_multiselection_conflicts();
// update object selection on Plater
update_selections_on_canvas();
if (!m_prevent_canvas_selection_update)
update_selections_on_canvas();
// to update the toolbar and info sizer
if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject) {
@ -451,7 +457,7 @@ void ObjectList::show_context_menu()
wxMenu* menu = type & itInstance ? &m_menu_instance :
m_objects_model->GetParent(item) != wxDataViewItem(0) ? &m_menu_part :
wxGetApp().plater()->printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object;
printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object;
if (!(type & itInstance))
append_menu_item_settings(menu);
@ -596,7 +602,7 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
std::vector<std::string> ObjectList::get_options(const bool is_part)
{
if (wxGetApp().plater()->printer_technology() == ptSLA) {
if (printer_technology() == ptSLA) {
SLAPrintObjectConfig full_sla_config;
auto options = full_sla_config.keys();
options.erase(find(options.begin(), options.end(), "layer_height"));
@ -615,7 +621,7 @@ std::vector<std::string> ObjectList::get_options(const bool is_part)
const std::vector<std::string>& ObjectList::get_options_for_bundle(const wxString& bundle_name)
{
const FreqSettingsBundle& bundle = wxGetApp().plater()->printer_technology() == ptSLA ?
const FreqSettingsBundle& bundle = printer_technology() == ptSLA ?
FREQ_SETTINGS_BUNDLE_SLA : FREQ_SETTINGS_BUNDLE_FFF;
for (auto& it : bundle)
@ -625,7 +631,7 @@ const std::vector<std::string>& ObjectList::get_options_for_bundle(const wxStrin
}
#if 0
// if "Quick menu" is selected
FreqSettingsBundle& bundle_quick = wxGetApp().plater()->printer_technology() == ptSLA ?
FreqSettingsBundle& bundle_quick = printer_technology() == ptSLA ?
m_freq_settings_sla: m_freq_settings_fff;
for (auto& it : bundle_quick)
@ -643,7 +649,7 @@ void ObjectList::get_options_menu(settings_menu_hierarchy& settings_menu, const
{
auto options = get_options(is_part);
auto extruders_cnt = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA ? 1 :
auto extruders_cnt = printer_technology() == ptSLA ? 1 :
wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
DynamicPrintConfig config;
@ -704,7 +710,7 @@ void ObjectList::get_settings_choice(const wxString& category_name)
if (selection_cnt > 0)
{
// Add selected items to the "Quick menu"
FreqSettingsBundle& freq_settings = wxGetApp().plater()->printer_technology() == ptSLA ?
FreqSettingsBundle& freq_settings = printer_technology() == ptSLA ?
m_freq_settings_sla : m_freq_settings_fff;
bool changed_existing = false;
@ -750,7 +756,9 @@ void ObjectList::get_settings_choice(const wxString& category_name)
for (auto sel : selections)
selected_options.push_back((*settings_list)[sel].first);
const DynamicPrintConfig& from_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
const DynamicPrintConfig& from_config = printer_technology() == ptFFF ?
wxGetApp().preset_bundle->prints.get_edited_preset().config :
wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
for (auto& setting : (*settings_list))
{
@ -1064,10 +1072,10 @@ wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu)
void ObjectList::create_freq_settings_popupmenu(wxMenu *menu)
{
// Add default settings bundles
const FreqSettingsBundle& bundle = wxGetApp().plater()->printer_technology() == ptFFF ?
const FreqSettingsBundle& bundle = printer_technology() == ptFFF ?
FREQ_SETTINGS_BUNDLE_FFF : FREQ_SETTINGS_BUNDLE_SLA;
auto extruders_cnt = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA ? 1 :
auto extruders_cnt = printer_technology() == ptSLA ? 1 :
wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
for (auto& it : bundle) {
@ -1080,7 +1088,7 @@ void ObjectList::create_freq_settings_popupmenu(wxMenu *menu)
}
#if 0
// Add "Quick" settings bundles
const FreqSettingsBundle& bundle_quick = wxGetApp().plater()->printer_technology() == ptFFF ?
const FreqSettingsBundle& bundle_quick = printer_technology() == ptFFF ?
m_freq_settings_fff : m_freq_settings_sla;
for (auto& it : bundle_quick) {
@ -1173,10 +1181,94 @@ void ObjectList::load_part( ModelObject* model_object,
}
// Find volume transformation, so that the chained (instance_trafo * volume_trafo) will be as close to identity
// as possible in least squares norm in regard to the 8 corners of bbox.
// Bounding box is expected to be centered around zero in all axes.
Geometry::Transformation volume_to_bed_transformation(const Geometry::Transformation &instance_transformation, const BoundingBoxf3 &bbox)
{
Geometry::Transformation out;
// Is the angle close to a multiple of 90 degrees?
auto ninety_degrees = [](double a) {
a = fmod(std::abs(a), 0.5 * PI);
if (a > 0.25 * PI)
a = 0.5 * PI - a;
return a < 0.001;
};
if (instance_transformation.is_scaling_uniform()) {
// No need to run the non-linear least squares fitting for uniform scaling.
// Just set the inverse.
out.set_from_transform(instance_transformation.get_matrix(true).inverse());
}
else if (ninety_degrees(instance_transformation.get_rotation().x()) && ninety_degrees(instance_transformation.get_rotation().y()) && ninety_degrees(instance_transformation.get_rotation().z()))
{
// Anisotropic scaling, rotation by multiples of ninety degrees.
Eigen::Matrix3d instance_rotation_trafo =
(Eigen::AngleAxisd(instance_transformation.get_rotation().z(), Vec3d::UnitZ()) *
Eigen::AngleAxisd(instance_transformation.get_rotation().y(), Vec3d::UnitY()) *
Eigen::AngleAxisd(instance_transformation.get_rotation().x(), Vec3d::UnitX())).toRotationMatrix();
Eigen::Matrix3d volume_rotation_trafo =
(Eigen::AngleAxisd(-instance_transformation.get_rotation().x(), Vec3d::UnitX()) *
Eigen::AngleAxisd(-instance_transformation.get_rotation().y(), Vec3d::UnitY()) *
Eigen::AngleAxisd(-instance_transformation.get_rotation().z(), Vec3d::UnitZ())).toRotationMatrix();
// 8 corners of the bounding box.
auto pts = Eigen::MatrixXd(8, 3);
pts(0, 0) = bbox.min.x(); pts(0, 1) = bbox.min.y(); pts(0, 2) = bbox.min.z();
pts(1, 0) = bbox.min.x(); pts(1, 1) = bbox.min.y(); pts(1, 2) = bbox.max.z();
pts(2, 0) = bbox.min.x(); pts(2, 1) = bbox.max.y(); pts(2, 2) = bbox.min.z();
pts(3, 0) = bbox.min.x(); pts(3, 1) = bbox.max.y(); pts(3, 2) = bbox.max.z();
pts(4, 0) = bbox.max.x(); pts(4, 1) = bbox.min.y(); pts(4, 2) = bbox.min.z();
pts(5, 0) = bbox.max.x(); pts(5, 1) = bbox.min.y(); pts(5, 2) = bbox.max.z();
pts(6, 0) = bbox.max.x(); pts(6, 1) = bbox.max.y(); pts(6, 2) = bbox.min.z();
pts(7, 0) = bbox.max.x(); pts(7, 1) = bbox.max.y(); pts(7, 2) = bbox.max.z();
// Corners of the bounding box transformed into the modifier mesh coordinate space, with inverse rotation applied to the modifier.
auto qs = pts *
(instance_rotation_trafo *
Eigen::Scaling(instance_transformation.get_scaling_factor().cwiseProduct(instance_transformation.get_mirror())) *
volume_rotation_trafo).inverse().transpose();
// Fill in scaling based on least squares fitting of the bounding box corners.
Vec3d scale;
for (int i = 0; i < 3; ++ i)
scale(i) = pts.col(i).dot(qs.col(i)) / pts.col(i).dot(pts.col(i));
out.set_rotation(Geometry::extract_euler_angles(volume_rotation_trafo));
out.set_scaling_factor(Vec3d(std::abs(scale(0)), std::abs(scale(1)), std::abs(scale(2))));
out.set_mirror(Vec3d(scale(0) > 0 ? 1. : -1, scale(1) > 0 ? 1. : -1, scale(2) > 0 ? 1. : -1));
}
else
{
// General anisotropic scaling, general rotation.
// Keep the modifier mesh in the instance coordinate system, so the modifier mesh will not be aligned with the world.
// Scale it to get the required size.
out.set_scaling_factor(instance_transformation.get_scaling_factor().cwiseInverse());
}
return out;
}
void ObjectList::load_generic_subobject(const std::string& type_name, const ModelVolumeType type)
{
const auto obj_idx = get_selected_obj_idx();
if (obj_idx < 0) return;
if (obj_idx < 0)
return;
const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
assert(obj_idx == selection.get_object_idx());
/** Any changes of the Object's composition is duplicated for all Object's Instances
* So, It's enough to take a bounding box of a first selected Instance and calculate Part(generic_subobject) position
*/
int instance_idx = *selection.get_instance_idxs().begin();
assert(instance_idx != -1);
if (instance_idx == -1)
return;
// Selected object
ModelObject &model_object = *(*m_objects)[obj_idx];
// Bounding box of the selected instance in world coordinate system including the translation, without modifiers.
BoundingBoxf3 instance_bb = model_object.instance_bounding_box(instance_idx);
const wxString name = _(L("Generic")) + "-" + _(type_name);
TriangleMesh mesh;
@ -1185,48 +1277,48 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
const auto& sz = BoundingBoxf(bed_shape).size();
const auto side = 0.1 * std::max(sz(0), sz(1));
if (type_name == "Box") {
if (type_name == "Box")
// Sitting on the print bed, left front front corner at (0, 0).
mesh = make_cube(side, side, side);
// box sets the base coordinate at 0, 0, move to center of plate
mesh.translate(-side * 0.5, -side * 0.5, 0);
}
else if (type_name == "Cylinder")
mesh = make_cylinder(0.5*side, side);
// Centered around 0, sitting on the print bed.
// The cylinder has the same volume as the box above.
mesh = make_cylinder(0.564 * side, side);
else if (type_name == "Sphere")
mesh = make_sphere(0.5*side, PI/18);
else if (type_name == "Slab") {
const auto& size = (*m_objects)[obj_idx]->bounding_box().size();
mesh = make_cube(size(0)*1.5, size(1)*1.5, size(2)*0.5);
// box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z
mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, 0);
}
// Centered around 0, half the sphere below the print bed, half above.
// The sphere has the same volume as the box above.
mesh = make_sphere(0.62 * side, PI / 18);
else if (type_name == "Slab")
// Sitting on the print bed, left front front corner at (0, 0).
mesh = make_cube(instance_bb.size().x()*1.5, instance_bb.size().y()*1.5, instance_bb.size().z()*0.5);
mesh.repair();
auto new_volume = (*m_objects)[obj_idx]->add_volume(mesh);
// Mesh will be centered when loading.
ModelVolume *new_volume = model_object.add_volume(std::move(mesh));
new_volume->set_type(type);
#if !ENABLE_GENERIC_SUBPARTS_PLACEMENT
new_volume->set_offset(Vec3d(0.0, 0.0, (*m_objects)[obj_idx]->origin_translation(2) - mesh.stl.stats.min(2)));
new_volume->set_offset(Vec3d(0.0, 0.0, model_object.origin_translation(2) - mesh.stl.stats.min(2)));
#endif // !ENABLE_GENERIC_SUBPARTS_PLACEMENT
#if !ENABLE_VOLUMES_CENTERING_FIXES
new_volume->center_geometry();
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
#if ENABLE_GENERIC_SUBPARTS_PLACEMENT
const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
int instance_idx = selection.get_instance_idx();
if (instance_idx != -1)
{
// First (any) GLVolume of the selected instance. They all share the same instance matrix.
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
const Transform3d& inst_m = v->get_instance_transformation().get_matrix(true);
TriangleMesh vol_mesh(mesh);
vol_mesh.transform(inst_m);
Vec3d vol_shift = -vol_mesh.bounding_box().center();
vol_mesh.translate((float)vol_shift(0), (float)vol_shift(1), (float)vol_shift(2));
Vec3d world_mesh_bb_size = vol_mesh.bounding_box().size();
BoundingBoxf3 inst_bb = (*m_objects)[obj_idx]->instance_bounding_box(instance_idx);
Vec3d world_target = Vec3d(inst_bb.max(0), inst_bb.min(1), inst_bb.min(2)) + 0.5 * world_mesh_bb_size;
new_volume->set_offset(inst_m.inverse() * (world_target - v->get_instance_offset()));
// Transform the new modifier to be aligned with the print bed.
const BoundingBoxf3 mesh_bb = new_volume->mesh.bounding_box();
new_volume->set_transformation(volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb));
// Set the modifier position.
auto offset = (type_name == "Slab") ?
// Slab: Lift to print bed
Vec3d(0., 0., 0.5 * mesh_bb.size().z() + instance_bb.min.z() - v->get_instance_offset().z()) :
// Translate the new modifier to be pickable: move to the left front corner of the instance's bounding box, lift to print bed.
Vec3d(instance_bb.max(0), instance_bb.min(1), instance_bb.min(2)) + 0.5 * mesh_bb.size() - v->get_instance_offset();
new_volume->set_offset(v->get_instance_transformation().get_matrix(true).inverse() * offset);
}
#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT
@ -2048,14 +2140,28 @@ bool ObjectList::has_multi_part_objects()
void ObjectList::update_settings_items()
{
m_prevent_canvas_selection_update = true;
wxDataViewItemArray sel;
GetSelections(sel); // stash selection
wxDataViewItemArray items;
m_objects_model->GetChildren(wxDataViewItem(0), items);
for (auto& item : items) {
const wxDataViewItem& settings_item = m_objects_model->GetSettingsItem(item);
select_item(settings_item ? settings_item : m_objects_model->AddSettingsChild(item));
// If settings item was deleted from the list,
// it's need to be deleted from selection array, if it was there
if (settings_item != m_objects_model->GetSettingsItem(item) &&
sel.Index(settings_item) != wxNOT_FOUND) {
sel.Remove(settings_item);
}
}
UnselectAll();
// restore selection:
SetSelections(sel);
m_prevent_canvas_selection_update = false;
}
void ObjectList::update_object_menu()

View file

@ -127,6 +127,10 @@ class ObjectList : public wxDataViewCtrl
bool m_prevent_update_extruder_in_config = false; // We use this flag to avoid updating of the extruder value in config
// during updating of the extruder count.
bool m_prevent_canvas_selection_update = false; // This flag prevents changing selection on the canvas. See function
// update_settings_items - updating canvas selection is undesirable,
// because it would turn off the gizmos (mainly a problem for the SLA gizmo)
bool m_parts_changed = false;
bool m_part_settings_changed = false;

View file

@ -361,16 +361,21 @@ void ObjectManipulation::change_rotation_value(const Vec3d& rotation)
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
const GLCanvas3D::Selection& selection = canvas->get_selection();
Vec3d delta_rotation = rotation - m_cache.rotation;
GLCanvas3D::TransformationType transformation_type(GLCanvas3D::TransformationType::World_Relative_Joint);
if (selection.is_single_full_instance() || selection.requires_local_axes())
transformation_type.set_independent();
if (selection.is_single_full_instance()) {
//FIXME GLCanvas3D::Selection::rotate() does not process absoulte rotations correctly: It does not recognize the axis index, which was changed.
// transformation_type.set_absolute();
transformation_type.set_local();
}
Vec3d rad_rotation;
for (size_t i = 0; i < 3; ++i)
{
rad_rotation(i) = Geometry::deg2rad(delta_rotation(i));
}
rad_rotation(i) = Geometry::deg2rad((transformation_type.absolute()) ? rotation(i) : rotation(i) - m_cache.rotation(i));
canvas->get_selection().start_dragging();
canvas->get_selection().rotate(rad_rotation, selection.is_single_full_instance() || selection.requires_local_axes());
canvas->get_selection().rotate(rad_rotation, transformation_type);
canvas->do_rotate();
m_cache.rotation = rotation;

View file

@ -2,6 +2,7 @@
#define slic3r_GUI_ObjectSettings_hpp_
#include <memory>
#include <vector>
#include <wx/panel.h>
class wxBoxSizer;

View file

@ -137,13 +137,11 @@ void View3D::mirror_selection(Axis axis)
m_canvas->mirror_selection(axis);
}
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
void View3D::update_toolbar_items_visibility()
{
if (m_canvas != nullptr)
m_canvas->update_toolbar_items_visibility();
}
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
void View3D::enable_toolbar_item(const std::string& name, bool enable)
{
@ -191,7 +189,8 @@ void View3D::reload_scene(bool refresh_immediately, bool force_full_scene_refres
void View3D::render()
{
if (m_canvas != nullptr)
m_canvas->render();
//m_canvas->render();
m_canvas->set_as_dirty();
}
Preview::Preview(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function<void()> schedule_background_process_func)
@ -418,11 +417,14 @@ void Preview::load_print()
load_print_as_sla();
}
void Preview::reload_print(bool force)
void Preview::reload_print(bool force, bool keep_volumes)
{
m_canvas->reset_volumes();
m_canvas->reset_legend_texture();
m_loaded = false;
if (!keep_volumes)
{
m_canvas->reset_volumes();
m_canvas->reset_legend_texture();
m_loaded = false;
}
if (!IsShown() && !force)
return;
@ -563,8 +565,12 @@ void Preview::create_double_slider()
auto& config = wxGetApp().preset_bundle->project_config;
((config.option<ConfigOptionFloats>("colorprint_heights"))->values) = (m_slider->GetTicksValues());
m_schedule_background_process();
bool color_print = !config.option<ConfigOptionFloats>("colorprint_heights")->values.empty();
int type = m_choice_view_type->FindString(color_print ? _(L("Color Print")) : _(L("Feature type")) );
const wxString& choise = !config.option<ConfigOptionFloats>("colorprint_heights")->values.empty() ? _(L("Color Print")) :
config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values.size() > 1 ?
_(L("Tool")) : _(L("Feature type"));
int type = m_choice_view_type->FindString(choise);
if (m_choice_view_type->GetSelection() != type) {
m_choice_view_type->SetSelection(type);
if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types))
@ -637,12 +643,13 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool for
const auto& config = wxGetApp().preset_bundle->project_config;
const std::vector<double> &ticks_from_config = (config.option<ConfigOptionFloats>("colorprint_heights"))->values;
m_slider->SetTicksValues(ticks_from_config);
bool color_print_enable = (wxGetApp().plater()->printer_technology() == ptFFF);
if (color_print_enable) {
const auto& config = wxGetApp().preset_bundle->full_config();
if (config.opt<ConfigOptionFloats>("nozzle_diameter")->values.size() > 1)
const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
if (cfg.opt<ConfigOptionFloats>("nozzle_diameter")->values.size() > 1)
color_print_enable = false;
}
m_slider->EnableTickManipulation(color_print_enable);

View file

@ -60,9 +60,7 @@ public:
void delete_selected();
void mirror_selection(Axis axis);
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
void update_toolbar_items_visibility();
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
void enable_toolbar_item(const std::string& name, bool enable);
int check_volumes_outside_state() const;
@ -129,7 +127,7 @@ public:
void set_drop_target(wxDropTarget* target);
void load_print();
void reload_print(bool force = false);
void reload_print(bool force = false, bool keep_volumes = false);
void refresh_print();
private:

View file

@ -239,32 +239,44 @@ bool ImGuiWrapper::checkbox(const wxString &label, bool &value)
return ImGui::Checkbox(label_utf8.c_str(), &value);
}
void ImGuiWrapper::text(const wxString &label)
void ImGuiWrapper::text(const char *label)
{
auto label_utf8 = into_u8(label);
ImGui::Text(label_utf8.c_str(), NULL);
ImGui::Text(label, NULL);
}
bool ImGuiWrapper::combo(const wxString& label, const std::vector<wxString>& options, wxString& selection)
void ImGuiWrapper::text(const std::string &label)
{
std::string selection_u8 = into_u8(selection);
this->text(label.c_str());
}
void ImGuiWrapper::text(const wxString &label)
{
this->text(into_u8(label).c_str());
}
bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>& options, int& selection)
{
// this is to force the label to the left of the widget:
text(label);
ImGui::SameLine();
if (ImGui::BeginCombo("", selection_u8.c_str())) {
for (const wxString& option : options) {
std::string option_u8 = into_u8(option);
bool is_selected = (selection_u8.empty()) ? false : (option_u8 == selection_u8);
if (ImGui::Selectable(option_u8.c_str(), is_selected))
selection = option_u8;
int selection_out = -1;
bool res = false;
const char *selection_str = selection < options.size() ? options[selection].c_str() : "";
if (ImGui::BeginCombo("", selection_str)) {
for (int i = 0; i < options.size(); i++) {
if (ImGui::Selectable(options[i].c_str(), i == selection)) {
selection_out = i;
}
}
ImGui::EndCombo();
return true;
res = true;
}
return false;
selection = selection_out;
return res;
}
void ImGuiWrapper::disabled_begin(bool disabled)
@ -405,8 +417,8 @@ void ImGuiWrapper::init_style()
static const unsigned COL_GREY_DARK = 0x444444ff;
static const unsigned COL_GREY_LIGHT = 0x666666ff;
static const unsigned COL_ORANGE_DARK = 0xba5418ff;
static const unsigned COL_ORANGE_LIGHT = 0xff6f22ff;
static const unsigned COL_ORANGE_DARK = 0xc16737ff;
static const unsigned COL_ORANGE_LIGHT = 0xff7d38ff;
// Generics
set_color(ImGuiCol_TitleBgActive, COL_ORANGE_DARK);

View file

@ -42,6 +42,8 @@ public:
bool update_mouse_data(wxMouseEvent &evt);
bool update_key_data(wxKeyEvent &evt);
float get_style_scaling() const { return m_style_scaling; }
void new_frame();
void render();
@ -57,8 +59,10 @@ public:
bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f");
bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f");
bool checkbox(const wxString &label, bool &value);
void text(const char *label);
void text(const std::string &label);
void text(const wxString &label);
bool combo(const wxString& label, const std::vector<wxString>& options, wxString& current_selection);
bool combo(const wxString& label, const std::vector<std::string>& options, int& selection); // Use -1 to not mark any option as selected
void disabled_begin(bool disabled);
void disabled_end();

View file

@ -3,6 +3,7 @@
#include <wx/wx.h>
#include <map>
#include <vector>
namespace Slic3r {
namespace GUI {

View file

@ -813,7 +813,7 @@ void Sidebar::show_info_sizer()
p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast<int>(model_object->materials_count())));
auto& stats = model_object->volumes.front()->mesh.stl.stats;
p->object_info->info_volume->SetLabel(wxString::Format("%.2f", size(0) * size(1) * size(2)));
p->object_info->info_volume->SetLabel(wxString::Format("%.2f", stats.volume));
p->object_info->info_facets->SetLabel(wxString::Format(_(L("%d (%d shells)")), static_cast<int>(model_object->facets_count()), stats.number_of_parts));
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
@ -1014,8 +1014,8 @@ struct Plater::priv
Slic3r::GCodePreviewData gcode_preview_data;
// GUI elements
wxSizer* panel_sizer;
wxPanel* current_panel;
wxSizer* panel_sizer{ nullptr };
wxPanel* current_panel{ nullptr };
std::vector<wxPanel*> panels;
Sidebar *sidebar;
Bed3D bed;
@ -1178,7 +1178,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
, sidebar(new Sidebar(q))
, delayed_scene_refresh(false)
, project_filename(wxEmptyString)
#if ENABLE_SVG_ICONS
, view_toolbar(GLToolbar::Radio, "View")
#else
, view_toolbar(GLToolbar::Radio)
#endif // ENABLE_SVG_ICONS
{
arranging.store(false);
rotoptimizing.store(false);
@ -1710,8 +1714,8 @@ void Plater::priv::selection_changed()
view3D->enable_toolbar_item("delete", can_delete_object());
view3D->enable_toolbar_item("more", can_increase_instances());
view3D->enable_toolbar_item("fewer", can_decrease_instances());
view3D->enable_toolbar_item("splitobjects", can_split/*_to_objects*/());
view3D->enable_toolbar_item("splitvolumes", can_split/*_to_volumes*/());
view3D->enable_toolbar_item("splitobjects", can_split());
view3D->enable_toolbar_item("splitvolumes", printer_technology == ptFFF && can_split());
// if the selection is not valid to allow for layer editing, we need to turn off the tool if it is running
bool enable_layer_editing = layers_height_allowed();
@ -2261,7 +2265,8 @@ void Plater::priv::set_current_panel(wxPanel* panel)
else if (current_panel == preview)
{
this->q->reslice();
preview->reload_print();
// keeps current gcode preview, if any
preview->reload_print(false, true);
preview->set_canvas_as_dirty();
view_toolbar.select_item("Preview");
}
@ -2623,11 +2628,11 @@ bool Plater::priv::complit_init_part_menu()
void Plater::priv::init_view_toolbar()
{
#if !ENABLE_SVG_ICONS
ItemsIconsTexture::Metadata icons_data;
icons_data.filename = "view_toolbar.png";
icons_data.icon_size = 64;
icons_data.icon_border_size = 0;
icons_data.icon_gap_size = 0;
#endif // !ENABLE_SVG_ICONS
BackgroundTexture::Metadata background_data;
background_data.filename = "toolbar_background.png";
@ -2636,7 +2641,11 @@ void Plater::priv::init_view_toolbar()
background_data.right = 16;
background_data.bottom = 16;
#if ENABLE_SVG_ICONS
if (!view_toolbar.init(background_data))
#else
if (!view_toolbar.init(icons_data, background_data))
#endif // ENABLE_SVG_ICONS
return;
view_toolbar.set_layout_orientation(GLToolbar::Layout::Bottom);
@ -2646,6 +2655,9 @@ void Plater::priv::init_view_toolbar()
GLToolbarItem::Data item;
item.name = "3D";
#if ENABLE_SVG_ICONS
item.icon_filename = "editor.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = GUI::L_str("3D editor view") + " [" + GUI::shortkey_ctrl_prefix() + "5]";
item.sprite_id = 0;
item.action_event = EVT_GLVIEWTOOLBAR_3D;
@ -2654,6 +2666,9 @@ void Plater::priv::init_view_toolbar()
return;
item.name = "Preview";
#if ENABLE_SVG_ICONS
item.icon_filename = "preview.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = GUI::L_str("Preview") + " [" + GUI::shortkey_ctrl_prefix() + "6]";
item.sprite_id = 1;
item.action_event = EVT_GLVIEWTOOLBAR_PREVIEW;
@ -2749,10 +2764,8 @@ void Plater::priv::set_bed_shape(const Pointfs& shape)
void Plater::priv::update_object_menu()
{
sidebar->obj_list()->append_menu_items_add_volume(&object_menu);
#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS
if (view3D != nullptr)
view3D->update_toolbar_items_visibility();
#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS
}
// Plater / Public
@ -2972,7 +2985,7 @@ void Plater::export_gcode()
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string());
wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save Zip file as:")),
wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save SL1 file as:")),
start_dir,
from_path(default_output_file.filename()),
GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_PNGZIP, default_output_file.extension().string()),
@ -3131,7 +3144,7 @@ void Plater::send_gcode()
}
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
PrintHostSendDialog dlg(default_output_file);
PrintHostSendDialog dlg(default_output_file, upload_job.printhost->can_start_print());
if (dlg.ShowModal() == wxID_OK) {
upload_job.upload_data.upload_path = dlg.filename();
upload_job.upload_data.start_print = dlg.start_print();
@ -3204,9 +3217,6 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
bed_shape_changed = true;
update_scheduled = true;
}
else if (opt_key == "host_type" && this->p->printer_technology == ptSLA) {
p->config->option<ConfigOptionEnum<PrintHostType>>(opt_key)->value = htSL1;
}
}
{

View file

@ -354,7 +354,7 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config)
const std::vector<std::string>& Preset::print_options()
{
static std::vector<std::string> s_opts {
"layer_height", "first_layer_height", "perimeters", "spiral_vase", "top_solid_layers", "bottom_solid_layers",
"layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "top_solid_layers", "bottom_solid_layers",
"extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs",
"seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
"infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle",
@ -460,6 +460,7 @@ const std::vector<std::string>& Preset::sla_print_options()
"support_object_elevation",
"support_points_density_relative",
"support_points_minimal_distance",
"slice_closing_radius",
"pad_enable",
"pad_wall_thickness",
"pad_wall_height",

View file

@ -28,14 +28,15 @@ namespace GUI {
static const char *CONFIG_KEY_PATH = "printhost_path";
static const char *CONFIG_KEY_PRINT = "printhost_print";
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path)
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_print)
: MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE)
, txt_filename(new wxTextCtrl(this, wxID_ANY))
, box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload"))))
, box_print(can_start_print ? new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload"))) : nullptr)
{
#ifdef __APPLE__
txt_filename->OSXDisableAllSmartSubstitutions();
#endif
const AppConfig *app_config = wxGetApp().app_config;
auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed.")));
label_dir_hint->Wrap(CONTENT_WIDTH * wxGetApp().em_unit());
@ -43,12 +44,13 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path)
content_sizer->Add(txt_filename, 0, wxEXPAND);
content_sizer->Add(label_dir_hint);
content_sizer->AddSpacer(VERT_SPACING);
content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING);
if (box_print != nullptr) {
content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING);
box_print->SetValue(app_config->get("recent", CONFIG_KEY_PRINT) == "1");
}
btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL));
const AppConfig *app_config = wxGetApp().app_config;
box_print->SetValue(app_config->get("recent", CONFIG_KEY_PRINT) == "1");
wxString recent_path = from_u8(app_config->get("recent", CONFIG_KEY_PATH));
if (recent_path.Length() > 0 && recent_path[recent_path.Length() - 1] != '/') {
@ -80,7 +82,7 @@ fs::path PrintHostSendDialog::filename() const
bool PrintHostSendDialog::start_print() const
{
return box_print->GetValue();
return box_print != nullptr ? box_print->GetValue() : false;
}
void PrintHostSendDialog::EndModal(int ret)
@ -94,8 +96,7 @@ void PrintHostSendDialog::EndModal(int ret)
wxGetApp().app_config->set("recent", CONFIG_KEY_PATH, into_u8(path));
}
bool print = box_print->GetValue();
GUI::get_app_config()->set("recent", CONFIG_KEY_PRINT, print ? "1" : "0");
GUI::get_app_config()->set("recent", CONFIG_KEY_PRINT, start_print() ? "1" : "0");
}
MsgDialog::EndModal(ret);
@ -157,7 +158,7 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
btn_cancel->Disable();
btn_error = new wxButton(this, wxID_ANY, _(L("Show error message")));
btn_error->Disable();
auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close")));
auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close"))); // Note: The label needs to be present, otherwise we get accelerator bugs on Mac
btnsizer->Add(btn_cancel, 0, wxRIGHT, SPACING);
btnsizer->Add(btn_error, 0);
btnsizer->AddStretchSpacer();

View file

@ -29,7 +29,7 @@ namespace GUI {
class PrintHostSendDialog : public GUI::MsgDialog
{
public:
PrintHostSendDialog(const boost::filesystem::path &path);
PrintHostSendDialog(const boost::filesystem::path &path, bool can_start_print);
boost::filesystem::path filename() const;
bool start_print() const;

View file

@ -1108,12 +1108,14 @@ void TabPrint::build()
optgroup = page->new_optgroup(_(L("Flow")));
optgroup->append_single_option_line("bridge_flow_ratio");
optgroup = page->new_optgroup(_(L("Slicing")));
optgroup->append_single_option_line("slice_closing_radius");
optgroup->append_single_option_line("resolution");
optgroup->append_single_option_line("xy_size_compensation");
optgroup->append_single_option_line("elefant_foot_compensation");
optgroup = page->new_optgroup(_(L("Other")));
optgroup->append_single_option_line("clip_multipart_objects");
optgroup->append_single_option_line("elefant_foot_compensation");
optgroup->append_single_option_line("xy_size_compensation");
// # optgroup->append_single_option_line("threads");
optgroup->append_single_option_line("resolution");
page = add_options_page(_(L("Output options")), "page_white_go.png");
optgroup = page->new_optgroup(_(L("Sequential printing")));
@ -1624,8 +1626,6 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
// Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment)
if (! sla) {
optgroup->append_single_option_line("host_type");
} else {
m_config->option<ConfigOptionEnum<PrintHostType>>("host_type", true)->value = htSL1;
}
auto printhost_browse = [this, optgroup] (wxWindow* parent) {
@ -2594,7 +2594,7 @@ void Tab::select_preset(std::string preset_name)
// The preset will be switched to a different, compatible preset, or the '-- default --'.
if (pu.technology == new_printer_technology)
m_dependent_tabs.emplace_back(pu.tab_type);
if (pu.old_preset_dirty)
if (pu.old_preset_dirty && !pu.new_preset_compatible)
pu.presets->discard_current_changes();
}
}
@ -3292,6 +3292,10 @@ void TabSLAPrint::build()
// optgroup->append_single_option_line("pad_edge_radius");
optgroup->append_single_option_line("pad_wall_slope");
page = add_options_page(_(L("Advanced")), "wrench.png");
optgroup = page->new_optgroup(_(L("Slicing")));
optgroup->append_single_option_line("slice_closing_radius");
page = add_options_page(_(L("Output options")), "page_white_go.png");
optgroup = page->new_optgroup(_(L("Output file")));
Option option = optgroup->get_option("output_filename_format");

View file

@ -42,7 +42,6 @@ wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const
wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
std::function<void(wxCommandEvent& event)> cb, const std::string& icon, wxEvtHandler* event_handler)
{
// const wxBitmap& bmp = !icon.empty() ? wxBitmap(from_u8(Slic3r::var(icon)), wxBITMAP_TYPE_PNG) : wxNullBitmap;
const wxBitmap& bmp = !icon.empty() ? create_scaled_bitmap(icon) : wxNullBitmap;
return append_menu_item(menu, id, string, description, cb, bmp, event_handler);
}
@ -54,7 +53,6 @@ wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxStrin
wxMenuItem* item = new wxMenuItem(menu, id, string, description);
if (!icon.empty())
// item->SetBitmap(wxBitmap(from_u8(Slic3r::var(icon)), wxBITMAP_TYPE_PNG));
item->SetBitmap(create_scaled_bitmap(icon));
item->SetSubMenu(sub_menu);
@ -421,11 +419,9 @@ wxBitmap create_scaled_bitmap(const std::string& bmp_name)
}
void PrusaObjectDataViewModelNode::set_object_action_icon() {
// m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("add_object.png")), wxBITMAP_TYPE_PNG);
m_action_icon = create_scaled_bitmap("add_object.png");
}
void PrusaObjectDataViewModelNode::set_part_action_icon() {
// m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var(m_type == itVolume ? "cog.png" : "brick_go.png")), wxBITMAP_TYPE_PNG);
m_action_icon = create_scaled_bitmap(m_type == itVolume ? "cog.png" : "brick_go.png");
}
@ -1436,32 +1432,22 @@ PrusaDoubleSlider::PrusaDoubleSlider(wxWindow *parent,
m_min_value(minValue), m_max_value(maxValue),
m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL)
{
#ifndef __WXOSX__ // SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX
SetDoubleBuffered(true);
#ifdef __WXOSX__
is_osx = true;
#endif //__WXOSX__
if (!is_osx)
SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX
// m_bmp_thumb_higher = wxBitmap(style == wxSL_HORIZONTAL ? Slic3r::GUI::from_u8(Slic3r::var("right_half_circle.png")) :
// Slic3r::GUI::from_u8(Slic3r::var("up_half_circle.png")), wxBITMAP_TYPE_PNG);
// m_bmp_thumb_lower = wxBitmap(style == wxSL_HORIZONTAL ? Slic3r::GUI::from_u8(Slic3r::var("left_half_circle.png")) :
// Slic3r::GUI::from_u8(Slic3r::var("down_half_circle.png")), wxBITMAP_TYPE_PNG);
m_bmp_thumb_higher = wxBitmap(create_scaled_bitmap(style == wxSL_HORIZONTAL ? "right_half_circle.png" : "up_half_circle.png"));
m_bmp_thumb_lower = wxBitmap(create_scaled_bitmap(style == wxSL_HORIZONTAL ? "left_half_circle.png" : "down_half_circle.png"));
m_thumb_size = m_bmp_thumb_lower.GetSize();
// m_bmp_add_tick_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_add_on.png")), wxBITMAP_TYPE_PNG);
// m_bmp_add_tick_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_add_off.png")), wxBITMAP_TYPE_PNG);
// m_bmp_del_tick_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_delete_on.png")), wxBITMAP_TYPE_PNG);
// m_bmp_del_tick_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_delete_off.png")), wxBITMAP_TYPE_PNG);
m_bmp_add_tick_on = create_scaled_bitmap("colorchange_add_on.png");
m_bmp_add_tick_off = create_scaled_bitmap("colorchange_add_off.png");
m_bmp_del_tick_on = create_scaled_bitmap("colorchange_delete_on.png");
m_bmp_del_tick_off = create_scaled_bitmap("colorchange_delete_off.png");
m_tick_icon_dim = m_bmp_add_tick_on.GetSize().x;
// m_bmp_one_layer_lock_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_lock_on.png")), wxBITMAP_TYPE_PNG);
// m_bmp_one_layer_lock_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_lock_off.png")), wxBITMAP_TYPE_PNG);
// m_bmp_one_layer_unlock_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_unlock_on.png")), wxBITMAP_TYPE_PNG);
// m_bmp_one_layer_unlock_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_unlock_off.png")), wxBITMAP_TYPE_PNG);
m_bmp_one_layer_lock_on = create_scaled_bitmap("one_layer_lock_on.png");
m_bmp_one_layer_lock_off = create_scaled_bitmap("one_layer_lock_off.png");
m_bmp_one_layer_unlock_on = create_scaled_bitmap("one_layer_unlock_on.png");
@ -1496,6 +1482,9 @@ PrusaDoubleSlider::PrusaDoubleSlider(wxWindow *parent,
line_pens = { &DARK_GREY_PEN, &GREY_PEN, &LIGHT_GREY_PEN };
segm_pens = { &DARK_ORANGE_PEN, &ORANGE_PEN, &LIGHT_ORANGE_PEN };
const wxFont& font = GetFont();
m_font = is_osx ? font.Smaller().Smaller() : font.Smaller();
}
int PrusaDoubleSlider::GetActiveValue() const
@ -1510,7 +1499,9 @@ wxSize PrusaDoubleSlider::DoGetBestSize() const
const wxSize size = wxControl::DoGetBestSize();
if (size.x > 1 && size.y > 1)
return size;
const int new_size = is_horizontal() ? 6 * Slic3r::GUI::wxGetApp().em_unit() : 8 * Slic3r::GUI::wxGetApp().em_unit();
const int new_size = is_horizontal() ?
(is_osx ? 8 : 6) * Slic3r::GUI::wxGetApp().em_unit() :
(is_osx ? 10 : 8) * Slic3r::GUI::wxGetApp().em_unit();
return wxSize(new_size, new_size);
}
@ -1574,7 +1565,7 @@ void PrusaDoubleSlider::draw_scroll_line(wxDC& dc, const int lower_pos, const in
wxCoord line_end_y = is_horizontal() ? height*0.5 - 1 : height - SLIDER_MARGIN + 1;
wxCoord segm_beg_x = is_horizontal() ? lower_pos : width*0.5 - 1;
wxCoord segm_beg_y = is_horizontal() ? height*0.5 - 1 : lower_pos-1;
wxCoord segm_beg_y = is_horizontal() ? height*0.5 - 1 : lower_pos/*-1*/;
wxCoord segm_end_x = is_horizontal() ? higher_pos : width*0.5 - 1;
wxCoord segm_end_y = is_horizontal() ? height*0.5 - 1 : higher_pos-1;
@ -1635,8 +1626,11 @@ std::vector<double> PrusaDoubleSlider::GetTicksValues() const
std::vector<double> values;
if (!m_values.empty())
for (auto tick : m_ticks)
for (auto tick : m_ticks) {
if (tick > m_values.size())
break;
values.push_back(m_values[tick].second);
}
return values;
}
@ -1646,6 +1640,8 @@ void PrusaDoubleSlider::SetTicksValues(const std::vector<double>& heights)
if (m_values.empty())
return;
const bool was_empty = m_ticks.empty();
m_ticks.clear();
unsigned int i = 0;
for (auto h : heights) {
@ -1655,7 +1651,10 @@ void PrusaDoubleSlider::SetTicksValues(const std::vector<double>& heights)
return;
m_ticks.insert(i-1);
}
if (!was_empty && m_ticks.empty())
// Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one
wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
}
void PrusaDoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos)
@ -1689,9 +1688,7 @@ void PrusaDoubleSlider::render()
draw_focus_rect();
wxPaintDC dc(this);
wxFont font = dc.GetFont();
const wxFont smaller_font = font.Smaller();
dc.SetFont(smaller_font);
dc.SetFont(m_font);
const wxCoord lower_pos = get_position_from_value(m_lower_value);
const wxCoord higher_pos = get_position_from_value(m_higher_value);
@ -1743,8 +1740,8 @@ void PrusaDoubleSlider::draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, c
if (m_selection == selection) {
//draw info line
dc.SetPen(DARK_ORANGE_PEN);
const wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x, pos.y - m_thumb_size.y) : wxPoint(pos.x - m_thumb_size.x, pos.y - 1);
const wxPoint pt_end = is_horizontal() ? wxPoint(pos.x, pos.y + m_thumb_size.y) : wxPoint(pos.x + m_thumb_size.x, pos.y - 1);
const wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x, pos.y - m_thumb_size.y) : wxPoint(pos.x - m_thumb_size.x, pos.y/* - 1*/);
const wxPoint pt_end = is_horizontal() ? wxPoint(pos.x, pos.y + m_thumb_size.y) : wxPoint(pos.x + m_thumb_size.x, pos.y/* - 1*/);
dc.DrawLine(pt_beg, pt_end);
//draw action icon
@ -1795,7 +1792,7 @@ void PrusaDoubleSlider::draw_thumb_item(wxDC& dc, const wxPoint& pos, const Sele
}
else {
x_draw = pos.x - int(0.5*m_thumb_size.x);
y_draw = pos.y;
y_draw = pos.y+1;
}
}
else{
@ -1866,9 +1863,9 @@ void PrusaDoubleSlider::draw_ticks(wxDC& dc)
const wxCoord pos = get_position_from_value(tick);
is_horizontal() ? dc.DrawLine(pos, mid-14, pos, mid-9) :
dc.DrawLine(mid - 14, pos - 1, mid - 9, pos - 1);
dc.DrawLine(mid - 14, pos/* - 1*/, mid - 9, pos/* - 1*/);
is_horizontal() ? dc.DrawLine(pos, mid+14, pos, mid+9) :
dc.DrawLine(mid + 14, pos - 1, mid + 9, pos - 1);
dc.DrawLine(mid + 14, pos/* - 1*/, mid + 9, pos/* - 1*/);
}
}
@ -2283,11 +2280,6 @@ PrusaLockButton::PrusaLockButton( wxWindow *parent,
const wxSize& size /*= wxDefaultSize*/):
wxButton(parent, id, wxEmptyString, pos, size, wxBU_EXACTFIT | wxNO_BORDER)
{
// m_bmp_lock_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_lock_on.png")), wxBITMAP_TYPE_PNG);
// m_bmp_lock_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_lock_off.png")), wxBITMAP_TYPE_PNG);
// m_bmp_unlock_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_unlock_on.png")), wxBITMAP_TYPE_PNG);
// m_bmp_unlock_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_unlock_off.png")), wxBITMAP_TYPE_PNG);
m_bmp_lock_on = create_scaled_bitmap("one_layer_lock_on.png");
m_bmp_lock_off = create_scaled_bitmap("one_layer_lock_off.png");
m_bmp_unlock_on = create_scaled_bitmap("one_layer_unlock_on.png");
@ -2349,9 +2341,11 @@ PrusaModeButton::PrusaModeButton( wxWindow *parent,
#ifdef __WXMSW__
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
#endif // __WXMSW__
// m_bmp_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("mode_off_sq.png")), wxBITMAP_TYPE_PNG);
m_bmp_off = create_scaled_bitmap("mode_off_sq.png");
m_tt_focused = wxString::Format(_(L("Switch to the %s mode")), mode);
m_tt_selected = wxString::Format(_(L("Current mode is %s")), mode);
SetBitmap(m_bmp_on);
//button events
@ -2372,6 +2366,7 @@ void PrusaModeButton::SetState(const bool state)
{
m_is_selected = state;
focus_button(m_is_selected);
SetToolTip(state ? m_tt_selected : m_tt_focused);
}
void PrusaModeButton::focus_button(const bool focus)

View file

@ -791,6 +791,8 @@ protected:
double get_double_value(const SelectedSlider& selection);
private:
bool is_osx { false };
wxFont m_font;
int m_min_value;
int m_max_value;
int m_lower_value;
@ -906,6 +908,8 @@ private:
wxBitmap m_bmp_on;
wxBitmap m_bmp_off;
wxString m_tt_selected;
wxString m_tt_focused;
};

View file

@ -5,7 +5,6 @@
#include <array>
#include <vector>
#include <string>
#include <random>
#include <thread>
#include <boost/optional.hpp>
#include <boost/system/error_code.hpp>
@ -34,6 +33,9 @@ namespace Slic3r {
// the implementations has been tested with AFL.
// Relevant RFC: https://www.ietf.org/rfc/rfc6762.txt
struct DnsName: public std::string
{
enum
@ -387,7 +389,7 @@ struct DnsMessage
DnsSDMap sdmap;
static optional<DnsMessage> decode(const std::vector<char> &buffer, optional<uint16_t> id_wanted = boost::none)
static optional<DnsMessage> decode(const std::vector<char> &buffer)
{
const auto size = buffer.size();
if (size < DnsHeader::SIZE + DnsQuestion::MIN_SIZE || size > MAX_SIZE) {
@ -397,10 +399,6 @@ struct DnsMessage
DnsMessage res;
res.header = DnsHeader::decode(buffer);
if (id_wanted && *id_wanted != res.header.id) {
return boost::none;
}
if (res.header.qdcount > 1 || res.header.ancount > MAX_ANS) {
return boost::none;
}
@ -472,16 +470,12 @@ struct BonjourRequest
static const asio::ip::address_v4 MCAST_IP4;
static const uint16_t MCAST_PORT;
uint16_t id;
std::vector<char> data;
static optional<BonjourRequest> make(const std::string &service, const std::string &protocol);
private:
BonjourRequest(uint16_t id, std::vector<char> &&data) :
id(id),
data(std::move(data))
{}
BonjourRequest(std::vector<char> &&data) : data(std::move(data)) {}
};
const asio::ip::address_v4 BonjourRequest::MCAST_IP4{0xe00000fb};
@ -493,22 +487,15 @@ optional<BonjourRequest> BonjourRequest::make(const std::string &service, const
return boost::none;
}
std::random_device dev;
std::uniform_int_distribution<uint16_t> dist;
uint16_t id = dist(dev);
uint16_t id_big = endian::native_to_big(id);
const char *id_char = reinterpret_cast<char*>(&id_big);
std::vector<char> data;
data.reserve(service.size() + 18);
// Add the transaction ID
data.push_back(id_char[0]);
data.push_back(id_char[1]);
// Add metadata
static const unsigned char rq_meta[] = {
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
0x00, 0x00, // Query ID (zero for mDNS)
0x00, 0x00, // Flags
0x00, 0x01, // One query
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Zero Answer, Authority, and Additional RRs
};
std::copy(rq_meta, rq_meta + sizeof(rq_meta), std::back_inserter(data));
@ -522,11 +509,14 @@ optional<BonjourRequest> BonjourRequest::make(const std::string &service, const
// Add the rest of PTR record
static const unsigned char ptr_tail[] = {
0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00, 0x0c, 0x00, 0xff,
0x05, // length of "label"
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, // "label" string and terminator
0x00, 0x0c, // Type PTR
0x00, 0xff, // Class ANY
};
std::copy(ptr_tail, ptr_tail + sizeof(ptr_tail), std::back_inserter(data));
return BonjourRequest(id, std::move(data));
return BonjourRequest(std::move(data));
}
@ -539,7 +529,6 @@ struct Bonjour::priv
const std::string service_dn;
unsigned timeout;
unsigned retries;
uint16_t rq_id;
std::vector<char> buffer;
std::thread io_thread;
@ -558,8 +547,7 @@ Bonjour::priv::priv(std::string service, std::string protocol) :
protocol(std::move(protocol)),
service_dn((boost::format("_%1%._%2%.local") % this->service % this->protocol).str()),
timeout(10),
retries(1),
rq_id(0)
retries(1)
{
buffer.resize(DnsMessage::MAX_SIZE);
}
@ -585,7 +573,7 @@ void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
}
buffer.resize(bytes);
const auto dns_msg = DnsMessage::decode(buffer, rq_id);
const auto dns_msg = DnsMessage::decode(buffer);
if (dns_msg) {
asio::ip::address ip = from.address();
if (dns_msg->rr_a) { ip = dns_msg->rr_a->ip; }
@ -629,7 +617,6 @@ void Bonjour::priv::lookup_perform()
}
auto self = this;
rq_id = brq->id;
try {
boost::asio::io_service io_service;

View file

@ -34,6 +34,8 @@ Duet::Duet(DynamicPrintConfig *config) :
Duet::~Duet() {}
const char* Duet::get_name() const { return "Duet"; }
bool Duet::test(wxString &msg) const
{
bool connected = connect(msg);
@ -119,6 +121,11 @@ bool Duet::can_test() const
return true;
}
bool Duet::can_start_print() const
{
return true;
}
bool Duet::connect(wxString &msg) const
{
bool res = false;

View file

@ -19,12 +19,15 @@ public:
Duet(DynamicPrintConfig *config);
virtual ~Duet();
virtual const char* get_name() const;
virtual bool test(wxString &curl_msg) const;
virtual wxString get_test_ok_msg () const;
virtual wxString get_test_failed_msg (wxString &msg) const;
virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const;
virtual bool has_auto_discovery() const;
virtual bool can_test() const;
virtual bool can_start_print() const;
virtual std::string get_host() const { return host; }
private:

View file

@ -29,25 +29,29 @@ OctoPrint::OctoPrint(DynamicPrintConfig *config) :
OctoPrint::~OctoPrint() {}
const char* OctoPrint::get_name() const { return "OctoPrint"; }
bool OctoPrint::test(wxString &msg) const
{
// Since the request is performed synchronously here,
// it is ok to refer to `msg` from within the closure
const char *name = get_name();
bool res = true;
auto url = make_url("api/version");
BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Get version at: %1%") % url;
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
auto http = Http::get(std::move(url));
set_auth(http);
http.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error getting version: %1%, HTTP %2%, body: `%3%`") % error % status % body;
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
res = false;
msg = format_error(body, error, status);
})
.on_complete([&, this](std::string body, unsigned) {
BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: Got version: %1%") % body;
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body;
try {
std::stringstream ss(body);
@ -88,6 +92,8 @@ wxString OctoPrint::get_test_failed_msg (wxString &msg) const
bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
{
const char *name = get_name();
const auto upload_filename = upload_data.upload_path.filename();
const auto upload_parent_path = upload_data.upload_path.parent_path();
@ -101,7 +107,8 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
auto url = make_url("api/files/local");
BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%")
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%")
% name
% upload_data.source_path
% url
% upload_filename.string()
@ -114,10 +121,10 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
.form_add_file("file", upload_data.source_path.string(), upload_filename.string())
.on_complete([&](std::string body, unsigned status) {
BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body;
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body;
})
.on_error([&](std::string body, std::string error, unsigned status) {
BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
error_fn(format_error(body, error, status));
res = false;
})
@ -144,6 +151,11 @@ bool OctoPrint::can_test() const
return true;
}
bool OctoPrint::can_start_print() const
{
return true;
}
bool OctoPrint::validate_version_text(const boost::optional<std::string> &version_text) const
{
return version_text ? boost::starts_with(*version_text, "OctoPrint") : true;
@ -172,21 +184,28 @@ std::string OctoPrint::make_url(const std::string &path) const
}
// SLAHost
// SL1Host
SLAHost::~SLAHost() {}
SL1Host::~SL1Host() {}
wxString SLAHost::get_test_ok_msg () const
const char* SL1Host::get_name() const { return "SL1Host"; }
wxString SL1Host::get_test_ok_msg () const
{
return _(L("Connection to Prusa SLA works correctly."));
}
wxString SLAHost::get_test_failed_msg (wxString &msg) const
wxString SL1Host::get_test_failed_msg (wxString &msg) const
{
return wxString::Format("%s: %s", _(L("Could not connect to Prusa SLA")), msg);
}
bool SLAHost::validate_version_text(const boost::optional<std::string> &version_text) const
bool SL1Host::can_start_print() const
{
return false;
}
bool SL1Host::validate_version_text(const boost::optional<std::string> &version_text) const
{
return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false;
}

View file

@ -20,12 +20,15 @@ public:
OctoPrint(DynamicPrintConfig *config);
virtual ~OctoPrint();
virtual const char* get_name() const;
virtual bool test(wxString &curl_msg) const;
virtual wxString get_test_ok_msg () const;
virtual wxString get_test_failed_msg (wxString &msg) const;
virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const;
virtual bool has_auto_discovery() const;
virtual bool can_test() const;
virtual bool can_start_print() const;
virtual std::string get_host() const { return host; }
protected:
@ -41,14 +44,17 @@ private:
};
class SLAHost: public OctoPrint
class SL1Host: public OctoPrint
{
public:
SLAHost(DynamicPrintConfig *config) : OctoPrint(config) {}
virtual ~SLAHost();
SL1Host(DynamicPrintConfig *config) : OctoPrint(config) {}
virtual ~SL1Host();
virtual const char* get_name() const;
virtual wxString get_test_ok_msg () const;
virtual wxString get_test_failed_msg (wxString &msg) const;
virtual bool can_start_print() const ;
protected:
virtual bool validate_version_text(const boost::optional<std::string> &version_text) const;
};

View file

@ -27,14 +27,26 @@ PrintHost::~PrintHost() {}
PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
{
const auto opt = config->option<ConfigOptionEnum<PrintHostType>>("host_type");
if (opt == nullptr) { return nullptr; }
PrinterTechnology tech = ptFFF;
switch (opt->value) {
case htOctoPrint: return new OctoPrint(config);
case htDuet: return new Duet(config);
case htSL1: return new SLAHost(config);
default: return nullptr;
{
const auto opt = config->option<ConfigOptionEnum<PrinterTechnology>>("printer_technology");
if (opt != nullptr) {
tech = opt->value;
}
}
if (tech == ptFFF) {
const auto opt = config->option<ConfigOptionEnum<PrintHostType>>("host_type");
const auto host_type = opt != nullptr ? opt->value : htOctoPrint;
switch (host_type) {
case htOctoPrint: return new OctoPrint(config);
case htDuet: return new Duet(config);
default: return nullptr;
}
} else {
return new SL1Host(config);
}
}

View file

@ -32,12 +32,15 @@ public:
typedef Http::ProgressFn ProgressFn;
typedef std::function<void(wxString /* error */)> ErrorFn;
virtual const char* get_name() const = 0;
virtual bool test(wxString &curl_msg) const = 0;
virtual wxString get_test_ok_msg () const = 0;
virtual wxString get_test_failed_msg (wxString &msg) const = 0;
virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const = 0;
virtual bool has_auto_discovery() const = 0;
virtual bool can_test() const = 0;
virtual bool can_start_print() const = 0;
virtual std::string get_host() const = 0;
static PrintHost* get_print_host(DynamicPrintConfig *config);