mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Merge branch 'master' into tm_colldetection_upgr
This commit is contained in:
		
						commit
						a7c07960ee
					
				
					 116 changed files with 5089 additions and 16124 deletions
				
			
		|  | @ -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) | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  |  | |||
							
								
								
									
										424
									
								
								src/avrdude/avrdude-slic3r.conf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										424
									
								
								src/avrdude/avrdude-slic3r.conf
									
										
									
									
									
										Normal 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"; | ||||
|       ; | ||||
|   ; | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										1188
									
								
								src/avrdude/avrdude-slic3r.conf.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1188
									
								
								src/avrdude/avrdude-slic3r.conf.h
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -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)) {} | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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) | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										41
									
								
								src/avrdude/conf-generate.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/avrdude/conf-generate.cpp
									
										
									
									
									
										Normal 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; | ||||
| } | ||||
|  | @ -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; | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  |  | |||
|  | @ -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); | ||||
| } | ||||
							
								
								
									
										54
									
								
								src/avrdude/main-standalone.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/avrdude/main-standalone.cpp
									
										
									
									
									
										Normal 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 | ||||
|  | @ -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); | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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++; | ||||
|  |  | |||
|  | @ -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; | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
							
								
								
									
										45
									
								
								src/avrdude/windows/utf8.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/avrdude/windows/utf8.c
									
										
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										10
									
								
								src/avrdude/windows/utf8.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/avrdude/windows/utf8.h
									
										
									
									
									
										Normal 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
 | ||||
|  | @ -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; | ||||
| } | ||||
|  |  | |||
|  | @ -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.
 | ||||
|  |  | |||
|  | @ -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"; | ||||
|  |  | |||
|  | @ -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"; | ||||
|  |  | |||
|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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); } | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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 = ""; | ||||
|     } | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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"); | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 " | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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"; | ||||
|  |  | |||
|  | @ -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); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 {  | ||||
|  |  | |||
|  | @ -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(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  |  | |||
|  | @ -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" | ||||
|  |  | |||
|  | @ -149,6 +149,7 @@ SlicingParameters SlicingParameters::create_from_config( | |||
|         params.object_print_z_max += print_z; | ||||
|     } | ||||
| 
 | ||||
|     params.valid = true; | ||||
|     return params; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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               && | ||||
|  |  | |||
|  | @ -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_
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -92,8 +92,8 @@ AboutDialog::AboutDialog() | |||
|             "<html>" | ||||
|             "<body bgcolor= %s link= %s>" | ||||
|             "<font color=%s>" | ||||
|             "Copyright © 2016-2018 Prusa Research. <br />" | ||||
|             "Copyright © 2011-2017 Alessandro Ranellucci. <br />" | ||||
|             "Copyright © 2016-2019 Prusa Research. <br />" | ||||
|             "Copyright © 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 />" | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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
											
										
									
								
							|  | @ -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); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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)
 | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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() | ||||
|  |  | |||
|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| #define slic3r_GUI_ObjectSettings_hpp_ | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| #include <wx/panel.h> | ||||
| 
 | ||||
| class wxBoxSizer; | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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: | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
| 
 | ||||
| #include <wx/wx.h> | ||||
| #include <map> | ||||
| #include <vector> | ||||
| 
 | ||||
| namespace Slic3r {  | ||||
| namespace GUI { | ||||
|  |  | |||
|  | @ -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; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|  |  | |||
|  | @ -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", | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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"); | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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: | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
| }; | ||||
|  |  | |||
|  | @ -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); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 tamasmeszaros
						tamasmeszaros