diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ecf8a9177d..2bc74fd82e 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,13 +1,13 @@ -Did you encounter an issue with using Slic3r? Fear not! This guide will help you to write a good bug report in just a few, simple steps. +Did you encounter an issue with using PrusaSlicer? Fear not! This guide will help you to write a good bug report in just a few, simple steps. -There is a good chance that the issue, you have encountered, is already reported. Please check the [list of reported issues](https://github.com/alexrj/Slic3r/issues) before creating a new issue report. If you find an existing issue report, feel free to add further information to that report. +There is a good chance that the issue, you have encountered, is already reported. Please check the [list of reported issues](https://github.com/prusa3d/PrusaSlicer/issues) before creating a new issue report. If you find an existing issue report, feel free to add further information to that report. -If possible, please include the following information when [reporting an issue](https://github.com/alexrj/Slic3r/issues/new): -* Slic3r version (See the about dialog for the version number. If running from git, please include the git commit ID from `git rev-parse HEAD` also.) +If possible, please include the following information when [reporting an issue](https://github.com/prusa3d/PrusaSlicer/issues/new): +* PrusaSlicer version (See the about dialog for the version number. If running from git, please include the git commit ID from `git rev-parse HEAD` also.) * Operating system type + version * Steps to reproduce the issue, including: * Command line parameters used, if any - * Slic3r configuration file (Use ``Export Config...`` from the ``File`` menu - please don't export a bundle) + * PrusaSlicer configuration file (Use ``Export Config...`` from the ``File`` menu - please don't export a bundle) * Expected result * Actual result * Any error messages @@ -17,5 +17,5 @@ If possible, please include the following information when [reporting an issue]( Please make sure only to include one issue per report. If you encounter multiple, unrelated issues, please report them as such. -Simon Tatham has written an excellent on article on [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) which is well worth reading, although it is not specific to Slic3r. +Simon Tatham has written an excellent on article on [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) which is well worth reading, although it is not specific to PrusaSlicer. diff --git a/CMakeLists.txt b/CMakeLists.txt index 52be8e8473..691e234aed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -369,9 +369,9 @@ include_directories(BEFORE SYSTEM ${EIGEN3_INCLUDE_DIR}) # Find expat or use bundled version # Always use the system libexpat on Linux. -if (NOT SLIC3R_STATIC OR CMAKE_SYSTEM_NAME STREQUAL "Linux") - find_package(EXPAT) -endif () + +find_package(EXPAT) + if (NOT EXPAT_FOUND) add_library(expat STATIC ${LIBDIR}/expat/xmlparse.c @@ -382,7 +382,8 @@ if (NOT EXPAT_FOUND) set(EXPAT_INCLUDE_DIRS ${LIBDIR}/expat/) set(EXPAT_LIBRARIES expat) endif () -include_directories(${EXPAT_INCLUDE_DIRS}) + +find_package(PNG REQUIRED) find_package(OpenGL REQUIRED) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 8702fd6d62..3251d2c622 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -34,8 +34,10 @@ endif () set(DESTDIR "${CMAKE_CURRENT_BINARY_DIR}/destdir" CACHE PATH "Destination directory") option(DEP_DEBUG "Build debug variants (only applicable on Windows)" ON) -option(DEP_WX_STABLE "Build against wxWidgets stable 3.0 as opposed to default 3.1 (Linux only)" OFF) -option(DEP_WX_GTK3 "Build wxWidgets against GTK3" OFF) + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + option(DEP_WX_GTK3 "Build wxWidgets against GTK3" OFF) +endif() # On developer machines, it can be enabled to speed up compilation and suppress warnings coming from IGL. # FIXME: @@ -127,12 +129,28 @@ else() include("deps-linux.cmake") endif() +set(ZLIB_PKG "") +if (NOT ZLIB_FOUND) + include(ZLIB/ZLIB.cmake) + set(ZLIB_PKG dep_ZLIB) +endif () +set(PNG_PKG "") +if (NOT PNG_FOUND) + include(PNG/PNG.cmake) + set(PNG_PKG dep_PNG) +endif () +set(EXPAT_PKG "") +if (NOT EXPAT_FOUND) + include(EXPAT/EXPAT.cmake) + set(EXPAT_PKG dep_EXPAT) +endif () + include(GLEW/GLEW.cmake) include(OpenCSG/OpenCSG.cmake) - include(GMP/GMP.cmake) include(MPFR/MPFR.cmake) include(CGAL/CGAL.cmake) +include(wxWidgets/wxWidgets.cmake) if (MSVC) @@ -141,15 +159,17 @@ if (MSVC) dep_boost dep_tbb dep_libcurl - dep_wxwidgets + dep_wxWidgets dep_gtest dep_cereal dep_nlopt # dep_qhull # Experimental - dep_ZLIB # on Windows we still need zlib dep_openvdb dep_OpenCSG dep_CGAL + ${PNG_PKG} + ${ZLIB_PKG} + ${EXPAT_PKG} ) else() @@ -159,7 +179,7 @@ else() dep_boost dep_tbb dep_libcurl - dep_wxwidgets + dep_wxWidgets dep_gtest dep_cereal dep_nlopt @@ -167,6 +187,9 @@ else() dep_openvdb dep_OpenCSG dep_CGAL + ${PNG_PKG} + ${ZLIB_PKG} + ${EXPAT_PKG} # dep_libigl # Not working, static build has different Eigen ) diff --git a/deps/EXPAT/EXPAT.cmake b/deps/EXPAT/EXPAT.cmake new file mode 100644 index 0000000000..ed5eb220f4 --- /dev/null +++ b/deps/EXPAT/EXPAT.cmake @@ -0,0 +1,9 @@ +prusaslicer_add_cmake_project(EXPAT + # GIT_REPOSITORY https://github.com/nigels-com/glew.git + # GIT_TAG 3a8eff7 # 2.1.0 + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/expat +) + +if (MSVC) + add_debug_dep(dep_EXPAT) +endif () diff --git a/deps/EXPAT/expat/CMakeLists.txt b/deps/EXPAT/expat/CMakeLists.txt new file mode 100644 index 0000000000..fa54c098f2 --- /dev/null +++ b/deps/EXPAT/expat/CMakeLists.txt @@ -0,0 +1,71 @@ +cmake_minimum_required(VERSION 3.0) + +project(EXPAT) + +if (BUILD_SHARED_LIBS AND MSVC) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +endif() + +add_library(expat + xmlparse.c + xmlrole.c + xmltok.c +) + +target_include_directories(expat PRIVATE ${PROJECT_SOURCE_DIR}) + +include(GNUInstallDirs) + +install( + FILES + ${PROJECT_SOURCE_DIR}/expat.h + ${PROJECT_SOURCE_DIR}/expat_config.h + ${PROJECT_SOURCE_DIR}/expat_external.h + DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR} +) + +add_library(EXPAT INTERFACE) +target_link_libraries(EXPAT INTERFACE expat) + +include(CMakePackageConfigHelpers) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + VERSION 1.95 + COMPATIBILITY AnyNewerVersion +) + +install(TARGETS expat EXPAT + EXPORT ${PROJECT_NAME}Targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +export(EXPORT ${PROJECT_NAME}Targets + FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake" + NAMESPACE ${PROJECT_NAME}:: ) + +set(ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) + +install(EXPORT ${PROJECT_NAME}Targets + FILE + "${PROJECT_NAME}Targets.cmake" + NAMESPACE + ${PROJECT_NAME}:: + DESTINATION + ${ConfigPackageLocation} +) + +configure_file(config.cmake.in ${PROJECT_NAME}Config.cmake @ONLY) + +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION + ${ConfigPackageLocation} +) + diff --git a/deps/EXPAT/expat/COPYING b/deps/EXPAT/expat/COPYING new file mode 100644 index 0000000000..092c83baee --- /dev/null +++ b/deps/EXPAT/expat/COPYING @@ -0,0 +1,21 @@ +Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper +Copyright (c) 2001-2016 Expat maintainers + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/deps/EXPAT/expat/README b/deps/EXPAT/expat/README new file mode 100644 index 0000000000..40873a6f8b --- /dev/null +++ b/deps/EXPAT/expat/README @@ -0,0 +1,146 @@ +Expat, Release 2.2.0, stripped and modified for inclusion into Slic3r. +Only the library sources needed for static linking were left. + +The original README follows: +--------------------------------------------------------------------- + + + + Expat, Release 2.2.0 + +This is Expat, a C library for parsing XML, written by James Clark. +Expat is a stream-oriented XML parser. This means that you register +handlers with the parser before starting the parse. These handlers +are called when the parser discovers the associated structures in the +document being parsed. A start tag is an example of the kind of +structures for which you may register handlers. + +Windows users should use the expat_win32bin package, which includes +both precompiled libraries and executables, and source code for +developers. + +Expat is free software. You may copy, distribute, and modify it under +the terms of the License contained in the file COPYING distributed +with this package. This license is the same as the MIT/X Consortium +license. + +Versions of Expat that have an odd minor version (the middle number in +the release above), are development releases and should be considered +as beta software. Releases with even minor version numbers are +intended to be production grade software. + +If you are building Expat from a check-out from the CVS repository, +you need to run a script that generates the configure script using the +GNU autoconf and libtool tools. To do this, you need to have +autoconf 2.58 or newer. Run the script like this: + + ./buildconf.sh + +Once this has been done, follow the same instructions as for building +from a source distribution. + +To build Expat from a source distribution, you first run the +configuration shell script in the top level distribution directory: + + ./configure + +There are many options which you may provide to configure (which you +can discover by running configure with the --help option). But the +one of most interest is the one that sets the installation directory. +By default, the configure script will set things up to install +libexpat into /usr/local/lib, expat.h into /usr/local/include, and +xmlwf into /usr/local/bin. If, for example, you'd prefer to install +into /home/me/mystuff/lib, /home/me/mystuff/include, and +/home/me/mystuff/bin, you can tell configure about that with: + + ./configure --prefix=/home/me/mystuff + +Another interesting option is to enable 64-bit integer support for +line and column numbers and the over-all byte index: + + ./configure CPPFLAGS=-DXML_LARGE_SIZE + +However, such a modification would be a breaking change to the ABI +and is therefore not recommended for general use - e.g. as part of +a Linux distribution - but rather for builds with special requirements. + +After running the configure script, the "make" command will build +things and "make install" will install things into their proper +location. Have a look at the "Makefile" to learn about additional +"make" options. Note that you need to have write permission into +the directories into which things will be installed. + +If you are interested in building Expat to provide document +information in UTF-16 encoding rather than the default UTF-8, follow +these instructions (after having run "make distclean"): + + 1. For UTF-16 output as unsigned short (and version/error + strings as char), run: + + ./configure CPPFLAGS=-DXML_UNICODE + + For UTF-16 output as wchar_t (incl. version/error strings), + run: + + ./configure CFLAGS="-g -O2 -fshort-wchar" \ + CPPFLAGS=-DXML_UNICODE_WCHAR_T + + 2. Edit the MakeFile, changing: + + LIBRARY = libexpat.la + + to: + + LIBRARY = libexpatw.la + + (Note the additional "w" in the library name.) + + 3. Run "make buildlib" (which builds the library only). + Or, to save step 2, run "make buildlib LIBRARY=libexpatw.la". + + 4. Run "make installlib" (which installs the library only). + Or, if step 2 was omitted, run "make installlib LIBRARY=libexpatw.la". + +Using DESTDIR or INSTALL_ROOT is enabled, with INSTALL_ROOT being the default +value for DESTDIR, and the rest of the make file using only DESTDIR. +It works as follows: + $ make install DESTDIR=/path/to/image +overrides the in-makefile set DESTDIR, while both + $ INSTALL_ROOT=/path/to/image make install + $ make install INSTALL_ROOT=/path/to/image +use DESTDIR=$(INSTALL_ROOT), even if DESTDIR eventually is defined in the +environment, because variable-setting priority is +1) commandline +2) in-makefile +3) environment + +Note: This only applies to the Expat library itself, building UTF-16 versions +of xmlwf and the tests is currently not supported. + +Note for Solaris users: The "ar" command is usually located in +"/usr/ccs/bin", which is not in the default PATH. You will need to +add this to your path for the "make" command, and probably also switch +to GNU make (the "make" found in /usr/ccs/bin does not seem to work +properly -- apparently it does not understand .PHONY directives). If +you're using ksh or bash, use this command to build: + + PATH=/usr/ccs/bin:$PATH make + +When using Expat with a project using autoconf for configuration, you +can use the probing macro in conftools/expat.m4 to determine how to +include Expat. See the comments at the top of that file for more +information. + +A reference manual is available in the file doc/reference.html in this +distribution. + +The homepage for this project is http://www.libexpat.org/. There +are links there to connect you to the bug reports page. If you need +to report a bug when you don't have access to a browser, you may also +send a bug report by email to expat-bugs@mail.libexpat.org. + +Discussion related to the direction of future expat development takes +place on expat-discuss@mail.libexpat.org. Archives of this list and +other Expat-related lists may be found at: + + http://mail.libexpat.org/mailman/listinfo/ diff --git a/deps/EXPAT/expat/ascii.h b/deps/EXPAT/expat/ascii.h new file mode 100644 index 0000000000..d10530b09b --- /dev/null +++ b/deps/EXPAT/expat/ascii.h @@ -0,0 +1,92 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#define ASCII_A 0x41 +#define ASCII_B 0x42 +#define ASCII_C 0x43 +#define ASCII_D 0x44 +#define ASCII_E 0x45 +#define ASCII_F 0x46 +#define ASCII_G 0x47 +#define ASCII_H 0x48 +#define ASCII_I 0x49 +#define ASCII_J 0x4A +#define ASCII_K 0x4B +#define ASCII_L 0x4C +#define ASCII_M 0x4D +#define ASCII_N 0x4E +#define ASCII_O 0x4F +#define ASCII_P 0x50 +#define ASCII_Q 0x51 +#define ASCII_R 0x52 +#define ASCII_S 0x53 +#define ASCII_T 0x54 +#define ASCII_U 0x55 +#define ASCII_V 0x56 +#define ASCII_W 0x57 +#define ASCII_X 0x58 +#define ASCII_Y 0x59 +#define ASCII_Z 0x5A + +#define ASCII_a 0x61 +#define ASCII_b 0x62 +#define ASCII_c 0x63 +#define ASCII_d 0x64 +#define ASCII_e 0x65 +#define ASCII_f 0x66 +#define ASCII_g 0x67 +#define ASCII_h 0x68 +#define ASCII_i 0x69 +#define ASCII_j 0x6A +#define ASCII_k 0x6B +#define ASCII_l 0x6C +#define ASCII_m 0x6D +#define ASCII_n 0x6E +#define ASCII_o 0x6F +#define ASCII_p 0x70 +#define ASCII_q 0x71 +#define ASCII_r 0x72 +#define ASCII_s 0x73 +#define ASCII_t 0x74 +#define ASCII_u 0x75 +#define ASCII_v 0x76 +#define ASCII_w 0x77 +#define ASCII_x 0x78 +#define ASCII_y 0x79 +#define ASCII_z 0x7A + +#define ASCII_0 0x30 +#define ASCII_1 0x31 +#define ASCII_2 0x32 +#define ASCII_3 0x33 +#define ASCII_4 0x34 +#define ASCII_5 0x35 +#define ASCII_6 0x36 +#define ASCII_7 0x37 +#define ASCII_8 0x38 +#define ASCII_9 0x39 + +#define ASCII_TAB 0x09 +#define ASCII_SPACE 0x20 +#define ASCII_EXCL 0x21 +#define ASCII_QUOT 0x22 +#define ASCII_AMP 0x26 +#define ASCII_APOS 0x27 +#define ASCII_MINUS 0x2D +#define ASCII_PERIOD 0x2E +#define ASCII_COLON 0x3A +#define ASCII_SEMI 0x3B +#define ASCII_LT 0x3C +#define ASCII_EQUALS 0x3D +#define ASCII_GT 0x3E +#define ASCII_LSQB 0x5B +#define ASCII_RSQB 0x5D +#define ASCII_UNDERSCORE 0x5F +#define ASCII_LPAREN 0x28 +#define ASCII_RPAREN 0x29 +#define ASCII_FF 0x0C +#define ASCII_SLASH 0x2F +#define ASCII_HASH 0x23 +#define ASCII_PIPE 0x7C +#define ASCII_COMMA 0x2C diff --git a/deps/EXPAT/expat/asciitab.h b/deps/EXPAT/expat/asciitab.h new file mode 100644 index 0000000000..79a15c28ca --- /dev/null +++ b/deps/EXPAT/expat/asciitab.h @@ -0,0 +1,36 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/deps/EXPAT/expat/config.cmake.in b/deps/EXPAT/expat/config.cmake.in new file mode 100644 index 0000000000..8edb5bbbd5 --- /dev/null +++ b/deps/EXPAT/expat/config.cmake.in @@ -0,0 +1,4 @@ +include(${CMAKE_CURRENT_LIST_DIR}/EXPATTargets.cmake) +set(EXPAT_LIBRARIES EXPAT::expat) +set(EXPAT_INCLUDE_DIRS ${_IMPORT_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}) + diff --git a/deps/EXPAT/expat/expat.h b/deps/EXPAT/expat/expat.h new file mode 100644 index 0000000000..086e24b39c --- /dev/null +++ b/deps/EXPAT/expat/expat.h @@ -0,0 +1,1048 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef Expat_INCLUDED +#define Expat_INCLUDED 1 + +#ifdef __VMS +/* 0 1 2 3 0 1 2 3 + 1234567890123456789012345678901 1234567890123456789012345678901 */ +#define XML_SetProcessingInstructionHandler XML_SetProcessingInstrHandler +#define XML_SetUnparsedEntityDeclHandler XML_SetUnparsedEntDeclHandler +#define XML_SetStartNamespaceDeclHandler XML_SetStartNamespcDeclHandler +#define XML_SetExternalEntityRefHandlerArg XML_SetExternalEntRefHandlerArg +#endif + +#include +#include "expat_external.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct XML_ParserStruct; +typedef struct XML_ParserStruct *XML_Parser; + +/* Should this be defined using stdbool.h when C99 is available? */ +typedef unsigned char XML_Bool; +#define XML_TRUE ((XML_Bool) 1) +#define XML_FALSE ((XML_Bool) 0) + +/* The XML_Status enum gives the possible return values for several + API functions. The preprocessor #defines are included so this + stanza can be added to code that still needs to support older + versions of Expat 1.95.x: + + #ifndef XML_STATUS_OK + #define XML_STATUS_OK 1 + #define XML_STATUS_ERROR 0 + #endif + + Otherwise, the #define hackery is quite ugly and would have been + dropped. +*/ +enum XML_Status { + XML_STATUS_ERROR = 0, +#define XML_STATUS_ERROR XML_STATUS_ERROR + XML_STATUS_OK = 1, +#define XML_STATUS_OK XML_STATUS_OK + XML_STATUS_SUSPENDED = 2 +#define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED +}; + +enum XML_Error { + XML_ERROR_NONE, + XML_ERROR_NO_MEMORY, + XML_ERROR_SYNTAX, + XML_ERROR_NO_ELEMENTS, + XML_ERROR_INVALID_TOKEN, + XML_ERROR_UNCLOSED_TOKEN, + XML_ERROR_PARTIAL_CHAR, + XML_ERROR_TAG_MISMATCH, + XML_ERROR_DUPLICATE_ATTRIBUTE, + XML_ERROR_JUNK_AFTER_DOC_ELEMENT, + XML_ERROR_PARAM_ENTITY_REF, + XML_ERROR_UNDEFINED_ENTITY, + XML_ERROR_RECURSIVE_ENTITY_REF, + XML_ERROR_ASYNC_ENTITY, + XML_ERROR_BAD_CHAR_REF, + XML_ERROR_BINARY_ENTITY_REF, + XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF, + XML_ERROR_MISPLACED_XML_PI, + XML_ERROR_UNKNOWN_ENCODING, + XML_ERROR_INCORRECT_ENCODING, + XML_ERROR_UNCLOSED_CDATA_SECTION, + XML_ERROR_EXTERNAL_ENTITY_HANDLING, + XML_ERROR_NOT_STANDALONE, + XML_ERROR_UNEXPECTED_STATE, + XML_ERROR_ENTITY_DECLARED_IN_PE, + XML_ERROR_FEATURE_REQUIRES_XML_DTD, + XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING, + /* Added in 1.95.7. */ + XML_ERROR_UNBOUND_PREFIX, + /* Added in 1.95.8. */ + XML_ERROR_UNDECLARING_PREFIX, + XML_ERROR_INCOMPLETE_PE, + XML_ERROR_XML_DECL, + XML_ERROR_TEXT_DECL, + XML_ERROR_PUBLICID, + XML_ERROR_SUSPENDED, + XML_ERROR_NOT_SUSPENDED, + XML_ERROR_ABORTED, + XML_ERROR_FINISHED, + XML_ERROR_SUSPEND_PE, + /* Added in 2.0. */ + XML_ERROR_RESERVED_PREFIX_XML, + XML_ERROR_RESERVED_PREFIX_XMLNS, + XML_ERROR_RESERVED_NAMESPACE_URI +}; + +enum XML_Content_Type { + XML_CTYPE_EMPTY = 1, + XML_CTYPE_ANY, + XML_CTYPE_MIXED, + XML_CTYPE_NAME, + XML_CTYPE_CHOICE, + XML_CTYPE_SEQ +}; + +enum XML_Content_Quant { + XML_CQUANT_NONE, + XML_CQUANT_OPT, + XML_CQUANT_REP, + XML_CQUANT_PLUS +}; + +/* If type == XML_CTYPE_EMPTY or XML_CTYPE_ANY, then quant will be + XML_CQUANT_NONE, and the other fields will be zero or NULL. + If type == XML_CTYPE_MIXED, then quant will be NONE or REP and + numchildren will contain number of elements that may be mixed in + and children point to an array of XML_Content cells that will be + all of XML_CTYPE_NAME type with no quantification. + + If type == XML_CTYPE_NAME, then the name points to the name, and + the numchildren field will be zero and children will be NULL. The + quant fields indicates any quantifiers placed on the name. + + CHOICE and SEQ will have name NULL, the number of children in + numchildren and children will point, recursively, to an array + of XML_Content cells. + + The EMPTY, ANY, and MIXED types will only occur at top level. +*/ + +typedef struct XML_cp XML_Content; + +struct XML_cp { + enum XML_Content_Type type; + enum XML_Content_Quant quant; + XML_Char * name; + unsigned int numchildren; + XML_Content * children; +}; + + +/* This is called for an element declaration. See above for + description of the model argument. It's the caller's responsibility + to free model when finished with it. +*/ +typedef void (XMLCALL *XML_ElementDeclHandler) (void *userData, + const XML_Char *name, + XML_Content *model); + +XMLPARSEAPI(void) +XML_SetElementDeclHandler(XML_Parser parser, + XML_ElementDeclHandler eldecl); + +/* The Attlist declaration handler is called for *each* attribute. So + a single Attlist declaration with multiple attributes declared will + generate multiple calls to this handler. The "default" parameter + may be NULL in the case of the "#IMPLIED" or "#REQUIRED" + keyword. The "isrequired" parameter will be true and the default + value will be NULL in the case of "#REQUIRED". If "isrequired" is + true and default is non-NULL, then this is a "#FIXED" default. +*/ +typedef void (XMLCALL *XML_AttlistDeclHandler) ( + void *userData, + const XML_Char *elname, + const XML_Char *attname, + const XML_Char *att_type, + const XML_Char *dflt, + int isrequired); + +XMLPARSEAPI(void) +XML_SetAttlistDeclHandler(XML_Parser parser, + XML_AttlistDeclHandler attdecl); + +/* The XML declaration handler is called for *both* XML declarations + and text declarations. The way to distinguish is that the version + parameter will be NULL for text declarations. The encoding + parameter may be NULL for XML declarations. The standalone + parameter will be -1, 0, or 1 indicating respectively that there + was no standalone parameter in the declaration, that it was given + as no, or that it was given as yes. +*/ +typedef void (XMLCALL *XML_XmlDeclHandler) (void *userData, + const XML_Char *version, + const XML_Char *encoding, + int standalone); + +XMLPARSEAPI(void) +XML_SetXmlDeclHandler(XML_Parser parser, + XML_XmlDeclHandler xmldecl); + + +typedef struct { + void *(*malloc_fcn)(size_t size); + void *(*realloc_fcn)(void *ptr, size_t size); + void (*free_fcn)(void *ptr); +} XML_Memory_Handling_Suite; + +/* Constructs a new parser; encoding is the encoding specified by the + external protocol or NULL if there is none specified. +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreate(const XML_Char *encoding); + +/* Constructs a new parser and namespace processor. Element type + names and attribute names that belong to a namespace will be + expanded; unprefixed attribute names are never expanded; unprefixed + element type names are expanded only if there is a default + namespace. The expanded name is the concatenation of the namespace + URI, the namespace separator character, and the local part of the + name. If the namespace separator is '\0' then the namespace URI + and the local part will be concatenated without any separator. + It is a programming error to use the separator '\0' with namespace + triplets (see XML_SetReturnNSTriplet). +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator); + + +/* Constructs a new parser using the memory management suite referred to + by memsuite. If memsuite is NULL, then use the standard library memory + suite. If namespaceSeparator is non-NULL it creates a parser with + namespace processing as described above. The character pointed at + will serve as the namespace separator. + + All further memory operations used for the created parser will come from + the given suite. +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreate_MM(const XML_Char *encoding, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *namespaceSeparator); + +/* Prepare a parser object to be re-used. This is particularly + valuable when memory allocation overhead is disproportionatly high, + such as when a large number of small documnents need to be parsed. + All handlers are cleared from the parser, except for the + unknownEncodingHandler. The parser's external state is re-initialized + except for the values of ns and ns_triplets. + + Added in Expat 1.95.3. +*/ +XMLPARSEAPI(XML_Bool) +XML_ParserReset(XML_Parser parser, const XML_Char *encoding); + +/* atts is array of name/value pairs, terminated by 0; + names and values are 0 terminated. +*/ +typedef void (XMLCALL *XML_StartElementHandler) (void *userData, + const XML_Char *name, + const XML_Char **atts); + +typedef void (XMLCALL *XML_EndElementHandler) (void *userData, + const XML_Char *name); + + +/* s is not 0 terminated. */ +typedef void (XMLCALL *XML_CharacterDataHandler) (void *userData, + const XML_Char *s, + int len); + +/* target and data are 0 terminated */ +typedef void (XMLCALL *XML_ProcessingInstructionHandler) ( + void *userData, + const XML_Char *target, + const XML_Char *data); + +/* data is 0 terminated */ +typedef void (XMLCALL *XML_CommentHandler) (void *userData, + const XML_Char *data); + +typedef void (XMLCALL *XML_StartCdataSectionHandler) (void *userData); +typedef void (XMLCALL *XML_EndCdataSectionHandler) (void *userData); + +/* This is called for any characters in the XML document for which + there is no applicable handler. This includes both characters that + are part of markup which is of a kind that is not reported + (comments, markup declarations), or characters that are part of a + construct which could be reported but for which no handler has been + supplied. The characters are passed exactly as they were in the XML + document except that they will be encoded in UTF-8 or UTF-16. + Line boundaries are not normalized. Note that a byte order mark + character is not passed to the default handler. There are no + guarantees about how characters are divided between calls to the + default handler: for example, a comment might be split between + multiple calls. +*/ +typedef void (XMLCALL *XML_DefaultHandler) (void *userData, + const XML_Char *s, + int len); + +/* This is called for the start of the DOCTYPE declaration, before + any DTD or internal subset is parsed. +*/ +typedef void (XMLCALL *XML_StartDoctypeDeclHandler) ( + void *userData, + const XML_Char *doctypeName, + const XML_Char *sysid, + const XML_Char *pubid, + int has_internal_subset); + +/* This is called for the start of the DOCTYPE declaration when the + closing > is encountered, but after processing any external + subset. +*/ +typedef void (XMLCALL *XML_EndDoctypeDeclHandler)(void *userData); + +/* This is called for entity declarations. The is_parameter_entity + argument will be non-zero if the entity is a parameter entity, zero + otherwise. + + For internal entities (), value will + be non-NULL and systemId, publicID, and notationName will be NULL. + The value string is NOT nul-terminated; the length is provided in + the value_length argument. Since it is legal to have zero-length + values, do not use this argument to test for internal entities. + + For external entities, value will be NULL and systemId will be + non-NULL. The publicId argument will be NULL unless a public + identifier was provided. The notationName argument will have a + non-NULL value only for unparsed entity declarations. + + Note that is_parameter_entity can't be changed to XML_Bool, since + that would break binary compatibility. +*/ +typedef void (XMLCALL *XML_EntityDeclHandler) ( + void *userData, + const XML_Char *entityName, + int is_parameter_entity, + const XML_Char *value, + int value_length, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + +XMLPARSEAPI(void) +XML_SetEntityDeclHandler(XML_Parser parser, + XML_EntityDeclHandler handler); + +/* OBSOLETE -- OBSOLETE -- OBSOLETE + This handler has been superseded by the EntityDeclHandler above. + It is provided here for backward compatibility. + + This is called for a declaration of an unparsed (NDATA) entity. + The base argument is whatever was set by XML_SetBase. The + entityName, systemId and notationName arguments will never be + NULL. The other arguments may be. +*/ +typedef void (XMLCALL *XML_UnparsedEntityDeclHandler) ( + void *userData, + const XML_Char *entityName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + +/* This is called for a declaration of notation. The base argument is + whatever was set by XML_SetBase. The notationName will never be + NULL. The other arguments can be. +*/ +typedef void (XMLCALL *XML_NotationDeclHandler) ( + void *userData, + const XML_Char *notationName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +/* When namespace processing is enabled, these are called once for + each namespace declaration. The call to the start and end element + handlers occur between the calls to the start and end namespace + declaration handlers. For an xmlns attribute, prefix will be + NULL. For an xmlns="" attribute, uri will be NULL. +*/ +typedef void (XMLCALL *XML_StartNamespaceDeclHandler) ( + void *userData, + const XML_Char *prefix, + const XML_Char *uri); + +typedef void (XMLCALL *XML_EndNamespaceDeclHandler) ( + void *userData, + const XML_Char *prefix); + +/* This is called if the document is not standalone, that is, it has an + external subset or a reference to a parameter entity, but does not + have standalone="yes". If this handler returns XML_STATUS_ERROR, + then processing will not continue, and the parser will return a + XML_ERROR_NOT_STANDALONE error. + If parameter entity parsing is enabled, then in addition to the + conditions above this handler will only be called if the referenced + entity was actually read. +*/ +typedef int (XMLCALL *XML_NotStandaloneHandler) (void *userData); + +/* This is called for a reference to an external parsed general + entity. The referenced entity is not automatically parsed. The + application can parse it immediately or later using + XML_ExternalEntityParserCreate. + + The parser argument is the parser parsing the entity containing the + reference; it can be passed as the parser argument to + XML_ExternalEntityParserCreate. The systemId argument is the + system identifier as specified in the entity declaration; it will + not be NULL. + + The base argument is the system identifier that should be used as + the base for resolving systemId if systemId was relative; this is + set by XML_SetBase; it may be NULL. + + The publicId argument is the public identifier as specified in the + entity declaration, or NULL if none was specified; the whitespace + in the public identifier will have been normalized as required by + the XML spec. + + The context argument specifies the parsing context in the format + expected by the context argument to XML_ExternalEntityParserCreate; + context is valid only until the handler returns, so if the + referenced entity is to be parsed later, it must be copied. + context is NULL only when the entity is a parameter entity. + + The handler should return XML_STATUS_ERROR if processing should not + continue because of a fatal error in the handling of the external + entity. In this case the calling parser will return an + XML_ERROR_EXTERNAL_ENTITY_HANDLING error. + + Note that unlike other handlers the first argument is the parser, + not userData. +*/ +typedef int (XMLCALL *XML_ExternalEntityRefHandler) ( + XML_Parser parser, + const XML_Char *context, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +/* This is called in two situations: + 1) An entity reference is encountered for which no declaration + has been read *and* this is not an error. + 2) An internal entity reference is read, but not expanded, because + XML_SetDefaultHandler has been called. + Note: skipped parameter entities in declarations and skipped general + entities in attribute values cannot be reported, because + the event would be out of sync with the reporting of the + declarations or attribute values +*/ +typedef void (XMLCALL *XML_SkippedEntityHandler) ( + void *userData, + const XML_Char *entityName, + int is_parameter_entity); + +/* This structure is filled in by the XML_UnknownEncodingHandler to + provide information to the parser about encodings that are unknown + to the parser. + + The map[b] member gives information about byte sequences whose + first byte is b. + + If map[b] is c where c is >= 0, then b by itself encodes the + Unicode scalar value c. + + If map[b] is -1, then the byte sequence is malformed. + + If map[b] is -n, where n >= 2, then b is the first byte of an + n-byte sequence that encodes a single Unicode scalar value. + + The data member will be passed as the first argument to the convert + function. + + The convert function is used to convert multibyte sequences; s will + point to a n-byte sequence where map[(unsigned char)*s] == -n. The + convert function must return the Unicode scalar value represented + by this byte sequence or -1 if the byte sequence is malformed. + + The convert function may be NULL if the encoding is a single-byte + encoding, that is if map[b] >= -1 for all bytes b. + + When the parser is finished with the encoding, then if release is + not NULL, it will call release passing it the data member; once + release has been called, the convert function will not be called + again. + + Expat places certain restrictions on the encodings that are supported + using this mechanism. + + 1. Every ASCII character that can appear in a well-formed XML document, + other than the characters + + $@\^`{}~ + + must be represented by a single byte, and that byte must be the + same byte that represents that character in ASCII. + + 2. No character may require more than 4 bytes to encode. + + 3. All characters encoded must have Unicode scalar values <= + 0xFFFF, (i.e., characters that would be encoded by surrogates in + UTF-16 are not allowed). Note that this restriction doesn't + apply to the built-in support for UTF-8 and UTF-16. + + 4. No Unicode character may be encoded by more than one distinct + sequence of bytes. +*/ +typedef struct { + int map[256]; + void *data; + int (XMLCALL *convert)(void *data, const char *s); + void (XMLCALL *release)(void *data); +} XML_Encoding; + +/* This is called for an encoding that is unknown to the parser. + + The encodingHandlerData argument is that which was passed as the + second argument to XML_SetUnknownEncodingHandler. + + The name argument gives the name of the encoding as specified in + the encoding declaration. + + If the callback can provide information about the encoding, it must + fill in the XML_Encoding structure, and return XML_STATUS_OK. + Otherwise it must return XML_STATUS_ERROR. + + If info does not describe a suitable encoding, then the parser will + return an XML_UNKNOWN_ENCODING error. +*/ +typedef int (XMLCALL *XML_UnknownEncodingHandler) ( + void *encodingHandlerData, + const XML_Char *name, + XML_Encoding *info); + +XMLPARSEAPI(void) +XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end); + +XMLPARSEAPI(void) +XML_SetStartElementHandler(XML_Parser parser, + XML_StartElementHandler handler); + +XMLPARSEAPI(void) +XML_SetEndElementHandler(XML_Parser parser, + XML_EndElementHandler handler); + +XMLPARSEAPI(void) +XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler); + +XMLPARSEAPI(void) +XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler); +XMLPARSEAPI(void) +XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler); + +XMLPARSEAPI(void) +XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end); + +XMLPARSEAPI(void) +XML_SetStartCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start); + +XMLPARSEAPI(void) +XML_SetEndCdataSectionHandler(XML_Parser parser, + XML_EndCdataSectionHandler end); + +/* This sets the default handler and also inhibits expansion of + internal entities. These entity references will be passed to the + default handler, or to the skipped entity handler, if one is set. +*/ +XMLPARSEAPI(void) +XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler); + +/* This sets the default handler but does not inhibit expansion of + internal entities. The entity reference will not be passed to the + default handler. +*/ +XMLPARSEAPI(void) +XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler); + +XMLPARSEAPI(void) +XML_SetDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end); + +XMLPARSEAPI(void) +XML_SetStartDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start); + +XMLPARSEAPI(void) +XML_SetEndDoctypeDeclHandler(XML_Parser parser, + XML_EndDoctypeDeclHandler end); + +XMLPARSEAPI(void) +XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler); + +XMLPARSEAPI(void) +XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler); + +XMLPARSEAPI(void) +XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end); + +XMLPARSEAPI(void) +XML_SetStartNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start); + +XMLPARSEAPI(void) +XML_SetEndNamespaceDeclHandler(XML_Parser parser, + XML_EndNamespaceDeclHandler end); + +XMLPARSEAPI(void) +XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler); + +XMLPARSEAPI(void) +XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler); + +/* If a non-NULL value for arg is specified here, then it will be + passed as the first argument to the external entity ref handler + instead of the parser object. +*/ +XMLPARSEAPI(void) +XML_SetExternalEntityRefHandlerArg(XML_Parser parser, + void *arg); + +XMLPARSEAPI(void) +XML_SetSkippedEntityHandler(XML_Parser parser, + XML_SkippedEntityHandler handler); + +XMLPARSEAPI(void) +XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *encodingHandlerData); + +/* This can be called within a handler for a start element, end + element, processing instruction or character data. It causes the + corresponding markup to be passed to the default handler. +*/ +XMLPARSEAPI(void) +XML_DefaultCurrent(XML_Parser parser); + +/* If do_nst is non-zero, and namespace processing is in effect, and + a name has a prefix (i.e. an explicit namespace qualifier) then + that name is returned as a triplet in a single string separated by + the separator character specified when the parser was created: URI + + sep + local_name + sep + prefix. + + If do_nst is zero, then namespace information is returned in the + default manner (URI + sep + local_name) whether or not the name + has a prefix. + + Note: Calling XML_SetReturnNSTriplet after XML_Parse or + XML_ParseBuffer has no effect. +*/ + +XMLPARSEAPI(void) +XML_SetReturnNSTriplet(XML_Parser parser, int do_nst); + +/* This value is passed as the userData argument to callbacks. */ +XMLPARSEAPI(void) +XML_SetUserData(XML_Parser parser, void *userData); + +/* Returns the last value set by XML_SetUserData or NULL. */ +#define XML_GetUserData(parser) (*(void **)(parser)) + +/* This is equivalent to supplying an encoding argument to + XML_ParserCreate. On success XML_SetEncoding returns non-zero, + zero otherwise. + Note: Calling XML_SetEncoding after XML_Parse or XML_ParseBuffer + has no effect and returns XML_STATUS_ERROR. +*/ +XMLPARSEAPI(enum XML_Status) +XML_SetEncoding(XML_Parser parser, const XML_Char *encoding); + +/* If this function is called, then the parser will be passed as the + first argument to callbacks instead of userData. The userData will + still be accessible using XML_GetUserData. +*/ +XMLPARSEAPI(void) +XML_UseParserAsHandlerArg(XML_Parser parser); + +/* If useDTD == XML_TRUE is passed to this function, then the parser + will assume that there is an external subset, even if none is + specified in the document. In such a case the parser will call the + externalEntityRefHandler with a value of NULL for the systemId + argument (the publicId and context arguments will be NULL as well). + Note: For the purpose of checking WFC: Entity Declared, passing + useDTD == XML_TRUE will make the parser behave as if the document + had a DTD with an external subset. + Note: If this function is called, then this must be done before + the first call to XML_Parse or XML_ParseBuffer, since it will + have no effect after that. Returns + XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING. + Note: If the document does not have a DOCTYPE declaration at all, + then startDoctypeDeclHandler and endDoctypeDeclHandler will not + be called, despite an external subset being parsed. + Note: If XML_DTD is not defined when Expat is compiled, returns + XML_ERROR_FEATURE_REQUIRES_XML_DTD. +*/ +XMLPARSEAPI(enum XML_Error) +XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD); + + +/* Sets the base to be used for resolving relative URIs in system + identifiers in declarations. Resolving relative identifiers is + left to the application: this value will be passed through as the + base argument to the XML_ExternalEntityRefHandler, + XML_NotationDeclHandler and XML_UnparsedEntityDeclHandler. The base + argument will be copied. Returns XML_STATUS_ERROR if out of memory, + XML_STATUS_OK otherwise. +*/ +XMLPARSEAPI(enum XML_Status) +XML_SetBase(XML_Parser parser, const XML_Char *base); + +XMLPARSEAPI(const XML_Char *) +XML_GetBase(XML_Parser parser); + +/* Returns the number of the attribute/value pairs passed in last call + to the XML_StartElementHandler that were specified in the start-tag + rather than defaulted. Each attribute/value pair counts as 2; thus + this correspondds to an index into the atts array passed to the + XML_StartElementHandler. +*/ +XMLPARSEAPI(int) +XML_GetSpecifiedAttributeCount(XML_Parser parser); + +/* Returns the index of the ID attribute passed in the last call to + XML_StartElementHandler, or -1 if there is no ID attribute. Each + attribute/value pair counts as 2; thus this correspondds to an + index into the atts array passed to the XML_StartElementHandler. +*/ +XMLPARSEAPI(int) +XML_GetIdAttributeIndex(XML_Parser parser); + +#ifdef XML_ATTR_INFO +/* Source file byte offsets for the start and end of attribute names and values. + The value indices are exclusive of surrounding quotes; thus in a UTF-8 source + file an attribute value of "blah" will yield: + info->valueEnd - info->valueStart = 4 bytes. +*/ +typedef struct { + XML_Index nameStart; /* Offset to beginning of the attribute name. */ + XML_Index nameEnd; /* Offset after the attribute name's last byte. */ + XML_Index valueStart; /* Offset to beginning of the attribute value. */ + XML_Index valueEnd; /* Offset after the attribute value's last byte. */ +} XML_AttrInfo; + +/* Returns an array of XML_AttrInfo structures for the attribute/value pairs + passed in last call to the XML_StartElementHandler that were specified + in the start-tag rather than defaulted. Each attribute/value pair counts + as 1; thus the number of entries in the array is + XML_GetSpecifiedAttributeCount(parser) / 2. +*/ +XMLPARSEAPI(const XML_AttrInfo *) +XML_GetAttributeInfo(XML_Parser parser); +#endif + +/* Parses some input. Returns XML_STATUS_ERROR if a fatal error is + detected. The last call to XML_Parse must have isFinal true; len + may be zero for this call (or any other). + + Though the return values for these functions has always been + described as a Boolean value, the implementation, at least for the + 1.95.x series, has always returned exactly one of the XML_Status + values. +*/ +XMLPARSEAPI(enum XML_Status) +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal); + +XMLPARSEAPI(void *) +XML_GetBuffer(XML_Parser parser, int len); + +XMLPARSEAPI(enum XML_Status) +XML_ParseBuffer(XML_Parser parser, int len, int isFinal); + +/* Stops parsing, causing XML_Parse() or XML_ParseBuffer() to return. + Must be called from within a call-back handler, except when aborting + (resumable = 0) an already suspended parser. Some call-backs may + still follow because they would otherwise get lost. Examples: + - endElementHandler() for empty elements when stopped in + startElementHandler(), + - endNameSpaceDeclHandler() when stopped in endElementHandler(), + and possibly others. + + Can be called from most handlers, including DTD related call-backs, + except when parsing an external parameter entity and resumable != 0. + Returns XML_STATUS_OK when successful, XML_STATUS_ERROR otherwise. + Possible error codes: + - XML_ERROR_SUSPENDED: when suspending an already suspended parser. + - XML_ERROR_FINISHED: when the parser has already finished. + - XML_ERROR_SUSPEND_PE: when suspending while parsing an external PE. + + When resumable != 0 (true) then parsing is suspended, that is, + XML_Parse() and XML_ParseBuffer() return XML_STATUS_SUSPENDED. + Otherwise, parsing is aborted, that is, XML_Parse() and XML_ParseBuffer() + return XML_STATUS_ERROR with error code XML_ERROR_ABORTED. + + *Note*: + This will be applied to the current parser instance only, that is, if + there is a parent parser then it will continue parsing when the + externalEntityRefHandler() returns. It is up to the implementation of + the externalEntityRefHandler() to call XML_StopParser() on the parent + parser (recursively), if one wants to stop parsing altogether. + + When suspended, parsing can be resumed by calling XML_ResumeParser(). +*/ +XMLPARSEAPI(enum XML_Status) +XML_StopParser(XML_Parser parser, XML_Bool resumable); + +/* Resumes parsing after it has been suspended with XML_StopParser(). + Must not be called from within a handler call-back. Returns same + status codes as XML_Parse() or XML_ParseBuffer(). + Additional error code XML_ERROR_NOT_SUSPENDED possible. + + *Note*: + This must be called on the most deeply nested child parser instance + first, and on its parent parser only after the child parser has finished, + to be applied recursively until the document entity's parser is restarted. + That is, the parent parser will not resume by itself and it is up to the + application to call XML_ResumeParser() on it at the appropriate moment. +*/ +XMLPARSEAPI(enum XML_Status) +XML_ResumeParser(XML_Parser parser); + +enum XML_Parsing { + XML_INITIALIZED, + XML_PARSING, + XML_FINISHED, + XML_SUSPENDED +}; + +typedef struct { + enum XML_Parsing parsing; + XML_Bool finalBuffer; +} XML_ParsingStatus; + +/* Returns status of parser with respect to being initialized, parsing, + finished, or suspended and processing the final buffer. + XXX XML_Parse() and XML_ParseBuffer() should return XML_ParsingStatus, + XXX with XML_FINISHED_OK or XML_FINISHED_ERROR replacing XML_FINISHED +*/ +XMLPARSEAPI(void) +XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status); + +/* Creates an XML_Parser object that can parse an external general + entity; context is a '\0'-terminated string specifying the parse + context; encoding is a '\0'-terminated string giving the name of + the externally specified encoding, or NULL if there is no + externally specified encoding. The context string consists of a + sequence of tokens separated by formfeeds (\f); a token consisting + of a name specifies that the general entity of the name is open; a + token of the form prefix=uri specifies the namespace for a + particular prefix; a token of the form =uri specifies the default + namespace. This can be called at any point after the first call to + an ExternalEntityRefHandler so longer as the parser has not yet + been freed. The new parser is completely independent and may + safely be used in a separate thread. The handlers and userData are + initialized from the parser argument. Returns NULL if out of memory. + Otherwise returns a new XML_Parser object. +*/ +XMLPARSEAPI(XML_Parser) +XML_ExternalEntityParserCreate(XML_Parser parser, + const XML_Char *context, + const XML_Char *encoding); + +enum XML_ParamEntityParsing { + XML_PARAM_ENTITY_PARSING_NEVER, + XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE, + XML_PARAM_ENTITY_PARSING_ALWAYS +}; + +/* Controls parsing of parameter entities (including the external DTD + subset). If parsing of parameter entities is enabled, then + references to external parameter entities (including the external + DTD subset) will be passed to the handler set with + XML_SetExternalEntityRefHandler. The context passed will be 0. + + Unlike external general entities, external parameter entities can + only be parsed synchronously. If the external parameter entity is + to be parsed, it must be parsed during the call to the external + entity ref handler: the complete sequence of + XML_ExternalEntityParserCreate, XML_Parse/XML_ParseBuffer and + XML_ParserFree calls must be made during this call. After + XML_ExternalEntityParserCreate has been called to create the parser + for the external parameter entity (context must be 0 for this + call), it is illegal to make any calls on the old parser until + XML_ParserFree has been called on the newly created parser. + If the library has been compiled without support for parameter + entity parsing (ie without XML_DTD being defined), then + XML_SetParamEntityParsing will return 0 if parsing of parameter + entities is requested; otherwise it will return non-zero. + Note: If XML_SetParamEntityParsing is called after XML_Parse or + XML_ParseBuffer, then it has no effect and will always return 0. +*/ +XMLPARSEAPI(int) +XML_SetParamEntityParsing(XML_Parser parser, + enum XML_ParamEntityParsing parsing); + +/* Sets the hash salt to use for internal hash calculations. + Helps in preventing DoS attacks based on predicting hash + function behavior. This must be called before parsing is started. + Returns 1 if successful, 0 when called after parsing has started. +*/ +XMLPARSEAPI(int) +XML_SetHashSalt(XML_Parser parser, + unsigned long hash_salt); + +/* If XML_Parse or XML_ParseBuffer have returned XML_STATUS_ERROR, then + XML_GetErrorCode returns information about the error. +*/ +XMLPARSEAPI(enum XML_Error) +XML_GetErrorCode(XML_Parser parser); + +/* These functions return information about the current parse + location. They may be called from any callback called to report + some parse event; in this case the location is the location of the + first of the sequence of characters that generated the event. When + called from callbacks generated by declarations in the document + prologue, the location identified isn't as neatly defined, but will + be within the relevant markup. When called outside of the callback + functions, the position indicated will be just past the last parse + event (regardless of whether there was an associated callback). + + They may also be called after returning from a call to XML_Parse + or XML_ParseBuffer. If the return value is XML_STATUS_ERROR then + the location is the location of the character at which the error + was detected; otherwise the location is the location of the last + parse event, as described above. +*/ +XMLPARSEAPI(XML_Size) XML_GetCurrentLineNumber(XML_Parser parser); +XMLPARSEAPI(XML_Size) XML_GetCurrentColumnNumber(XML_Parser parser); +XMLPARSEAPI(XML_Index) XML_GetCurrentByteIndex(XML_Parser parser); + +/* Return the number of bytes in the current event. + Returns 0 if the event is in an internal entity. +*/ +XMLPARSEAPI(int) +XML_GetCurrentByteCount(XML_Parser parser); + +/* If XML_CONTEXT_BYTES is defined, returns the input buffer, sets + the integer pointed to by offset to the offset within this buffer + of the current parse position, and sets the integer pointed to by size + to the size of this buffer (the number of input bytes). Otherwise + returns a NULL pointer. Also returns a NULL pointer if a parse isn't + active. + + NOTE: The character pointer returned should not be used outside + the handler that makes the call. +*/ +XMLPARSEAPI(const char *) +XML_GetInputContext(XML_Parser parser, + int *offset, + int *size); + +/* For backwards compatibility with previous versions. */ +#define XML_GetErrorLineNumber XML_GetCurrentLineNumber +#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber +#define XML_GetErrorByteIndex XML_GetCurrentByteIndex + +/* Frees the content model passed to the element declaration handler */ +XMLPARSEAPI(void) +XML_FreeContentModel(XML_Parser parser, XML_Content *model); + +/* Exposing the memory handling functions used in Expat */ +XMLPARSEAPI(void *) +XML_ATTR_MALLOC +XML_ATTR_ALLOC_SIZE(2) +XML_MemMalloc(XML_Parser parser, size_t size); + +XMLPARSEAPI(void *) +XML_ATTR_ALLOC_SIZE(3) +XML_MemRealloc(XML_Parser parser, void *ptr, size_t size); + +XMLPARSEAPI(void) +XML_MemFree(XML_Parser parser, void *ptr); + +/* Frees memory used by the parser. */ +XMLPARSEAPI(void) +XML_ParserFree(XML_Parser parser); + +/* Returns a string describing the error. */ +XMLPARSEAPI(const XML_LChar *) +XML_ErrorString(enum XML_Error code); + +/* Return a string containing the version number of this expat */ +XMLPARSEAPI(const XML_LChar *) +XML_ExpatVersion(void); + +typedef struct { + int major; + int minor; + int micro; +} XML_Expat_Version; + +/* Return an XML_Expat_Version structure containing numeric version + number information for this version of expat. +*/ +XMLPARSEAPI(XML_Expat_Version) +XML_ExpatVersionInfo(void); + +/* Added in Expat 1.95.5. */ +enum XML_FeatureEnum { + XML_FEATURE_END = 0, + XML_FEATURE_UNICODE, + XML_FEATURE_UNICODE_WCHAR_T, + XML_FEATURE_DTD, + XML_FEATURE_CONTEXT_BYTES, + XML_FEATURE_MIN_SIZE, + XML_FEATURE_SIZEOF_XML_CHAR, + XML_FEATURE_SIZEOF_XML_LCHAR, + XML_FEATURE_NS, + XML_FEATURE_LARGE_SIZE, + XML_FEATURE_ATTR_INFO + /* Additional features must be added to the end of this enum. */ +}; + +typedef struct { + enum XML_FeatureEnum feature; + const XML_LChar *name; + long int value; +} XML_Feature; + +XMLPARSEAPI(const XML_Feature *) +XML_GetFeatureList(void); + + +/* Expat follows the semantic versioning convention. + See http://semver.org. +*/ +#define XML_MAJOR_VERSION 2 +#define XML_MINOR_VERSION 2 +#define XML_MICRO_VERSION 0 + +#ifdef __cplusplus +} +#endif + +#endif /* not Expat_INCLUDED */ diff --git a/deps/EXPAT/expat/expat_config.h b/deps/EXPAT/expat/expat_config.h new file mode 100644 index 0000000000..8aa80db0d6 --- /dev/null +++ b/deps/EXPAT/expat/expat_config.h @@ -0,0 +1,33 @@ +/*================================================================ +** Copyright 2000, Clark Cooper +** All rights reserved. +** +** This is free software. You are permitted to copy, distribute, or modify +** it under the terms of the MIT/X license (contained in the COPYING file +** with this distribution.) +*/ + +#ifndef EXPATCONFIG_H +#define EXPATCONFIG_H + +#include +#include + +#define XML_NS 1 +#define XML_DTD 1 +#define XML_CONTEXT_BYTES 1024 + +/* we will assume all Windows platforms are little endian */ +#define BYTEORDER 1234 + +/* Windows has memmove() available. */ +#define HAVE_MEMMOVE + +#ifdef WIN32 + #define WIN32_LEAN_AND_MEAN + #include + #undef WIN32_LEAN_AND_MEAN +#else +#endif + +#endif /* ifndef EXPATCONFIG_H */ diff --git a/deps/EXPAT/expat/expat_external.h b/deps/EXPAT/expat/expat_external.h new file mode 100644 index 0000000000..56cd843670 --- /dev/null +++ b/deps/EXPAT/expat/expat_external.h @@ -0,0 +1,129 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef Expat_External_INCLUDED +#define Expat_External_INCLUDED 1 + +/* External API definitions */ + +#if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__) +#define XML_USE_MSC_EXTENSIONS 1 +#endif + +/* Expat tries very hard to make the API boundary very specifically + defined. There are two macros defined to control this boundary; + each of these can be defined before including this header to + achieve some different behavior, but doing so it not recommended or + tested frequently. + + XMLCALL - The calling convention to use for all calls across the + "library boundary." This will default to cdecl, and + try really hard to tell the compiler that's what we + want. + + XMLIMPORT - Whatever magic is needed to note that a function is + to be imported from a dynamically loaded library + (.dll, .so, or .sl, depending on your platform). + + The XMLCALL macro was added in Expat 1.95.7. The only one which is + expected to be directly useful in client code is XMLCALL. + + Note that on at least some Unix versions, the Expat library must be + compiled with the cdecl calling convention as the default since + system headers may assume the cdecl convention. +*/ +#ifndef XMLCALL +#if defined(_MSC_VER) +#define XMLCALL __cdecl +#elif defined(__GNUC__) && defined(__i386) && !defined(__INTEL_COMPILER) +#define XMLCALL __attribute__((cdecl)) +#else +/* For any platform which uses this definition and supports more than + one calling convention, we need to extend this definition to + declare the convention used on that platform, if it's possible to + do so. + + If this is the case for your platform, please file a bug report + with information on how to identify your platform via the C + pre-processor and how to specify the same calling convention as the + platform's malloc() implementation. +*/ +#define XMLCALL +#endif +#endif /* not defined XMLCALL */ + + +#if !defined(XML_STATIC) && !defined(XMLIMPORT) +#ifndef XML_BUILDING_EXPAT +/* using Expat from an application */ + +#ifdef XML_USE_MSC_EXTENSIONS +// #define XMLIMPORT __declspec(dllimport) +#endif + +#endif +#endif /* not defined XML_STATIC */ + +#if !defined(XMLIMPORT) && defined(__GNUC__) && (__GNUC__ >= 4) +#define XMLIMPORT __attribute__ ((visibility ("default"))) +#endif + +/* If we didn't define it above, define it away: */ +#ifndef XMLIMPORT +#define XMLIMPORT +#endif + +#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)) +#define XML_ATTR_MALLOC __attribute__((__malloc__)) +#else +#define XML_ATTR_MALLOC +#endif + +#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) +#define XML_ATTR_ALLOC_SIZE(x) __attribute__((__alloc_size__(x))) +#else +#define XML_ATTR_ALLOC_SIZE(x) +#endif + +#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef XML_UNICODE_WCHAR_T +#define XML_UNICODE +#endif + +#ifdef XML_UNICODE /* Information is UTF-16 encoded. */ +#ifdef XML_UNICODE_WCHAR_T +typedef wchar_t XML_Char; +typedef wchar_t XML_LChar; +#else +typedef unsigned short XML_Char; +typedef char XML_LChar; +#endif /* XML_UNICODE_WCHAR_T */ +#else /* Information is UTF-8 encoded. */ +typedef char XML_Char; +typedef char XML_LChar; +#endif /* XML_UNICODE */ + +#ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */ +#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400 +typedef __int64 XML_Index; +typedef unsigned __int64 XML_Size; +#else +typedef long long XML_Index; +typedef unsigned long long XML_Size; +#endif +#else +typedef long XML_Index; +typedef unsigned long XML_Size; +#endif /* XML_LARGE_SIZE */ + +#ifdef __cplusplus +} +#endif + +#endif /* not Expat_External_INCLUDED */ diff --git a/deps/EXPAT/expat/iasciitab.h b/deps/EXPAT/expat/iasciitab.h new file mode 100644 index 0000000000..24a1d5ccc9 --- /dev/null +++ b/deps/EXPAT/expat/iasciitab.h @@ -0,0 +1,37 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */ +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/deps/EXPAT/expat/internal.h b/deps/EXPAT/expat/internal.h new file mode 100644 index 0000000000..94cb98e15c --- /dev/null +++ b/deps/EXPAT/expat/internal.h @@ -0,0 +1,95 @@ +/* internal.h + + Internal definitions used by Expat. This is not needed to compile + client code. + + The following calling convention macros are defined for frequently + called functions: + + FASTCALL - Used for those internal functions that have a simple + body and a low number of arguments and local variables. + + PTRCALL - Used for functions called though function pointers. + + PTRFASTCALL - Like PTRCALL, but for low number of arguments. + + inline - Used for selected internal functions for which inlining + may improve performance on some platforms. + + Note: Use of these macros is based on judgement, not hard rules, + and therefore subject to change. +*/ + +#if defined(__GNUC__) && defined(__i386__) && !defined(__MINGW32__) +/* We'll use this version by default only where we know it helps. + + regparm() generates warnings on Solaris boxes. See SF bug #692878. + + Instability reported with egcs on a RedHat Linux 7.3. + Let's comment out: + #define FASTCALL __attribute__((stdcall, regparm(3))) + and let's try this: +*/ +#define FASTCALL __attribute__((regparm(3))) +#define PTRFASTCALL __attribute__((regparm(3))) +#endif + +/* Using __fastcall seems to have an unexpected negative effect under + MS VC++, especially for function pointers, so we won't use it for + now on that platform. It may be reconsidered for a future release + if it can be made more effective. + Likely reason: __fastcall on Windows is like stdcall, therefore + the compiler cannot perform stack optimizations for call clusters. +*/ + +/* Make sure all of these are defined if they aren't already. */ + +#ifndef FASTCALL +#define FASTCALL +#endif + +#ifndef PTRCALL +#define PTRCALL +#endif + +#ifndef PTRFASTCALL +#define PTRFASTCALL +#endif + +#ifndef XML_MIN_SIZE +#if !defined(__cplusplus) && !defined(inline) +#ifdef __GNUC__ +#define inline __inline +#endif /* __GNUC__ */ +#endif +#endif /* XML_MIN_SIZE */ + +#ifdef __cplusplus +#define inline inline +#else +#ifndef inline +#define inline +#endif +#endif + +#ifndef UNUSED_P +# ifdef __GNUC__ +# define UNUSED_P(p) UNUSED_ ## p __attribute__((__unused__)) +# else +# define UNUSED_P(p) UNUSED_ ## p +# endif +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + +void +align_limit_to_full_utf8_characters(const char * from, const char ** fromLimRef); + + +#ifdef __cplusplus +} +#endif diff --git a/deps/EXPAT/expat/latin1tab.h b/deps/EXPAT/expat/latin1tab.h new file mode 100644 index 0000000000..53c25d76b2 --- /dev/null +++ b/deps/EXPAT/expat/latin1tab.h @@ -0,0 +1,36 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME, +/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, diff --git a/deps/EXPAT/expat/nametab.h b/deps/EXPAT/expat/nametab.h new file mode 100644 index 0000000000..b05e62c77a --- /dev/null +++ b/deps/EXPAT/expat/nametab.h @@ -0,0 +1,150 @@ +static const unsigned namingBitmap[] = { +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF, +0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000, +0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060, +0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003, +0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003, +0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000, +0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001, +0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003, +0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000, +0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003, +0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003, +0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000, +0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000, +0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF, +0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB, +0x40000000, 0xF580C900, 0x00000007, 0x02010800, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF, +0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF, +0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF, +0x00000000, 0x00004C40, 0x00000000, 0x00000000, +0x00000007, 0x00000000, 0x00000000, 0x00000000, +0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF, +0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF, +0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000, +0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003, +0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF, +0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF, +0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF, +0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF, +0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0, +0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1, +0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3, +0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80, +0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000, +0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000, +0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF, +0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x1FFF0000, 0x00000002, +0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF, +0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF, +}; +static const unsigned char nmstrtPages[] = { +0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, +0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const unsigned char namePages[] = { +0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00, +0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/deps/EXPAT/expat/utf8tab.h b/deps/EXPAT/expat/utf8tab.h new file mode 100644 index 0000000000..7bb3e77603 --- /dev/null +++ b/deps/EXPAT/expat/utf8tab.h @@ -0,0 +1,37 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + + +/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4, +/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM, diff --git a/deps/EXPAT/expat/xmlparse.c b/deps/EXPAT/expat/xmlparse.c new file mode 100644 index 0000000000..fbe5e02006 --- /dev/null +++ b/deps/EXPAT/expat/xmlparse.c @@ -0,0 +1,6458 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include +#include /* memset(), memcpy() */ +#include +#include /* UINT_MAX */ + +#ifdef WIN32 +#define getpid GetCurrentProcessId +#else +#include /* gettimeofday() */ +#include /* getpid() */ +#include /* getpid() */ +#endif + +#define XML_BUILDING_EXPAT 1 + +#include "expat_config.h" +#include "ascii.h" +#include "expat.h" + +#ifdef XML_UNICODE +#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX +#define XmlConvert XmlUtf16Convert +#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS +#define XmlEncode XmlUtf16Encode +/* Using pointer subtraction to convert to integer type. */ +#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((char *)(s) - (char *)NULL) & 1)) +typedef unsigned short ICHAR; +#else +#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX +#define XmlConvert XmlUtf8Convert +#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS +#define XmlEncode XmlUtf8Encode +#define MUST_CONVERT(enc, s) (!(enc)->isUtf8) +typedef char ICHAR; +#endif + + +#ifndef XML_NS + +#define XmlInitEncodingNS XmlInitEncoding +#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding +#undef XmlGetInternalEncodingNS +#define XmlGetInternalEncodingNS XmlGetInternalEncoding +#define XmlParseXmlDeclNS XmlParseXmlDecl + +#endif + +#ifdef XML_UNICODE + +#ifdef XML_UNICODE_WCHAR_T +#define XML_T(x) (const wchar_t)x +#define XML_L(x) L ## x +#else +#define XML_T(x) (const unsigned short)x +#define XML_L(x) x +#endif + +#else + +#define XML_T(x) x +#define XML_L(x) x + +#endif + +/* Round up n to be a multiple of sz, where sz is a power of 2. */ +#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1)) + +/* Handle the case where memmove() doesn't exist. */ +#ifndef HAVE_MEMMOVE +#ifdef HAVE_BCOPY +#define memmove(d,s,l) bcopy((s),(d),(l)) +#else +#error memmove does not exist on this platform, nor is a substitute available +#endif /* HAVE_BCOPY */ +#endif /* HAVE_MEMMOVE */ + +#include "internal.h" +#include "xmltok.h" +#include "xmlrole.h" + +typedef const XML_Char *KEY; + +typedef struct { + KEY name; +} NAMED; + +typedef struct { + NAMED **v; + unsigned char power; + size_t size; + size_t used; + const XML_Memory_Handling_Suite *mem; +} HASH_TABLE; + +/* Basic character hash algorithm, taken from Python's string hash: + h = h * 1000003 ^ character, the constant being a prime number. + +*/ +#ifdef XML_UNICODE +#define CHAR_HASH(h, c) \ + (((h) * 0xF4243) ^ (unsigned short)(c)) +#else +#define CHAR_HASH(h, c) \ + (((h) * 0xF4243) ^ (unsigned char)(c)) +#endif + +/* For probing (after a collision) we need a step size relative prime + to the hash table size, which is a power of 2. We use double-hashing, + since we can calculate a second hash value cheaply by taking those bits + of the first hash value that were discarded (masked out) when the table + index was calculated: index = hash & mask, where mask = table->size - 1. + We limit the maximum step size to table->size / 4 (mask >> 2) and make + it odd, since odd numbers are always relative prime to a power of 2. +*/ +#define SECOND_HASH(hash, mask, power) \ + ((((hash) & ~(mask)) >> ((power) - 1)) & ((mask) >> 2)) +#define PROBE_STEP(hash, mask, power) \ + ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1)) + +typedef struct { + NAMED **p; + NAMED **end; +} HASH_TABLE_ITER; + +#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */ +#define INIT_DATA_BUF_SIZE 1024 +#define INIT_ATTS_SIZE 16 +#define INIT_ATTS_VERSION 0xFFFFFFFF +#define INIT_BLOCK_SIZE 1024 +#define INIT_BUFFER_SIZE 1024 + +#define EXPAND_SPARE 24 + +typedef struct binding { + struct prefix *prefix; + struct binding *nextTagBinding; + struct binding *prevPrefixBinding; + const struct attribute_id *attId; + XML_Char *uri; + int uriLen; + int uriAlloc; +} BINDING; + +typedef struct prefix { + const XML_Char *name; + BINDING *binding; +} PREFIX; + +typedef struct { + const XML_Char *str; + const XML_Char *localPart; + const XML_Char *prefix; + int strLen; + int uriLen; + int prefixLen; +} TAG_NAME; + +/* TAG represents an open element. + The name of the element is stored in both the document and API + encodings. The memory buffer 'buf' is a separately-allocated + memory area which stores the name. During the XML_Parse()/ + XMLParseBuffer() when the element is open, the memory for the 'raw' + version of the name (in the document encoding) is shared with the + document buffer. If the element is open across calls to + XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to + contain the 'raw' name as well. + + A parser re-uses these structures, maintaining a list of allocated + TAG objects in a free list. +*/ +typedef struct tag { + struct tag *parent; /* parent of this element */ + const char *rawName; /* tagName in the original encoding */ + int rawNameLength; + TAG_NAME name; /* tagName in the API encoding */ + char *buf; /* buffer for name components */ + char *bufEnd; /* end of the buffer */ + BINDING *bindings; +} TAG; + +typedef struct { + const XML_Char *name; + const XML_Char *textPtr; + int textLen; /* length in XML_Chars */ + int processed; /* # of processed bytes - when suspended */ + const XML_Char *systemId; + const XML_Char *base; + const XML_Char *publicId; + const XML_Char *notation; + XML_Bool open; + XML_Bool is_param; + XML_Bool is_internal; /* true if declared in internal subset outside PE */ +} ENTITY; + +typedef struct { + enum XML_Content_Type type; + enum XML_Content_Quant quant; + const XML_Char * name; + int firstchild; + int lastchild; + int childcnt; + int nextsib; +} CONTENT_SCAFFOLD; + +#define INIT_SCAFFOLD_ELEMENTS 32 + +typedef struct block { + struct block *next; + int size; + XML_Char s[1]; +} BLOCK; + +typedef struct { + BLOCK *blocks; + BLOCK *freeBlocks; + const XML_Char *end; + XML_Char *ptr; + XML_Char *start; + const XML_Memory_Handling_Suite *mem; +} STRING_POOL; + +/* The XML_Char before the name is used to determine whether + an attribute has been specified. */ +typedef struct attribute_id { + XML_Char *name; + PREFIX *prefix; + XML_Bool maybeTokenized; + XML_Bool xmlns; +} ATTRIBUTE_ID; + +typedef struct { + const ATTRIBUTE_ID *id; + XML_Bool isCdata; + const XML_Char *value; +} DEFAULT_ATTRIBUTE; + +typedef struct { + unsigned long version; + unsigned long hash; + const XML_Char *uriName; +} NS_ATT; + +typedef struct { + const XML_Char *name; + PREFIX *prefix; + const ATTRIBUTE_ID *idAtt; + int nDefaultAtts; + int allocDefaultAtts; + DEFAULT_ATTRIBUTE *defaultAtts; +} ELEMENT_TYPE; + +typedef struct { + HASH_TABLE generalEntities; + HASH_TABLE elementTypes; + HASH_TABLE attributeIds; + HASH_TABLE prefixes; + STRING_POOL pool; + STRING_POOL entityValuePool; + /* false once a parameter entity reference has been skipped */ + XML_Bool keepProcessing; + /* true once an internal or external PE reference has been encountered; + this includes the reference to an external subset */ + XML_Bool hasParamEntityRefs; + XML_Bool standalone; +#ifdef XML_DTD + /* indicates if external PE has been read */ + XML_Bool paramEntityRead; + HASH_TABLE paramEntities; +#endif /* XML_DTD */ + PREFIX defaultPrefix; + /* === scaffolding for building content model === */ + XML_Bool in_eldecl; + CONTENT_SCAFFOLD *scaffold; + unsigned contentStringLen; + unsigned scaffSize; + unsigned scaffCount; + int scaffLevel; + int *scaffIndex; +} DTD; + +typedef struct open_internal_entity { + const char *internalEventPtr; + const char *internalEventEndPtr; + struct open_internal_entity *next; + ENTITY *entity; + int startTagLevel; + XML_Bool betweenDecl; /* WFC: PE Between Declarations */ +} OPEN_INTERNAL_ENTITY; + +typedef enum XML_Error PTRCALL Processor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr); + +static Processor prologProcessor; +static Processor prologInitProcessor; +static Processor contentProcessor; +static Processor cdataSectionProcessor; +#ifdef XML_DTD +static Processor ignoreSectionProcessor; +static Processor externalParEntProcessor; +static Processor externalParEntInitProcessor; +static Processor entityValueProcessor; +static Processor entityValueInitProcessor; +#endif /* XML_DTD */ +static Processor epilogProcessor; +static Processor errorProcessor; +static Processor externalEntityInitProcessor; +static Processor externalEntityInitProcessor2; +static Processor externalEntityInitProcessor3; +static Processor externalEntityContentProcessor; +static Processor internalEntityProcessor; + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName); +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next); +static enum XML_Error +initializeEncoding(XML_Parser parser); +static enum XML_Error +doProlog(XML_Parser parser, const ENCODING *enc, const char *s, + const char *end, int tok, const char *next, const char **nextPtr, + XML_Bool haveMore); +static enum XML_Error +processInternalEntity(XML_Parser parser, ENTITY *entity, + XML_Bool betweenDecl); +static enum XML_Error +doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + const char *start, const char *end, const char **endPtr, + XML_Bool haveMore); +static enum XML_Error +doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr, + const char *end, const char **nextPtr, XML_Bool haveMore); +#ifdef XML_DTD +static enum XML_Error +doIgnoreSection(XML_Parser parser, const ENCODING *, const char **startPtr, + const char *end, const char **nextPtr, XML_Bool haveMore); +#endif /* XML_DTD */ + +static enum XML_Error +storeAtts(XML_Parser parser, const ENCODING *, const char *s, + TAG_NAME *tagNamePtr, BINDING **bindingsPtr); +static enum XML_Error +addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + const XML_Char *uri, BINDING **bindingsPtr); +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata, + XML_Bool isId, const XML_Char *dfltValue, XML_Parser parser); +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, + const char *, const char *, STRING_POOL *); +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, + const char *, const char *, STRING_POOL *); +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static int +setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); +static enum XML_Error +storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); +static int +reportComment(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static void +reportDefault(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); + +static const XML_Char * getContext(XML_Parser parser); +static XML_Bool +setContext(XML_Parser parser, const XML_Char *context); + +static void FASTCALL normalizePublicId(XML_Char *s); + +static DTD * dtdCreate(const XML_Memory_Handling_Suite *ms); +/* do not call if parentParser != NULL */ +static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); +static void +dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms); +static int +dtdCopy(XML_Parser oldParser, + DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms); +static int +copyEntityTable(XML_Parser oldParser, + HASH_TABLE *, STRING_POOL *, const HASH_TABLE *); +static NAMED * +lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize); +static void FASTCALL +hashTableInit(HASH_TABLE *, const XML_Memory_Handling_Suite *ms); +static void FASTCALL hashTableClear(HASH_TABLE *); +static void FASTCALL hashTableDestroy(HASH_TABLE *); +static void FASTCALL +hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); +static NAMED * FASTCALL hashTableIterNext(HASH_TABLE_ITER *); + +static void FASTCALL +poolInit(STRING_POOL *, const XML_Memory_Handling_Suite *ms); +static void FASTCALL poolClear(STRING_POOL *); +static void FASTCALL poolDestroy(STRING_POOL *); +static XML_Char * +poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Char * +poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Bool FASTCALL poolGrow(STRING_POOL *pool); +static const XML_Char * FASTCALL +poolCopyString(STRING_POOL *pool, const XML_Char *s); +static const XML_Char * +poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n); +static const XML_Char * FASTCALL +poolAppendString(STRING_POOL *pool, const XML_Char *s); + +static int FASTCALL nextScaffoldPart(XML_Parser parser); +static XML_Content * build_model(XML_Parser parser); +static ELEMENT_TYPE * +getElementType(XML_Parser parser, const ENCODING *enc, + const char *ptr, const char *end); + +static unsigned long generate_hash_secret_salt(XML_Parser parser); +static XML_Bool startParsing(XML_Parser parser); + +static XML_Parser +parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep, + DTD *dtd); + +static void +parserInit(XML_Parser parser, const XML_Char *encodingName); + +#define poolStart(pool) ((pool)->start) +#define poolEnd(pool) ((pool)->ptr) +#define poolLength(pool) ((pool)->ptr - (pool)->start) +#define poolChop(pool) ((void)--(pool->ptr)) +#define poolLastChar(pool) (((pool)->ptr)[-1]) +#define poolDiscard(pool) ((pool)->ptr = (pool)->start) +#define poolFinish(pool) ((pool)->start = (pool)->ptr) +#define poolAppendChar(pool, c) \ + (((pool)->ptr == (pool)->end && !poolGrow(pool)) \ + ? 0 \ + : ((*((pool)->ptr)++ = c), 1)) + +struct XML_ParserStruct { + /* The first member must be userData so that the XML_GetUserData + macro works. */ + void *m_userData; + void *m_handlerArg; + char *m_buffer; + const XML_Memory_Handling_Suite m_mem; + /* first character to be parsed */ + const char *m_bufferPtr; + /* past last character to be parsed */ + char *m_bufferEnd; + /* allocated end of buffer */ + const char *m_bufferLim; + XML_Index m_parseEndByteIndex; + const char *m_parseEndPtr; + XML_Char *m_dataBuf; + XML_Char *m_dataBufEnd; + XML_StartElementHandler m_startElementHandler; + XML_EndElementHandler m_endElementHandler; + XML_CharacterDataHandler m_characterDataHandler; + XML_ProcessingInstructionHandler m_processingInstructionHandler; + XML_CommentHandler m_commentHandler; + XML_StartCdataSectionHandler m_startCdataSectionHandler; + XML_EndCdataSectionHandler m_endCdataSectionHandler; + XML_DefaultHandler m_defaultHandler; + XML_StartDoctypeDeclHandler m_startDoctypeDeclHandler; + XML_EndDoctypeDeclHandler m_endDoctypeDeclHandler; + XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler; + XML_NotationDeclHandler m_notationDeclHandler; + XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler; + XML_NotStandaloneHandler m_notStandaloneHandler; + XML_ExternalEntityRefHandler m_externalEntityRefHandler; + XML_Parser m_externalEntityRefHandlerArg; + XML_SkippedEntityHandler m_skippedEntityHandler; + XML_UnknownEncodingHandler m_unknownEncodingHandler; + XML_ElementDeclHandler m_elementDeclHandler; + XML_AttlistDeclHandler m_attlistDeclHandler; + XML_EntityDeclHandler m_entityDeclHandler; + XML_XmlDeclHandler m_xmlDeclHandler; + const ENCODING *m_encoding; + INIT_ENCODING m_initEncoding; + const ENCODING *m_internalEncoding; + const XML_Char *m_protocolEncodingName; + XML_Bool m_ns; + XML_Bool m_ns_triplets; + void *m_unknownEncodingMem; + void *m_unknownEncodingData; + void *m_unknownEncodingHandlerData; + void (XMLCALL *m_unknownEncodingRelease)(void *); + PROLOG_STATE m_prologState; + Processor *m_processor; + enum XML_Error m_errorCode; + const char *m_eventPtr; + const char *m_eventEndPtr; + const char *m_positionPtr; + OPEN_INTERNAL_ENTITY *m_openInternalEntities; + OPEN_INTERNAL_ENTITY *m_freeInternalEntities; + XML_Bool m_defaultExpandInternalEntities; + int m_tagLevel; + ENTITY *m_declEntity; + const XML_Char *m_doctypeName; + const XML_Char *m_doctypeSysid; + const XML_Char *m_doctypePubid; + const XML_Char *m_declAttributeType; + const XML_Char *m_declNotationName; + const XML_Char *m_declNotationPublicId; + ELEMENT_TYPE *m_declElementType; + ATTRIBUTE_ID *m_declAttributeId; + XML_Bool m_declAttributeIsCdata; + XML_Bool m_declAttributeIsId; + DTD *m_dtd; + const XML_Char *m_curBase; + TAG *m_tagStack; + TAG *m_freeTagList; + BINDING *m_inheritedBindings; + BINDING *m_freeBindingList; + int m_attsSize; + int m_nSpecifiedAtts; + int m_idAttIndex; + ATTRIBUTE *m_atts; + NS_ATT *m_nsAtts; + unsigned long m_nsAttsVersion; + unsigned char m_nsAttsPower; +#ifdef XML_ATTR_INFO + XML_AttrInfo *m_attInfo; +#endif + POSITION m_position; + STRING_POOL m_tempPool; + STRING_POOL m_temp2Pool; + char *m_groupConnector; + unsigned int m_groupSize; + XML_Char m_namespaceSeparator; + XML_Parser m_parentParser; + XML_ParsingStatus m_parsingStatus; +#ifdef XML_DTD + XML_Bool m_isParamEntity; + XML_Bool m_useForeignDTD; + enum XML_ParamEntityParsing m_paramEntityParsing; +#endif + unsigned long m_hash_secret_salt; +}; + +#define MALLOC(s) (parser->m_mem.malloc_fcn((s))) +#define REALLOC(p,s) (parser->m_mem.realloc_fcn((p),(s))) +#define FREE(p) (parser->m_mem.free_fcn((p))) + +#define userData (parser->m_userData) +#define handlerArg (parser->m_handlerArg) +#define startElementHandler (parser->m_startElementHandler) +#define endElementHandler (parser->m_endElementHandler) +#define characterDataHandler (parser->m_characterDataHandler) +#define processingInstructionHandler \ + (parser->m_processingInstructionHandler) +#define commentHandler (parser->m_commentHandler) +#define startCdataSectionHandler \ + (parser->m_startCdataSectionHandler) +#define endCdataSectionHandler (parser->m_endCdataSectionHandler) +#define defaultHandler (parser->m_defaultHandler) +#define startDoctypeDeclHandler (parser->m_startDoctypeDeclHandler) +#define endDoctypeDeclHandler (parser->m_endDoctypeDeclHandler) +#define unparsedEntityDeclHandler \ + (parser->m_unparsedEntityDeclHandler) +#define notationDeclHandler (parser->m_notationDeclHandler) +#define startNamespaceDeclHandler \ + (parser->m_startNamespaceDeclHandler) +#define endNamespaceDeclHandler (parser->m_endNamespaceDeclHandler) +#define notStandaloneHandler (parser->m_notStandaloneHandler) +#define externalEntityRefHandler \ + (parser->m_externalEntityRefHandler) +#define externalEntityRefHandlerArg \ + (parser->m_externalEntityRefHandlerArg) +#define internalEntityRefHandler \ + (parser->m_internalEntityRefHandler) +#define skippedEntityHandler (parser->m_skippedEntityHandler) +#define unknownEncodingHandler (parser->m_unknownEncodingHandler) +#define elementDeclHandler (parser->m_elementDeclHandler) +#define attlistDeclHandler (parser->m_attlistDeclHandler) +#define entityDeclHandler (parser->m_entityDeclHandler) +#define xmlDeclHandler (parser->m_xmlDeclHandler) +#define encoding (parser->m_encoding) +#define initEncoding (parser->m_initEncoding) +#define internalEncoding (parser->m_internalEncoding) +#define unknownEncodingMem (parser->m_unknownEncodingMem) +#define unknownEncodingData (parser->m_unknownEncodingData) +#define unknownEncodingHandlerData \ + (parser->m_unknownEncodingHandlerData) +#define unknownEncodingRelease (parser->m_unknownEncodingRelease) +#define protocolEncodingName (parser->m_protocolEncodingName) +#define ns (parser->m_ns) +#define ns_triplets (parser->m_ns_triplets) +#define prologState (parser->m_prologState) +#define processor (parser->m_processor) +#define errorCode (parser->m_errorCode) +#define eventPtr (parser->m_eventPtr) +#define eventEndPtr (parser->m_eventEndPtr) +#define positionPtr (parser->m_positionPtr) +#define position (parser->m_position) +#define openInternalEntities (parser->m_openInternalEntities) +#define freeInternalEntities (parser->m_freeInternalEntities) +#define defaultExpandInternalEntities \ + (parser->m_defaultExpandInternalEntities) +#define tagLevel (parser->m_tagLevel) +#define buffer (parser->m_buffer) +#define bufferPtr (parser->m_bufferPtr) +#define bufferEnd (parser->m_bufferEnd) +#define parseEndByteIndex (parser->m_parseEndByteIndex) +#define parseEndPtr (parser->m_parseEndPtr) +#define bufferLim (parser->m_bufferLim) +#define dataBuf (parser->m_dataBuf) +#define dataBufEnd (parser->m_dataBufEnd) +#define _dtd (parser->m_dtd) +#define curBase (parser->m_curBase) +#define declEntity (parser->m_declEntity) +#define doctypeName (parser->m_doctypeName) +#define doctypeSysid (parser->m_doctypeSysid) +#define doctypePubid (parser->m_doctypePubid) +#define declAttributeType (parser->m_declAttributeType) +#define declNotationName (parser->m_declNotationName) +#define declNotationPublicId (parser->m_declNotationPublicId) +#define declElementType (parser->m_declElementType) +#define declAttributeId (parser->m_declAttributeId) +#define declAttributeIsCdata (parser->m_declAttributeIsCdata) +#define declAttributeIsId (parser->m_declAttributeIsId) +#define freeTagList (parser->m_freeTagList) +#define freeBindingList (parser->m_freeBindingList) +#define inheritedBindings (parser->m_inheritedBindings) +#define tagStack (parser->m_tagStack) +#define atts (parser->m_atts) +#define attsSize (parser->m_attsSize) +#define nSpecifiedAtts (parser->m_nSpecifiedAtts) +#define idAttIndex (parser->m_idAttIndex) +#define nsAtts (parser->m_nsAtts) +#define nsAttsVersion (parser->m_nsAttsVersion) +#define nsAttsPower (parser->m_nsAttsPower) +#define attInfo (parser->m_attInfo) +#define tempPool (parser->m_tempPool) +#define temp2Pool (parser->m_temp2Pool) +#define groupConnector (parser->m_groupConnector) +#define groupSize (parser->m_groupSize) +#define namespaceSeparator (parser->m_namespaceSeparator) +#define parentParser (parser->m_parentParser) +#define ps_parsing (parser->m_parsingStatus.parsing) +#define ps_finalBuffer (parser->m_parsingStatus.finalBuffer) +#ifdef XML_DTD +#define isParamEntity (parser->m_isParamEntity) +#define useForeignDTD (parser->m_useForeignDTD) +#define paramEntityParsing (parser->m_paramEntityParsing) +#endif /* XML_DTD */ +#define hash_secret_salt (parser->m_hash_secret_salt) + +XML_Parser XMLCALL +XML_ParserCreate(const XML_Char *encodingName) +{ + return XML_ParserCreate_MM(encodingName, NULL, NULL); +} + +XML_Parser XMLCALL +XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) +{ + XML_Char tmp[2]; + *tmp = nsSep; + return XML_ParserCreate_MM(encodingName, NULL, tmp); +} + +static const XML_Char implicitContext[] = { + ASCII_x, ASCII_m, ASCII_l, ASCII_EQUALS, ASCII_h, ASCII_t, ASCII_t, ASCII_p, + ASCII_COLON, ASCII_SLASH, ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, + ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, ASCII_g, + ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, ASCII_SLASH, ASCII_1, ASCII_9, + ASCII_9, ASCII_8, ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m, ASCII_e, + ASCII_s, ASCII_p, ASCII_a, ASCII_c, ASCII_e, '\0' +}; + +static unsigned long +gather_time_entropy(void) +{ +#ifdef WIN32 + FILETIME ft; + GetSystemTimeAsFileTime(&ft); /* never fails */ + return ft.dwHighDateTime ^ ft.dwLowDateTime; +#else + struct timeval tv; + int gettimeofday_res; + + gettimeofday_res = gettimeofday(&tv, NULL); + assert (gettimeofday_res == 0); + + /* Microseconds time is <20 bits entropy */ + return tv.tv_usec; +#endif +} + +static unsigned long +generate_hash_secret_salt(XML_Parser parser) +{ + /* Process ID is 0 bits entropy if attacker has local access + * XML_Parser address is few bits of entropy if attacker has local access */ + // Prusa3D specific: Fix for a following warning, which turns to an error on some Perl/XS installations: + // error: cast from 'XML_Parser' to 'long unsigned int' loses precision [-fpermissive] + unsigned long *parser_addr = (unsigned long*)&parser; + const unsigned long entropy = + gather_time_entropy() ^ getpid() ^ *parser_addr; + + /* Factors are 2^31-1 and 2^61-1 (Mersenne primes M31 and M61) */ + if (sizeof(unsigned long) == 4) { + return entropy * 2147483647; + } else { + return entropy * (unsigned long)2305843009213693951; + } +} + +static XML_Bool /* only valid for root parser */ +startParsing(XML_Parser parser) +{ + /* hash functions must be initialized before setContext() is called */ + if (hash_secret_salt == 0) + hash_secret_salt = generate_hash_secret_salt(parser); + if (ns) { + /* implicit context only set for root parser, since child + parsers (i.e. external entity parsers) will inherit it + */ + return setContext(parser, implicitContext); + } + return XML_TRUE; +} + +XML_Parser XMLCALL +XML_ParserCreate_MM(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep) +{ + return parserCreate(encodingName, memsuite, nameSep, NULL); +} + +static XML_Parser +parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep, + DTD *dtd) +{ + XML_Parser parser; + + if (memsuite) { + XML_Memory_Handling_Suite *mtemp; + parser = (XML_Parser) + memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = memsuite->malloc_fcn; + mtemp->realloc_fcn = memsuite->realloc_fcn; + mtemp->free_fcn = memsuite->free_fcn; + } + } + else { + XML_Memory_Handling_Suite *mtemp; + parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = malloc; + mtemp->realloc_fcn = realloc; + mtemp->free_fcn = free; + } + } + + if (!parser) + return parser; + + buffer = NULL; + bufferLim = NULL; + + attsSize = INIT_ATTS_SIZE; + atts = (ATTRIBUTE *)MALLOC(attsSize * sizeof(ATTRIBUTE)); + if (atts == NULL) { + FREE(parser); + return NULL; + } +#ifdef XML_ATTR_INFO + attInfo = (XML_AttrInfo*)MALLOC(attsSize * sizeof(XML_AttrInfo)); + if (attInfo == NULL) { + FREE(atts); + FREE(parser); + return NULL; + } +#endif + dataBuf = (XML_Char *)MALLOC(INIT_DATA_BUF_SIZE * sizeof(XML_Char)); + if (dataBuf == NULL) { + FREE(atts); +#ifdef XML_ATTR_INFO + FREE(attInfo); +#endif + FREE(parser); + return NULL; + } + dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE; + + if (dtd) + _dtd = dtd; + else { + _dtd = dtdCreate(&parser->m_mem); + if (_dtd == NULL) { + FREE(dataBuf); + FREE(atts); +#ifdef XML_ATTR_INFO + FREE(attInfo); +#endif + FREE(parser); + return NULL; + } + } + + freeBindingList = NULL; + freeTagList = NULL; + freeInternalEntities = NULL; + + groupSize = 0; + groupConnector = NULL; + + unknownEncodingHandler = NULL; + unknownEncodingHandlerData = NULL; + + namespaceSeparator = ASCII_EXCL; + ns = XML_FALSE; + ns_triplets = XML_FALSE; + + nsAtts = NULL; + nsAttsVersion = 0; + nsAttsPower = 0; + + poolInit(&tempPool, &(parser->m_mem)); + poolInit(&temp2Pool, &(parser->m_mem)); + parserInit(parser, encodingName); + + if (encodingName && !protocolEncodingName) { + XML_ParserFree(parser); + return NULL; + } + + if (nameSep) { + ns = XML_TRUE; + internalEncoding = XmlGetInternalEncodingNS(); + namespaceSeparator = *nameSep; + } + else { + internalEncoding = XmlGetInternalEncoding(); + } + + return parser; +} + +static void +parserInit(XML_Parser parser, const XML_Char *encodingName) +{ + processor = prologInitProcessor; + XmlPrologStateInit(&prologState); + protocolEncodingName = (encodingName != NULL + ? poolCopyString(&tempPool, encodingName) + : NULL); + curBase = NULL; + XmlInitEncoding(&initEncoding, &encoding, 0); + userData = NULL; + handlerArg = NULL; + startElementHandler = NULL; + endElementHandler = NULL; + characterDataHandler = NULL; + processingInstructionHandler = NULL; + commentHandler = NULL; + startCdataSectionHandler = NULL; + endCdataSectionHandler = NULL; + defaultHandler = NULL; + startDoctypeDeclHandler = NULL; + endDoctypeDeclHandler = NULL; + unparsedEntityDeclHandler = NULL; + notationDeclHandler = NULL; + startNamespaceDeclHandler = NULL; + endNamespaceDeclHandler = NULL; + notStandaloneHandler = NULL; + externalEntityRefHandler = NULL; + externalEntityRefHandlerArg = parser; + skippedEntityHandler = NULL; + elementDeclHandler = NULL; + attlistDeclHandler = NULL; + entityDeclHandler = NULL; + xmlDeclHandler = NULL; + bufferPtr = buffer; + bufferEnd = buffer; + parseEndByteIndex = 0; + parseEndPtr = NULL; + declElementType = NULL; + declAttributeId = NULL; + declEntity = NULL; + doctypeName = NULL; + doctypeSysid = NULL; + doctypePubid = NULL; + declAttributeType = NULL; + declNotationName = NULL; + declNotationPublicId = NULL; + declAttributeIsCdata = XML_FALSE; + declAttributeIsId = XML_FALSE; + memset(&position, 0, sizeof(POSITION)); + errorCode = XML_ERROR_NONE; + eventPtr = NULL; + eventEndPtr = NULL; + positionPtr = NULL; + openInternalEntities = NULL; + defaultExpandInternalEntities = XML_TRUE; + tagLevel = 0; + tagStack = NULL; + inheritedBindings = NULL; + nSpecifiedAtts = 0; + unknownEncodingMem = NULL; + unknownEncodingRelease = NULL; + unknownEncodingData = NULL; + parentParser = NULL; + ps_parsing = XML_INITIALIZED; +#ifdef XML_DTD + isParamEntity = XML_FALSE; + useForeignDTD = XML_FALSE; + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif + hash_secret_salt = 0; +} + +/* moves list of bindings to freeBindingList */ +static void FASTCALL +moveToFreeBindingList(XML_Parser parser, BINDING *bindings) +{ + while (bindings) { + BINDING *b = bindings; + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + } +} + +XML_Bool XMLCALL +XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) +{ + TAG *tStk; + OPEN_INTERNAL_ENTITY *openEntityList; + if (parentParser) + return XML_FALSE; + /* move tagStack to freeTagList */ + tStk = tagStack; + while (tStk) { + TAG *tag = tStk; + tStk = tStk->parent; + tag->parent = freeTagList; + moveToFreeBindingList(parser, tag->bindings); + tag->bindings = NULL; + freeTagList = tag; + } + /* move openInternalEntities to freeInternalEntities */ + openEntityList = openInternalEntities; + while (openEntityList) { + OPEN_INTERNAL_ENTITY *openEntity = openEntityList; + openEntityList = openEntity->next; + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + moveToFreeBindingList(parser, inheritedBindings); + FREE(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + poolClear(&tempPool); + poolClear(&temp2Pool); + parserInit(parser, encodingName); + dtdReset(_dtd, &parser->m_mem); + return XML_TRUE; +} + +enum XML_Status XMLCALL +XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + /* Block after XML_Parse()/XML_ParseBuffer() has been called. + XXX There's no way for the caller to determine which of the + XXX possible error cases caused the XML_STATUS_ERROR return. + */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return XML_STATUS_ERROR; + if (encodingName == NULL) + protocolEncodingName = NULL; + else { + protocolEncodingName = poolCopyString(&tempPool, encodingName); + if (!protocolEncodingName) + return XML_STATUS_ERROR; + } + return XML_STATUS_OK; +} + +XML_Parser XMLCALL +XML_ExternalEntityParserCreate(XML_Parser oldParser, + const XML_Char *context, + const XML_Char *encodingName) +{ + XML_Parser parser = oldParser; + DTD *newDtd = NULL; + DTD *oldDtd = _dtd; + XML_StartElementHandler oldStartElementHandler = startElementHandler; + XML_EndElementHandler oldEndElementHandler = endElementHandler; + XML_CharacterDataHandler oldCharacterDataHandler = characterDataHandler; + XML_ProcessingInstructionHandler oldProcessingInstructionHandler + = processingInstructionHandler; + XML_CommentHandler oldCommentHandler = commentHandler; + XML_StartCdataSectionHandler oldStartCdataSectionHandler + = startCdataSectionHandler; + XML_EndCdataSectionHandler oldEndCdataSectionHandler + = endCdataSectionHandler; + XML_DefaultHandler oldDefaultHandler = defaultHandler; + XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler + = unparsedEntityDeclHandler; + XML_NotationDeclHandler oldNotationDeclHandler = notationDeclHandler; + XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler + = startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler + = endNamespaceDeclHandler; + XML_NotStandaloneHandler oldNotStandaloneHandler = notStandaloneHandler; + XML_ExternalEntityRefHandler oldExternalEntityRefHandler + = externalEntityRefHandler; + XML_SkippedEntityHandler oldSkippedEntityHandler = skippedEntityHandler; + XML_UnknownEncodingHandler oldUnknownEncodingHandler + = unknownEncodingHandler; + XML_ElementDeclHandler oldElementDeclHandler = elementDeclHandler; + XML_AttlistDeclHandler oldAttlistDeclHandler = attlistDeclHandler; + XML_EntityDeclHandler oldEntityDeclHandler = entityDeclHandler; + XML_XmlDeclHandler oldXmlDeclHandler = xmlDeclHandler; + ELEMENT_TYPE * oldDeclElementType = declElementType; + + void *oldUserData = userData; + void *oldHandlerArg = handlerArg; + XML_Bool oldDefaultExpandInternalEntities = defaultExpandInternalEntities; + XML_Parser oldExternalEntityRefHandlerArg = externalEntityRefHandlerArg; +#ifdef XML_DTD + enum XML_ParamEntityParsing oldParamEntityParsing = paramEntityParsing; + int oldInEntityValue = prologState.inEntityValue; +#endif + XML_Bool oldns_triplets = ns_triplets; + /* Note that the new parser shares the same hash secret as the old + parser, so that dtdCopy and copyEntityTable can lookup values + from hash tables associated with either parser without us having + to worry which hash secrets each table has. + */ + unsigned long oldhash_secret_salt = hash_secret_salt; + +#ifdef XML_DTD + if (!context) + newDtd = oldDtd; +#endif /* XML_DTD */ + + /* Note that the magical uses of the pre-processor to make field + access look more like C++ require that `parser' be overwritten + here. This makes this function more painful to follow than it + would be otherwise. + */ + if (ns) { + XML_Char tmp[2]; + *tmp = namespaceSeparator; + parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); + } + else { + parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); + } + + if (!parser) + return NULL; + + startElementHandler = oldStartElementHandler; + endElementHandler = oldEndElementHandler; + characterDataHandler = oldCharacterDataHandler; + processingInstructionHandler = oldProcessingInstructionHandler; + commentHandler = oldCommentHandler; + startCdataSectionHandler = oldStartCdataSectionHandler; + endCdataSectionHandler = oldEndCdataSectionHandler; + defaultHandler = oldDefaultHandler; + unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler; + notationDeclHandler = oldNotationDeclHandler; + startNamespaceDeclHandler = oldStartNamespaceDeclHandler; + endNamespaceDeclHandler = oldEndNamespaceDeclHandler; + notStandaloneHandler = oldNotStandaloneHandler; + externalEntityRefHandler = oldExternalEntityRefHandler; + skippedEntityHandler = oldSkippedEntityHandler; + unknownEncodingHandler = oldUnknownEncodingHandler; + elementDeclHandler = oldElementDeclHandler; + attlistDeclHandler = oldAttlistDeclHandler; + entityDeclHandler = oldEntityDeclHandler; + xmlDeclHandler = oldXmlDeclHandler; + declElementType = oldDeclElementType; + userData = oldUserData; + if (oldUserData == oldHandlerArg) + handlerArg = userData; + else + handlerArg = parser; + if (oldExternalEntityRefHandlerArg != oldParser) + externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg; + defaultExpandInternalEntities = oldDefaultExpandInternalEntities; + ns_triplets = oldns_triplets; + hash_secret_salt = oldhash_secret_salt; + parentParser = oldParser; +#ifdef XML_DTD + paramEntityParsing = oldParamEntityParsing; + prologState.inEntityValue = oldInEntityValue; + if (context) { +#endif /* XML_DTD */ + if (!dtdCopy(oldParser, _dtd, oldDtd, &parser->m_mem) + || !setContext(parser, context)) { + XML_ParserFree(parser); + return NULL; + } + processor = externalEntityInitProcessor; +#ifdef XML_DTD + } + else { + /* The DTD instance referenced by _dtd is shared between the document's + root parser and external PE parsers, therefore one does not need to + call setContext. In addition, one also *must* not call setContext, + because this would overwrite existing prefix->binding pointers in + _dtd with ones that get destroyed with the external PE parser. + This would leave those prefixes with dangling pointers. + */ + isParamEntity = XML_TRUE; + XmlPrologStateInitExternalEntity(&prologState); + processor = externalParEntInitProcessor; + } +#endif /* XML_DTD */ + return parser; +} + +static void FASTCALL +destroyBindings(BINDING *bindings, XML_Parser parser) +{ + for (;;) { + BINDING *b = bindings; + if (!b) + break; + bindings = b->nextTagBinding; + FREE(b->uri); + FREE(b); + } +} + +void XMLCALL +XML_ParserFree(XML_Parser parser) +{ + TAG *tagList; + OPEN_INTERNAL_ENTITY *entityList; + if (parser == NULL) + return; + /* free tagStack and freeTagList */ + tagList = tagStack; + for (;;) { + TAG *p; + if (tagList == NULL) { + if (freeTagList == NULL) + break; + tagList = freeTagList; + freeTagList = NULL; + } + p = tagList; + tagList = tagList->parent; + FREE(p->buf); + destroyBindings(p->bindings, parser); + FREE(p); + } + /* free openInternalEntities and freeInternalEntities */ + entityList = openInternalEntities; + for (;;) { + OPEN_INTERNAL_ENTITY *openEntity; + if (entityList == NULL) { + if (freeInternalEntities == NULL) + break; + entityList = freeInternalEntities; + freeInternalEntities = NULL; + } + openEntity = entityList; + entityList = entityList->next; + FREE(openEntity); + } + + destroyBindings(freeBindingList, parser); + destroyBindings(inheritedBindings, parser); + poolDestroy(&tempPool); + poolDestroy(&temp2Pool); +#ifdef XML_DTD + /* external parameter entity parsers share the DTD structure + parser->m_dtd with the root parser, so we must not destroy it + */ + if (!isParamEntity && _dtd) +#else + if (_dtd) +#endif /* XML_DTD */ + dtdDestroy(_dtd, (XML_Bool)!parentParser, &parser->m_mem); + FREE((void *)atts); +#ifdef XML_ATTR_INFO + FREE((void *)attInfo); +#endif + FREE(groupConnector); + FREE(buffer); + FREE(dataBuf); + FREE(nsAtts); + FREE(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + FREE(parser); +} + +void XMLCALL +XML_UseParserAsHandlerArg(XML_Parser parser) +{ + handlerArg = parser; +} + +enum XML_Error XMLCALL +XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) +{ +#ifdef XML_DTD + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING; + useForeignDTD = useDTD; + return XML_ERROR_NONE; +#else + return XML_ERROR_FEATURE_REQUIRES_XML_DTD; +#endif +} + +void XMLCALL +XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return; + ns_triplets = do_nst ? XML_TRUE : XML_FALSE; +} + +void XMLCALL +XML_SetUserData(XML_Parser parser, void *p) +{ + if (handlerArg == userData) + handlerArg = userData = p; + else + userData = p; +} + +enum XML_Status XMLCALL +XML_SetBase(XML_Parser parser, const XML_Char *p) +{ + if (p) { + p = poolCopyString(&_dtd->pool, p); + if (!p) + return XML_STATUS_ERROR; + curBase = p; + } + else + curBase = NULL; + return XML_STATUS_OK; +} + +const XML_Char * XMLCALL +XML_GetBase(XML_Parser parser) +{ + return curBase; +} + +int XMLCALL +XML_GetSpecifiedAttributeCount(XML_Parser parser) +{ + return nSpecifiedAtts; +} + +int XMLCALL +XML_GetIdAttributeIndex(XML_Parser parser) +{ + return idAttIndex; +} + +#ifdef XML_ATTR_INFO +const XML_AttrInfo * XMLCALL +XML_GetAttributeInfo(XML_Parser parser) +{ + return attInfo; +} +#endif + +void XMLCALL +XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end) +{ + startElementHandler = start; + endElementHandler = end; +} + +void XMLCALL +XML_SetStartElementHandler(XML_Parser parser, + XML_StartElementHandler start) { + startElementHandler = start; +} + +void XMLCALL +XML_SetEndElementHandler(XML_Parser parser, + XML_EndElementHandler end) { + endElementHandler = end; +} + +void XMLCALL +XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler) +{ + characterDataHandler = handler; +} + +void XMLCALL +XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler) +{ + processingInstructionHandler = handler; +} + +void XMLCALL +XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler) +{ + commentHandler = handler; +} + +void XMLCALL +XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end) +{ + startCdataSectionHandler = start; + endCdataSectionHandler = end; +} + +void XMLCALL +XML_SetStartCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start) { + startCdataSectionHandler = start; +} + +void XMLCALL +XML_SetEndCdataSectionHandler(XML_Parser parser, + XML_EndCdataSectionHandler end) { + endCdataSectionHandler = end; +} + +void XMLCALL +XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = XML_FALSE; +} + +void XMLCALL +XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = XML_TRUE; +} + +void XMLCALL +XML_SetDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end) +{ + startDoctypeDeclHandler = start; + endDoctypeDeclHandler = end; +} + +void XMLCALL +XML_SetStartDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start) { + startDoctypeDeclHandler = start; +} + +void XMLCALL +XML_SetEndDoctypeDeclHandler(XML_Parser parser, + XML_EndDoctypeDeclHandler end) { + endDoctypeDeclHandler = end; +} + +void XMLCALL +XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler) +{ + unparsedEntityDeclHandler = handler; +} + +void XMLCALL +XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler) +{ + notationDeclHandler = handler; +} + +void XMLCALL +XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end) +{ + startNamespaceDeclHandler = start; + endNamespaceDeclHandler = end; +} + +void XMLCALL +XML_SetStartNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start) { + startNamespaceDeclHandler = start; +} + +void XMLCALL +XML_SetEndNamespaceDeclHandler(XML_Parser parser, + XML_EndNamespaceDeclHandler end) { + endNamespaceDeclHandler = end; +} + +void XMLCALL +XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler) +{ + notStandaloneHandler = handler; +} + +void XMLCALL +XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler) +{ + externalEntityRefHandler = handler; +} + +void XMLCALL +XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg) +{ + if (arg) + externalEntityRefHandlerArg = (XML_Parser)arg; + else + externalEntityRefHandlerArg = parser; +} + +void XMLCALL +XML_SetSkippedEntityHandler(XML_Parser parser, + XML_SkippedEntityHandler handler) +{ + skippedEntityHandler = handler; +} + +void XMLCALL +XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *data) +{ + unknownEncodingHandler = handler; + unknownEncodingHandlerData = data; +} + +void XMLCALL +XML_SetElementDeclHandler(XML_Parser parser, + XML_ElementDeclHandler eldecl) +{ + elementDeclHandler = eldecl; +} + +void XMLCALL +XML_SetAttlistDeclHandler(XML_Parser parser, + XML_AttlistDeclHandler attdecl) +{ + attlistDeclHandler = attdecl; +} + +void XMLCALL +XML_SetEntityDeclHandler(XML_Parser parser, + XML_EntityDeclHandler handler) +{ + entityDeclHandler = handler; +} + +void XMLCALL +XML_SetXmlDeclHandler(XML_Parser parser, + XML_XmlDeclHandler handler) { + xmlDeclHandler = handler; +} + +int XMLCALL +XML_SetParamEntityParsing(XML_Parser parser, + enum XML_ParamEntityParsing peParsing) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return 0; +#ifdef XML_DTD + paramEntityParsing = peParsing; + return 1; +#else + return peParsing == XML_PARAM_ENTITY_PARSING_NEVER; +#endif +} + +int XMLCALL +XML_SetHashSalt(XML_Parser parser, + unsigned long hash_salt) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return 0; + hash_secret_salt = hash_salt; + return 1; +} + +enum XML_Status XMLCALL +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + case XML_INITIALIZED: + if (parentParser == NULL && !startParsing(parser)) { + errorCode = XML_ERROR_NO_MEMORY; + return XML_STATUS_ERROR; + } + default: + ps_parsing = XML_PARSING; + } + + if (len == 0) { + ps_finalBuffer = (XML_Bool)isFinal; + if (!isFinal) + return XML_STATUS_OK; + positionPtr = bufferPtr; + parseEndPtr = bufferEnd; + + /* If data are left over from last buffer, and we now know that these + data are the final chunk of input, then we have to check them again + to detect errors based on that fact. + */ + errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); + + if (errorCode == XML_ERROR_NONE) { + switch (ps_parsing) { + case XML_SUSPENDED: + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return XML_STATUS_SUSPENDED; + case XML_INITIALIZED: + case XML_PARSING: + ps_parsing = XML_FINISHED; + /* fall through */ + default: + return XML_STATUS_OK; + } + } + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } +#ifndef XML_CONTEXT_BYTES + else if (bufferPtr == bufferEnd) { + const char *end; + int nLeftOver; + enum XML_Status result; + parseEndByteIndex += len; + positionPtr = s; + ps_finalBuffer = (XML_Bool)isFinal; + + errorCode = processor(parser, s, parseEndPtr = s + len, &end); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (isFinal) { + ps_parsing = XML_FINISHED; + return XML_STATUS_OK; + } + /* fall through */ + default: + result = XML_STATUS_OK; + } + } + + XmlUpdatePosition(encoding, positionPtr, end, &position); + nLeftOver = s + len - end; + if (nLeftOver) { + if (buffer == NULL || nLeftOver > bufferLim - buffer) { + /* FIXME avoid integer overflow */ + char *temp; + temp = (buffer == NULL + ? (char *)MALLOC(len * 2) + : (char *)REALLOC(buffer, len * 2)); + if (temp == NULL) { + errorCode = XML_ERROR_NO_MEMORY; + eventPtr = eventEndPtr = NULL; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + buffer = temp; + bufferLim = buffer + len * 2; + } + memcpy(buffer, end, nLeftOver); + } + bufferPtr = buffer; + bufferEnd = buffer + nLeftOver; + positionPtr = bufferPtr; + parseEndPtr = bufferEnd; + eventPtr = bufferPtr; + eventEndPtr = bufferPtr; + return result; + } +#endif /* not defined XML_CONTEXT_BYTES */ + else { + void *buff = XML_GetBuffer(parser, len); + if (buff == NULL) + return XML_STATUS_ERROR; + else { + memcpy(buff, s, len); + return XML_ParseBuffer(parser, len, isFinal); + } + } +} + +enum XML_Status XMLCALL +XML_ParseBuffer(XML_Parser parser, int len, int isFinal) +{ + const char *start; + enum XML_Status result = XML_STATUS_OK; + + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + case XML_INITIALIZED: + if (parentParser == NULL && !startParsing(parser)) { + errorCode = XML_ERROR_NO_MEMORY; + return XML_STATUS_ERROR; + } + default: + ps_parsing = XML_PARSING; + } + + start = bufferPtr; + positionPtr = start; + bufferEnd += len; + parseEndPtr = bufferEnd; + parseEndByteIndex += len; + ps_finalBuffer = (XML_Bool)isFinal; + + errorCode = processor(parser, start, parseEndPtr, &bufferPtr); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (isFinal) { + ps_parsing = XML_FINISHED; + return result; + } + default: ; /* should not happen */ + } + } + + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return result; +} + +void * XMLCALL +XML_GetBuffer(XML_Parser parser, int len) +{ + if (len < 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return NULL; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return NULL; + default: ; + } + + if (len > bufferLim - bufferEnd) { +#ifdef XML_CONTEXT_BYTES + int keep; +#endif /* defined XML_CONTEXT_BYTES */ + /* Do not invoke signed arithmetic overflow: */ + int neededSize = (int) ((unsigned)len + (unsigned)(bufferEnd - bufferPtr)); + if (neededSize < 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } +#ifdef XML_CONTEXT_BYTES + keep = (int)(bufferPtr - buffer); + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; + neededSize += keep; +#endif /* defined XML_CONTEXT_BYTES */ + if (neededSize <= bufferLim - buffer) { +#ifdef XML_CONTEXT_BYTES + if (keep < bufferPtr - buffer) { + int offset = (int)(bufferPtr - buffer) - keep; + memmove(buffer, &buffer[offset], bufferEnd - bufferPtr + keep); + bufferEnd -= offset; + bufferPtr -= offset; + } +#else + memmove(buffer, bufferPtr, bufferEnd - bufferPtr); + bufferEnd = buffer + (bufferEnd - bufferPtr); + bufferPtr = buffer; +#endif /* not defined XML_CONTEXT_BYTES */ + } + else { + char *newBuf; + int bufferSize = (int)(bufferLim - bufferPtr); + if (bufferSize == 0) + bufferSize = INIT_BUFFER_SIZE; + do { + /* Do not invoke signed arithmetic overflow: */ + bufferSize = (int) (2U * (unsigned) bufferSize); + } while (bufferSize < neededSize && bufferSize > 0); + if (bufferSize <= 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } + newBuf = (char *)MALLOC(bufferSize); + if (newBuf == 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } + bufferLim = newBuf + bufferSize; +#ifdef XML_CONTEXT_BYTES + if (bufferPtr) { + int keep = (int)(bufferPtr - buffer); + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; + memcpy(newBuf, &bufferPtr[-keep], bufferEnd - bufferPtr + keep); + FREE(buffer); + buffer = newBuf; + bufferEnd = buffer + (bufferEnd - bufferPtr) + keep; + bufferPtr = buffer + keep; + } + else { + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; + } +#else + if (bufferPtr) { + memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr); + FREE(buffer); + } + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; +#endif /* not defined XML_CONTEXT_BYTES */ + } + eventPtr = eventEndPtr = NULL; + positionPtr = NULL; + } + return bufferEnd; +} + +enum XML_Status XMLCALL +XML_StopParser(XML_Parser parser, XML_Bool resumable) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + if (resumable) { + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + } + ps_parsing = XML_FINISHED; + break; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + default: + if (resumable) { +#ifdef XML_DTD + if (isParamEntity) { + errorCode = XML_ERROR_SUSPEND_PE; + return XML_STATUS_ERROR; + } +#endif + ps_parsing = XML_SUSPENDED; + } + else + ps_parsing = XML_FINISHED; + } + return XML_STATUS_OK; +} + +enum XML_Status XMLCALL +XML_ResumeParser(XML_Parser parser) +{ + enum XML_Status result = XML_STATUS_OK; + + if (ps_parsing != XML_SUSPENDED) { + errorCode = XML_ERROR_NOT_SUSPENDED; + return XML_STATUS_ERROR; + } + ps_parsing = XML_PARSING; + + errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (ps_finalBuffer) { + ps_parsing = XML_FINISHED; + return result; + } + default: ; + } + } + + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return result; +} + +void XMLCALL +XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status) +{ + assert(status != NULL); + *status = parser->m_parsingStatus; +} + +enum XML_Error XMLCALL +XML_GetErrorCode(XML_Parser parser) +{ + return errorCode; +} + +XML_Index XMLCALL +XML_GetCurrentByteIndex(XML_Parser parser) +{ + if (eventPtr) + return (XML_Index)(parseEndByteIndex - (parseEndPtr - eventPtr)); + return -1; +} + +int XMLCALL +XML_GetCurrentByteCount(XML_Parser parser) +{ + if (eventEndPtr && eventPtr) + return (int)(eventEndPtr - eventPtr); + return 0; +} + +const char * XMLCALL +XML_GetInputContext(XML_Parser parser, int *offset, int *size) +{ +#ifdef XML_CONTEXT_BYTES + if (eventPtr && buffer) { + *offset = (int)(eventPtr - buffer); + *size = (int)(bufferEnd - buffer); + return buffer; + } +#endif /* defined XML_CONTEXT_BYTES */ + return (char *) 0; +} + +XML_Size XMLCALL +XML_GetCurrentLineNumber(XML_Parser parser) +{ + if (eventPtr && eventPtr >= positionPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.lineNumber + 1; +} + +XML_Size XMLCALL +XML_GetCurrentColumnNumber(XML_Parser parser) +{ + if (eventPtr && eventPtr >= positionPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.columnNumber; +} + +void XMLCALL +XML_FreeContentModel(XML_Parser parser, XML_Content *model) +{ + FREE(model); +} + +void * XMLCALL +XML_MemMalloc(XML_Parser parser, size_t size) +{ + return MALLOC(size); +} + +void * XMLCALL +XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) +{ + return REALLOC(ptr, size); +} + +void XMLCALL +XML_MemFree(XML_Parser parser, void *ptr) +{ + FREE(ptr); +} + +void XMLCALL +XML_DefaultCurrent(XML_Parser parser) +{ + if (defaultHandler) { + if (openInternalEntities) + reportDefault(parser, + internalEncoding, + openInternalEntities->internalEventPtr, + openInternalEntities->internalEventEndPtr); + else + reportDefault(parser, encoding, eventPtr, eventEndPtr); + } +} + +const XML_LChar * XMLCALL +XML_ErrorString(enum XML_Error code) +{ + static const XML_LChar* const message[] = { + 0, + XML_L("out of memory"), + XML_L("syntax error"), + XML_L("no element found"), + XML_L("not well-formed (invalid token)"), + XML_L("unclosed token"), + XML_L("partial character"), + XML_L("mismatched tag"), + XML_L("duplicate attribute"), + XML_L("junk after document element"), + XML_L("illegal parameter entity reference"), + XML_L("undefined entity"), + XML_L("recursive entity reference"), + XML_L("asynchronous entity"), + XML_L("reference to invalid character number"), + XML_L("reference to binary entity"), + XML_L("reference to external entity in attribute"), + XML_L("XML or text declaration not at start of entity"), + XML_L("unknown encoding"), + XML_L("encoding specified in XML declaration is incorrect"), + XML_L("unclosed CDATA section"), + XML_L("error in processing external entity reference"), + XML_L("document is not standalone"), + XML_L("unexpected parser state - please send a bug report"), + XML_L("entity declared in parameter entity"), + XML_L("requested feature requires XML_DTD support in Expat"), + XML_L("cannot change setting once parsing has begun"), + XML_L("unbound prefix"), + XML_L("must not undeclare prefix"), + XML_L("incomplete markup in parameter entity"), + XML_L("XML declaration not well-formed"), + XML_L("text declaration not well-formed"), + XML_L("illegal character(s) in public id"), + XML_L("parser suspended"), + XML_L("parser not suspended"), + XML_L("parsing aborted"), + XML_L("parsing finished"), + XML_L("cannot suspend in external parameter entity"), + XML_L("reserved prefix (xml) must not be undeclared or bound to another namespace name"), + XML_L("reserved prefix (xmlns) must not be declared or undeclared"), + XML_L("prefix must not be bound to one of the reserved namespace names") + }; + if (code > 0 && code < sizeof(message)/sizeof(message[0])) + return message[code]; + return NULL; +} + +const XML_LChar * XMLCALL +XML_ExpatVersion(void) { + + /* V1 is used to string-ize the version number. However, it would + string-ize the actual version macro *names* unless we get them + substituted before being passed to V1. CPP is defined to expand + a macro, then rescan for more expansions. Thus, we use V2 to expand + the version macros, then CPP will expand the resulting V1() macro + with the correct numerals. */ + /* ### I'm assuming cpp is portable in this respect... */ + +#define V1(a,b,c) XML_L(#a)XML_L(".")XML_L(#b)XML_L(".")XML_L(#c) +#define V2(a,b,c) XML_L("expat_")V1(a,b,c) + + return V2(XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION); + +#undef V1 +#undef V2 +} + +XML_Expat_Version XMLCALL +XML_ExpatVersionInfo(void) +{ + XML_Expat_Version version; + + version.major = XML_MAJOR_VERSION; + version.minor = XML_MINOR_VERSION; + version.micro = XML_MICRO_VERSION; + + return version; +} + +const XML_Feature * XMLCALL +XML_GetFeatureList(void) +{ + static const XML_Feature features[] = { + {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"), + sizeof(XML_Char)}, + {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"), + sizeof(XML_LChar)}, +#ifdef XML_UNICODE + {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0}, +#endif +#ifdef XML_UNICODE_WCHAR_T + {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0}, +#endif +#ifdef XML_DTD + {XML_FEATURE_DTD, XML_L("XML_DTD"), 0}, +#endif +#ifdef XML_CONTEXT_BYTES + {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"), + XML_CONTEXT_BYTES}, +#endif +#ifdef XML_MIN_SIZE + {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0}, +#endif +#ifdef XML_NS + {XML_FEATURE_NS, XML_L("XML_NS"), 0}, +#endif +#ifdef XML_LARGE_SIZE + {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0}, +#endif +#ifdef XML_ATTR_INFO + {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0}, +#endif + {XML_FEATURE_END, NULL, 0} + }; + + return features; +} + +/* Initially tag->rawName always points into the parse buffer; + for those TAG instances opened while the current parse buffer was + processed, and not yet closed, we need to store tag->rawName in a more + permanent location, since the parse buffer is about to be discarded. +*/ +static XML_Bool +storeRawNames(XML_Parser parser) +{ + TAG *tag = tagStack; + while (tag) { + int bufSize; + int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1); + char *rawNameBuf = tag->buf + nameLen; + /* Stop if already stored. Since tagStack is a stack, we can stop + at the first entry that has already been copied; everything + below it in the stack is already been accounted for in a + previous call to this function. + */ + if (tag->rawName == rawNameBuf) + break; + /* For re-use purposes we need to ensure that the + size of tag->buf is a multiple of sizeof(XML_Char). + */ + bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)); + if (bufSize > tag->bufEnd - tag->buf) { + char *temp = (char *)REALLOC(tag->buf, bufSize); + if (temp == NULL) + return XML_FALSE; + /* if tag->name.str points to tag->buf (only when namespace + processing is off) then we have to update it + */ + if (tag->name.str == (XML_Char *)tag->buf) + tag->name.str = (XML_Char *)temp; + /* if tag->name.localPart is set (when namespace processing is on) + then update it as well, since it will always point into tag->buf + */ + if (tag->name.localPart) + tag->name.localPart = (XML_Char *)temp + (tag->name.localPart - + (XML_Char *)tag->buf); + tag->buf = temp; + tag->bufEnd = temp + bufSize; + rawNameBuf = temp + nameLen; + } + memcpy(rawNameBuf, tag->rawName, tag->rawNameLength); + tag->rawName = rawNameBuf; + tag = tag->parent; + } + return XML_TRUE; +} + +static enum XML_Error PTRCALL +contentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doContent(parser, 0, encoding, start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result == XML_ERROR_NONE) { + if (!storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; + } + return result; +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = externalEntityInitProcessor2; + return externalEntityInitProcessor2(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor2(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + const char *next = start; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(encoding, start, end, &next); + switch (tok) { + case XML_TOK_BOM: + /* If we are at the end of the buffer, this would cause the next stage, + i.e. externalEntityInitProcessor3, to pass control directly to + doContent (by detecting XML_TOK_NONE) without processing any xml text + declaration - causing the error XML_ERROR_MISPLACED_XML_PI in doContent. + */ + if (next == end && !ps_finalBuffer) { + *endPtr = next; + return XML_ERROR_NONE; + } + start = next; + break; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityInitProcessor3; + return externalEntityInitProcessor3(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor3(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + int tok; + const char *next = start; /* XmlContentTok doesn't always set the last arg */ + eventPtr = start; + tok = XmlContentTok(encoding, start, end, &next); + eventEndPtr = next; + + switch (tok) { + case XML_TOK_XML_DECL: + { + enum XML_Error result; + result = processXmlDecl(parser, 1, start, next); + if (result != XML_ERROR_NONE) + return result; + switch (ps_parsing) { + case XML_SUSPENDED: + *endPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + start = next; + } + } + break; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityContentProcessor; + tagLevel = 1; + return externalEntityContentProcessor(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityContentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doContent(parser, 1, encoding, start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result == XML_ERROR_NONE) { + if (!storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; + } + return result; +} + +static enum XML_Error +doContent(XML_Parser parser, + int startTagLevel, + const ENCODING *enc, + const char *s, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + /* save one level of indirection */ + DTD * const dtd = _dtd; + + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + + for (;;) { + const char *next = s; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_TRAILING_CR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + *eventEndPP = end; + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + /* We are at the end of the final buffer, should we check for + XML_SUSPENDED, XML_FINISHED? + */ + if (startTagLevel == 0) + return XML_ERROR_NO_ELEMENTS; + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + *nextPtr = end; + return XML_ERROR_NONE; + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (startTagLevel > 0) { + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_NO_ELEMENTS; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (characterDataHandler) + characterDataHandler(handlerArg, &ch, 1); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + name = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); + poolDiscard(&dtd->pool); + /* First, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the skipped entity or default handler. + */ + if (!dtd->hasParamEntityRefs || dtd->standalone) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->notation) + return XML_ERROR_BINARY_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + if (!defaultExpandInternalEntities) { + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, entity->name, 0); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + result = processInternalEntity(parser, entity, XML_FALSE); + if (result != XML_ERROR_NONE) + return result; + } + else if (externalEntityRefHandler) { + const XML_Char *context; + entity->open = XML_TRUE; + context = getContext(parser); + entity->open = XML_FALSE; + if (!context) + return XML_ERROR_NO_MEMORY; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + context, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + poolDiscard(&tempPool); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + case XML_TOK_START_TAG_NO_ATTS: + /* fall through */ + case XML_TOK_START_TAG_WITH_ATTS: + { + TAG *tag; + enum XML_Error result; + XML_Char *toPtr; + if (freeTagList) { + tag = freeTagList; + freeTagList = freeTagList->parent; + } + else { + tag = (TAG *)MALLOC(sizeof(TAG)); + if (!tag) + return XML_ERROR_NO_MEMORY; + tag->buf = (char *)MALLOC(INIT_TAG_BUF_SIZE); + if (!tag->buf) { + FREE(tag); + return XML_ERROR_NO_MEMORY; + } + tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE; + } + tag->bindings = NULL; + tag->parent = tagStack; + tagStack = tag; + tag->name.localPart = NULL; + tag->name.prefix = NULL; + tag->rawName = s + enc->minBytesPerChar; + tag->rawNameLength = XmlNameLength(enc, tag->rawName); + ++tagLevel; + { + const char *rawNameEnd = tag->rawName + tag->rawNameLength; + const char *fromPtr = tag->rawName; + toPtr = (XML_Char *)tag->buf; + for (;;) { + int bufSize; + int convLen; + const enum XML_Convert_Result convert_res = XmlConvert(enc, + &fromPtr, rawNameEnd, + (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1); + convLen = (int)(toPtr - (XML_Char *)tag->buf); + if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) { + tag->name.strLen = convLen; + break; + } + bufSize = (int)(tag->bufEnd - tag->buf) << 1; + { + char *temp = (char *)REALLOC(tag->buf, bufSize); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + tag->buf = temp; + tag->bufEnd = temp + bufSize; + toPtr = (XML_Char *)temp + convLen; + } + } + } + tag->name.str = (XML_Char *)tag->buf; + *toPtr = XML_T('\0'); + result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings)); + if (result) + return result; + if (startElementHandler) + startElementHandler(handlerArg, tag->name.str, + (const XML_Char **)atts); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&tempPool); + break; + } + case XML_TOK_EMPTY_ELEMENT_NO_ATTS: + /* fall through */ + case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: + { + const char *rawName = s + enc->minBytesPerChar; + enum XML_Error result; + BINDING *bindings = NULL; + XML_Bool noElmHandlers = XML_TRUE; + TAG_NAME name; + name.str = poolStoreString(&tempPool, enc, rawName, + rawName + XmlNameLength(enc, rawName)); + if (!name.str) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + result = storeAtts(parser, enc, s, &name, &bindings); + if (result) + return result; + poolFinish(&tempPool); + if (startElementHandler) { + startElementHandler(handlerArg, name.str, (const XML_Char **)atts); + noElmHandlers = XML_FALSE; + } + if (endElementHandler) { + if (startElementHandler) + *eventPP = *eventEndPP; + endElementHandler(handlerArg, name.str); + noElmHandlers = XML_FALSE; + } + if (noElmHandlers && defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&tempPool); + while (bindings) { + BINDING *b = bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + break; + case XML_TOK_END_TAG: + if (tagLevel == startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + else { + int len; + const char *rawName; + TAG *tag = tagStack; + tagStack = tag->parent; + tag->parent = freeTagList; + freeTagList = tag; + rawName = s + enc->minBytesPerChar*2; + len = XmlNameLength(enc, rawName); + if (len != tag->rawNameLength + || memcmp(tag->rawName, rawName, len) != 0) { + *eventPP = rawName; + return XML_ERROR_TAG_MISMATCH; + } + --tagLevel; + if (endElementHandler) { + const XML_Char *localPart; + const XML_Char *prefix; + XML_Char *uri; + localPart = tag->name.localPart; + if (ns && localPart) { + /* localPart and prefix may have been overwritten in + tag->name.str, since this points to the binding->uri + buffer which gets re-used; so we have to add them again + */ + uri = (XML_Char *)tag->name.str + tag->name.uriLen; + /* don't need to check for space - already done in storeAtts() */ + while (*localPart) *uri++ = *localPart++; + prefix = (XML_Char *)tag->name.prefix; + if (ns_triplets && prefix) { + *uri++ = namespaceSeparator; + while (*prefix) *uri++ = *prefix++; + } + *uri = XML_T('\0'); + } + endElementHandler(handlerArg, tag->name.str); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + while (tag->bindings) { + BINDING *b = tag->bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + tag->bindings = tag->bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + } + break; + case XML_TOK_CHAR_REF: + { + int n = XmlCharRefNumber(enc, s); + if (n < 0) + return XML_ERROR_BAD_CHAR_REF; + if (characterDataHandler) { + XML_Char buf[XML_ENCODE_MAX]; + characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_CDATA_SECT_OPEN: + { + enum XML_Error result; + if (startCdataSectionHandler) + startCdataSectionHandler(handlerArg); +#if 0 + /* Suppose you doing a transformation on a document that involves + changing only the character data. You set up a defaultHandler + and a characterDataHandler. The defaultHandler simply copies + characters through. The characterDataHandler does the + transformation and writes the characters out escaping them as + necessary. This case will fail to work if we leave out the + following two lines (because & and < inside CDATA sections will + be incorrectly escaped). + + However, now we have a start/endCdataSectionHandler, so it seems + easier to let the user deal with this. + */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore); + if (result != XML_ERROR_NONE) + return result; + else if (!next) { + processor = cdataSectionProcessor; + return result; + } + } + break; + case XML_TOK_TRAILING_RSQB: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + characterDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)end - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + /* We are at the end of the final buffer, should we check for + XML_SUSPENDED, XML_FINISHED? + */ + if (startTagLevel == 0) { + *eventPP = end; + return XML_ERROR_NO_ELEMENTS; + } + if (tagLevel != startTagLevel) { + *eventPP = end; + return XML_ERROR_ASYNC_ENTITY; + } + *nextPtr = end; + return XML_ERROR_NONE; + case XML_TOK_DATA_CHARS: + { + XML_CharacterDataHandler charDataHandler = characterDataHandler; + if (charDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + const enum XML_Convert_Result convert_res = XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + charDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) + break; + *eventPP = s; + } + } + else + charDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)next - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + default: + if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + *eventPP = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } + /* not reached */ +} + +/* Precondition: all arguments must be non-NULL; + Purpose: + - normalize attributes + - check attributes for well-formedness + - generate namespace aware attribute names (URI, prefix) + - build list of attributes for startElementHandler + - default attributes + - process namespace declarations (check and report them) + - generate namespace aware element name (URI, prefix) +*/ +static enum XML_Error +storeAtts(XML_Parser parser, const ENCODING *enc, + const char *attStr, TAG_NAME *tagNamePtr, + BINDING **bindingsPtr) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + ELEMENT_TYPE *elementType; + int nDefaultAtts; + const XML_Char **appAtts; /* the attribute list for the application */ + int attIndex = 0; + int prefixLen; + int i; + int n; + XML_Char *uri; + int nPrefixes = 0; + BINDING *binding; + const XML_Char *localPart; + + /* lookup the element type name */ + elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, tagNamePtr->str,0); + if (!elementType) { + const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str); + if (!name) + return XML_ERROR_NO_MEMORY; + elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, name, + sizeof(ELEMENT_TYPE)); + if (!elementType) + return XML_ERROR_NO_MEMORY; + if (ns && !setElementTypePrefix(parser, elementType)) + return XML_ERROR_NO_MEMORY; + } + nDefaultAtts = elementType->nDefaultAtts; + + /* get the attributes from the tokenizer */ + n = XmlGetAttributes(enc, attStr, attsSize, atts); + if (n + nDefaultAtts > attsSize) { + int oldAttsSize = attsSize; + ATTRIBUTE *temp; +#ifdef XML_ATTR_INFO + XML_AttrInfo *temp2; +#endif + attsSize = n + nDefaultAtts + INIT_ATTS_SIZE; + temp = (ATTRIBUTE *)REALLOC((void *)atts, attsSize * sizeof(ATTRIBUTE)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + atts = temp; +#ifdef XML_ATTR_INFO + temp2 = (XML_AttrInfo *)REALLOC((void *)attInfo, attsSize * sizeof(XML_AttrInfo)); + if (temp2 == NULL) + return XML_ERROR_NO_MEMORY; + attInfo = temp2; +#endif + if (n > oldAttsSize) + XmlGetAttributes(enc, attStr, n, atts); + } + + appAtts = (const XML_Char **)atts; + for (i = 0; i < n; i++) { + ATTRIBUTE *currAtt = &atts[i]; +#ifdef XML_ATTR_INFO + XML_AttrInfo *currAttInfo = &attInfo[i]; +#endif + /* add the name and value to the attribute list */ + ATTRIBUTE_ID *attId = getAttributeId(parser, enc, currAtt->name, + currAtt->name + + XmlNameLength(enc, currAtt->name)); + if (!attId) + return XML_ERROR_NO_MEMORY; +#ifdef XML_ATTR_INFO + currAttInfo->nameStart = parseEndByteIndex - (parseEndPtr - currAtt->name); + currAttInfo->nameEnd = currAttInfo->nameStart + + XmlNameLength(enc, currAtt->name); + currAttInfo->valueStart = parseEndByteIndex - + (parseEndPtr - currAtt->valuePtr); + currAttInfo->valueEnd = parseEndByteIndex - (parseEndPtr - currAtt->valueEnd); +#endif + /* Detect duplicate attributes by their QNames. This does not work when + namespace processing is turned on and different prefixes for the same + namespace are used. For this case we have a check further down. + */ + if ((attId->name)[-1]) { + if (enc == encoding) + eventPtr = atts[i].name; + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + (attId->name)[-1] = 1; + appAtts[attIndex++] = attId->name; + if (!atts[i].normalized) { + enum XML_Error result; + XML_Bool isCdata = XML_TRUE; + + /* figure out whether declared as other than CDATA */ + if (attId->maybeTokenized) { + int j; + for (j = 0; j < nDefaultAtts; j++) { + if (attId == elementType->defaultAtts[j].id) { + isCdata = elementType->defaultAtts[j].isCdata; + break; + } + } + } + + /* normalize the attribute value */ + result = storeAttributeValue(parser, enc, isCdata, + atts[i].valuePtr, atts[i].valueEnd, + &tempPool); + if (result) + return result; + appAtts[attIndex] = poolStart(&tempPool); + poolFinish(&tempPool); + } + else { + /* the value did not need normalizing */ + appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr, + atts[i].valueEnd); + if (appAtts[attIndex] == 0) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + } + /* handle prefixed attribute names */ + if (attId->prefix) { + if (attId->xmlns) { + /* deal with namespace declarations here */ + enum XML_Error result = addBinding(parser, attId->prefix, attId, + appAtts[attIndex], bindingsPtr); + if (result) + return result; + --attIndex; + } + else { + /* deal with other prefixed names later */ + attIndex++; + nPrefixes++; + (attId->name)[-1] = 2; + } + } + else + attIndex++; + } + + /* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */ + nSpecifiedAtts = attIndex; + if (elementType->idAtt && (elementType->idAtt->name)[-1]) { + for (i = 0; i < attIndex; i += 2) + if (appAtts[i] == elementType->idAtt->name) { + idAttIndex = i; + break; + } + } + else + idAttIndex = -1; + + /* do attribute defaulting */ + for (i = 0; i < nDefaultAtts; i++) { + const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + i; + if (!(da->id->name)[-1] && da->value) { + if (da->id->prefix) { + if (da->id->xmlns) { + enum XML_Error result = addBinding(parser, da->id->prefix, da->id, + da->value, bindingsPtr); + if (result) + return result; + } + else { + (da->id->name)[-1] = 2; + nPrefixes++; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + else { + (da->id->name)[-1] = 1; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + } + appAtts[attIndex] = 0; + + /* expand prefixed attribute names, check for duplicates, + and clear flags that say whether attributes were specified */ + i = 0; + if (nPrefixes) { + int j; /* hash table index */ + unsigned long version = nsAttsVersion; + int nsAttsSize = (int)1 << nsAttsPower; + /* size of hash table must be at least 2 * (# of prefixed attributes) */ + if ((nPrefixes << 1) >> nsAttsPower) { /* true for nsAttsPower = 0 */ + NS_ATT *temp; + /* hash table size must also be a power of 2 and >= 8 */ + while (nPrefixes >> nsAttsPower++); + if (nsAttsPower < 3) + nsAttsPower = 3; + nsAttsSize = (int)1 << nsAttsPower; + temp = (NS_ATT *)REALLOC(nsAtts, nsAttsSize * sizeof(NS_ATT)); + if (!temp) + return XML_ERROR_NO_MEMORY; + nsAtts = temp; + version = 0; /* force re-initialization of nsAtts hash table */ + } + /* using a version flag saves us from initializing nsAtts every time */ + if (!version) { /* initialize version flags when version wraps around */ + version = INIT_ATTS_VERSION; + for (j = nsAttsSize; j != 0; ) + nsAtts[--j].version = version; + } + nsAttsVersion = --version; + + /* expand prefixed names and check for duplicates */ + for (; i < attIndex; i += 2) { + const XML_Char *s = appAtts[i]; + if (s[-1] == 2) { /* prefixed */ + ATTRIBUTE_ID *id; + const BINDING *b; + unsigned long uriHash = hash_secret_salt; + ((XML_Char *)s)[-1] = 0; /* clear flag */ + id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, s, 0); + if (!id || !id->prefix) + return XML_ERROR_NO_MEMORY; + b = id->prefix->binding; + if (!b) + return XML_ERROR_UNBOUND_PREFIX; + + /* as we expand the name we also calculate its hash value */ + for (j = 0; j < b->uriLen; j++) { + const XML_Char c = b->uri[j]; + if (!poolAppendChar(&tempPool, c)) + return XML_ERROR_NO_MEMORY; + uriHash = CHAR_HASH(uriHash, c); + } + while (*s++ != XML_T(ASCII_COLON)) + ; + do { /* copies null terminator */ + const XML_Char c = *s; + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + uriHash = CHAR_HASH(uriHash, c); + } while (*s++); + + { /* Check hash table for duplicate of expanded name (uriName). + Derived from code in lookup(parser, HASH_TABLE *table, ...). + */ + unsigned char step = 0; + unsigned long mask = nsAttsSize - 1; + j = uriHash & mask; /* index into hash table */ + while (nsAtts[j].version == version) { + /* for speed we compare stored hash values first */ + if (uriHash == nsAtts[j].hash) { + const XML_Char *s1 = poolStart(&tempPool); + const XML_Char *s2 = nsAtts[j].uriName; + /* s1 is null terminated, but not s2 */ + for (; *s1 == *s2 && *s1 != 0; s1++, s2++); + if (*s1 == 0) + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + if (!step) + step = PROBE_STEP(uriHash, mask, nsAttsPower); + j < step ? (j += nsAttsSize - step) : (j -= step); + } + } + + if (ns_triplets) { /* append namespace separator and prefix */ + tempPool.ptr[-1] = namespaceSeparator; + s = b->prefix->name; + do { + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + } while (*s++); + } + + /* store expanded name in attribute list */ + s = poolStart(&tempPool); + poolFinish(&tempPool); + appAtts[i] = s; + + /* fill empty slot with new version, uriName and hash value */ + nsAtts[j].version = version; + nsAtts[j].hash = uriHash; + nsAtts[j].uriName = s; + + if (!--nPrefixes) { + i += 2; + break; + } + } + else /* not prefixed */ + ((XML_Char *)s)[-1] = 0; /* clear flag */ + } + } + /* clear flags for the remaining attributes */ + for (; i < attIndex; i += 2) + ((XML_Char *)(appAtts[i]))[-1] = 0; + for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding) + binding->attId->name[-1] = 0; + + if (!ns) + return XML_ERROR_NONE; + + /* expand the element type name */ + if (elementType->prefix) { + binding = elementType->prefix->binding; + if (!binding) + return XML_ERROR_UNBOUND_PREFIX; + localPart = tagNamePtr->str; + while (*localPart++ != XML_T(ASCII_COLON)) + ; + } + else if (dtd->defaultPrefix.binding) { + binding = dtd->defaultPrefix.binding; + localPart = tagNamePtr->str; + } + else + return XML_ERROR_NONE; + prefixLen = 0; + if (ns_triplets && binding->prefix->name) { + for (; binding->prefix->name[prefixLen++];) + ; /* prefixLen includes null terminator */ + } + tagNamePtr->localPart = localPart; + tagNamePtr->uriLen = binding->uriLen; + tagNamePtr->prefix = binding->prefix->name; + tagNamePtr->prefixLen = prefixLen; + for (i = 0; localPart[i++];) + ; /* i includes null terminator */ + n = i + binding->uriLen + prefixLen; + if (n > binding->uriAlloc) { + TAG *p; + uri = (XML_Char *)MALLOC((n + EXPAND_SPARE) * sizeof(XML_Char)); + if (!uri) + return XML_ERROR_NO_MEMORY; + binding->uriAlloc = n + EXPAND_SPARE; + memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char)); + for (p = tagStack; p; p = p->parent) + if (p->name.str == binding->uri) + p->name.str = uri; + FREE(binding->uri); + binding->uri = uri; + } + /* if namespaceSeparator != '\0' then uri includes it already */ + uri = binding->uri + binding->uriLen; + memcpy(uri, localPart, i * sizeof(XML_Char)); + /* we always have a namespace separator between localPart and prefix */ + if (prefixLen) { + uri += i - 1; + *uri = namespaceSeparator; /* replace null terminator */ + memcpy(uri + 1, binding->prefix->name, prefixLen * sizeof(XML_Char)); + } + tagNamePtr->str = binding->uri; + return XML_ERROR_NONE; +} + +/* addBinding() overwrites the value of prefix->binding without checking. + Therefore one must keep track of the old value outside of addBinding(). +*/ +static enum XML_Error +addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + const XML_Char *uri, BINDING **bindingsPtr) +{ + static const XML_Char xmlNamespace[] = { + ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, + ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, + ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, + ASCII_SLASH, ASCII_1, ASCII_9, ASCII_9, ASCII_8, ASCII_SLASH, + ASCII_n, ASCII_a, ASCII_m, ASCII_e, ASCII_s, ASCII_p, ASCII_a, ASCII_c, + ASCII_e, '\0' + }; + static const int xmlLen = + (int)sizeof(xmlNamespace)/sizeof(XML_Char) - 1; + static const XML_Char xmlnsNamespace[] = { + ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, + ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, + ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_2, ASCII_0, ASCII_0, + ASCII_0, ASCII_SLASH, ASCII_x, ASCII_m, ASCII_l, ASCII_n, ASCII_s, + ASCII_SLASH, '\0' + }; + static const int xmlnsLen = + (int)sizeof(xmlnsNamespace)/sizeof(XML_Char) - 1; + + XML_Bool mustBeXML = XML_FALSE; + XML_Bool isXML = XML_TRUE; + XML_Bool isXMLNS = XML_TRUE; + + BINDING *b; + int len; + + /* empty URI is only valid for default namespace per XML NS 1.0 (not 1.1) */ + if (*uri == XML_T('\0') && prefix->name) + return XML_ERROR_UNDECLARING_PREFIX; + + if (prefix->name + && prefix->name[0] == XML_T(ASCII_x) + && prefix->name[1] == XML_T(ASCII_m) + && prefix->name[2] == XML_T(ASCII_l)) { + + /* Not allowed to bind xmlns */ + if (prefix->name[3] == XML_T(ASCII_n) + && prefix->name[4] == XML_T(ASCII_s) + && prefix->name[5] == XML_T('\0')) + return XML_ERROR_RESERVED_PREFIX_XMLNS; + + if (prefix->name[3] == XML_T('\0')) + mustBeXML = XML_TRUE; + } + + for (len = 0; uri[len]; len++) { + if (isXML && (len > xmlLen || uri[len] != xmlNamespace[len])) + isXML = XML_FALSE; + + if (!mustBeXML && isXMLNS + && (len > xmlnsLen || uri[len] != xmlnsNamespace[len])) + isXMLNS = XML_FALSE; + } + isXML = isXML && len == xmlLen; + isXMLNS = isXMLNS && len == xmlnsLen; + + if (mustBeXML != isXML) + return mustBeXML ? XML_ERROR_RESERVED_PREFIX_XML + : XML_ERROR_RESERVED_NAMESPACE_URI; + + if (isXMLNS) + return XML_ERROR_RESERVED_NAMESPACE_URI; + + if (namespaceSeparator) + len++; + if (freeBindingList) { + b = freeBindingList; + if (len > b->uriAlloc) { + XML_Char *temp = (XML_Char *)REALLOC(b->uri, + sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + b->uri = temp; + b->uriAlloc = len + EXPAND_SPARE; + } + freeBindingList = b->nextTagBinding; + } + else { + b = (BINDING *)MALLOC(sizeof(BINDING)); + if (!b) + return XML_ERROR_NO_MEMORY; + b->uri = (XML_Char *)MALLOC(sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (!b->uri) { + FREE(b); + return XML_ERROR_NO_MEMORY; + } + b->uriAlloc = len + EXPAND_SPARE; + } + b->uriLen = len; + memcpy(b->uri, uri, len * sizeof(XML_Char)); + if (namespaceSeparator) + b->uri[len - 1] = namespaceSeparator; + b->prefix = prefix; + b->attId = attId; + b->prevPrefixBinding = prefix->binding; + /* NULL binding when default namespace undeclared */ + if (*uri == XML_T('\0') && prefix == &_dtd->defaultPrefix) + prefix->binding = NULL; + else + prefix->binding = b; + b->nextTagBinding = *bindingsPtr; + *bindingsPtr = b; + /* if attId == NULL then we are not starting a namespace scope */ + if (attId && startNamespaceDeclHandler) + startNamespaceDeclHandler(handlerArg, prefix->name, + prefix->binding ? uri : 0); + return XML_ERROR_NONE; +} + +/* The idea here is to avoid using stack for each CDATA section when + the whole file is parsed with one call. +*/ +static enum XML_Error PTRCALL +cdataSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doCdataSection(parser, encoding, &start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result != XML_ERROR_NONE) + return result; + if (start) { + if (parentParser) { /* we are parsing an external entity */ + processor = externalEntityContentProcessor; + return externalEntityContentProcessor(parser, start, end, endPtr); + } + else { + processor = contentProcessor; + return contentProcessor(parser, start, end, endPtr); + } + } + return result; +} + +/* startPtr gets set to non-null if the section is closed, and to null if + the section is not yet closed. +*/ +static enum XML_Error +doCdataSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = NULL; + + for (;;) { + const char *next; + int tok = XmlCdataSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_CDATA_SECT_CLOSE: + if (endCdataSectionHandler) + endCdataSectionHandler(handlerArg); +#if 0 + /* see comment under XML_TOK_CDATA_SECT_OPEN */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + *nextPtr = next; + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + else + return XML_ERROR_NONE; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_DATA_CHARS: + { + XML_CharacterDataHandler charDataHandler = characterDataHandler; + if (charDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + const enum XML_Convert_Result convert_res = XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = next; + charDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) + break; + *eventPP = s; + } + } + else + charDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)next - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_CDATA_SECTION; + default: + *eventPP = next; + return XML_ERROR_UNEXPECTED_STATE; + } + + *eventPP = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } + /* not reached */ +} + +#ifdef XML_DTD + +/* The idea here is to avoid using stack for each IGNORE section when + the whole file is parsed with one call. +*/ +static enum XML_Error PTRCALL +ignoreSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doIgnoreSection(parser, encoding, &start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result != XML_ERROR_NONE) + return result; + if (start) { + processor = prologProcessor; + return prologProcessor(parser, start, end, endPtr); + } + return result; +} + +/* startPtr gets set to non-null is the section is closed, and to null + if the section is not yet closed. +*/ +static enum XML_Error +doIgnoreSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + const char *next; + int tok; + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = NULL; + tok = XmlIgnoreSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_IGNORE_SECT: + if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + *nextPtr = next; + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + else + return XML_ERROR_NONE; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */ + default: + *eventPP = next; + return XML_ERROR_UNEXPECTED_STATE; + } + /* not reached */ +} + +#endif /* XML_DTD */ + +static enum XML_Error +initializeEncoding(XML_Parser parser) +{ + const char *s; +#ifdef XML_UNICODE + char encodingBuf[128]; + if (!protocolEncodingName) + s = NULL; + else { + int i; + for (i = 0; protocolEncodingName[i]; i++) { + if (i == sizeof(encodingBuf) - 1 + || (protocolEncodingName[i] & ~0x7f) != 0) { + encodingBuf[0] = '\0'; + break; + } + encodingBuf[i] = (char)protocolEncodingName[i]; + } + encodingBuf[i] = '\0'; + s = encodingBuf; + } +#else + s = protocolEncodingName; +#endif + if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s)) + return XML_ERROR_NONE; + return handleUnknownEncoding(parser, protocolEncodingName); +} + +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next) +{ + const char *encodingName = NULL; + const XML_Char *storedEncName = NULL; + const ENCODING *newEncoding = NULL; + const char *version = NULL; + const char *versionend; + const XML_Char *storedversion = NULL; + int standalone = -1; + if (!(ns + ? XmlParseXmlDeclNS + : XmlParseXmlDecl)(isGeneralTextEntity, + encoding, + s, + next, + &eventPtr, + &version, + &versionend, + &encodingName, + &newEncoding, + &standalone)) { + if (isGeneralTextEntity) + return XML_ERROR_TEXT_DECL; + else + return XML_ERROR_XML_DECL; + } + if (!isGeneralTextEntity && standalone == 1) { + _dtd->standalone = XML_TRUE; +#ifdef XML_DTD + if (paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE) + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif /* XML_DTD */ + } + if (xmlDeclHandler) { + if (encodingName != NULL) { + storedEncName = poolStoreString(&temp2Pool, + encoding, + encodingName, + encodingName + + XmlNameLength(encoding, encodingName)); + if (!storedEncName) + return XML_ERROR_NO_MEMORY; + poolFinish(&temp2Pool); + } + if (version) { + storedversion = poolStoreString(&temp2Pool, + encoding, + version, + versionend - encoding->minBytesPerChar); + if (!storedversion) + return XML_ERROR_NO_MEMORY; + } + xmlDeclHandler(handlerArg, storedversion, storedEncName, standalone); + } + else if (defaultHandler) + reportDefault(parser, encoding, s, next); + if (protocolEncodingName == NULL) { + if (newEncoding) { + if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) { + eventPtr = encodingName; + return XML_ERROR_INCORRECT_ENCODING; + } + encoding = newEncoding; + } + else if (encodingName) { + enum XML_Error result; + if (!storedEncName) { + storedEncName = poolStoreString( + &temp2Pool, encoding, encodingName, + encodingName + XmlNameLength(encoding, encodingName)); + if (!storedEncName) + return XML_ERROR_NO_MEMORY; + } + result = handleUnknownEncoding(parser, storedEncName); + poolClear(&temp2Pool); + if (result == XML_ERROR_UNKNOWN_ENCODING) + eventPtr = encodingName; + return result; + } + } + + if (storedEncName || storedversion) + poolClear(&temp2Pool); + + return XML_ERROR_NONE; +} + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + if (unknownEncodingHandler) { + XML_Encoding info; + int i; + for (i = 0; i < 256; i++) + info.map[i] = -1; + info.convert = NULL; + info.data = NULL; + info.release = NULL; + if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName, + &info)) { + ENCODING *enc; + unknownEncodingMem = MALLOC(XmlSizeOfUnknownEncoding()); + if (!unknownEncodingMem) { + if (info.release) + info.release(info.data); + return XML_ERROR_NO_MEMORY; + } + enc = (ns + ? XmlInitUnknownEncodingNS + : XmlInitUnknownEncoding)(unknownEncodingMem, + info.map, + info.convert, + info.data); + if (enc) { + unknownEncodingData = info.data; + unknownEncodingRelease = info.release; + encoding = enc; + return XML_ERROR_NONE; + } + } + if (info.release != NULL) + info.release(info.data); + } + return XML_ERROR_UNKNOWN_ENCODING; +} + +static enum XML_Error PTRCALL +prologInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = prologProcessor; + return prologProcessor(parser, s, end, nextPtr); +} + +#ifdef XML_DTD + +static enum XML_Error PTRCALL +externalParEntInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + + /* we know now that XML_Parse(Buffer) has been called, + so we consider the external parameter entity read */ + _dtd->paramEntityRead = XML_TRUE; + + if (prologState.inEntityValue) { + processor = entityValueInitProcessor; + return entityValueInitProcessor(parser, s, end, nextPtr); + } + else { + processor = externalParEntProcessor; + return externalParEntProcessor(parser, s, end, nextPtr); + } +} + +static enum XML_Error PTRCALL +entityValueInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + int tok; + const char *start = s; + const char *next = start; + eventPtr = start; + + for (;;) { + tok = XmlPrologTok(encoding, start, end, &next); + eventEndPtr = next; + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + /* found end of entity value - can store it now */ + return storeEntityValue(parser, encoding, s, end); + } + else if (tok == XML_TOK_XML_DECL) { + enum XML_Error result; + result = processXmlDecl(parser, 0, start, next); + if (result != XML_ERROR_NONE) + return result; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + *nextPtr = next; + } + /* stop scanning for text declaration - we found one */ + processor = entityValueProcessor; + return entityValueProcessor(parser, next, end, nextPtr); + } + /* If we are at the end of the buffer, this would cause XmlPrologTok to + return XML_TOK_NONE on the next call, which would then cause the + function to exit with *nextPtr set to s - that is what we want for other + tokens, but not for the BOM - we would rather like to skip it; + then, when this routine is entered the next time, XmlPrologTok will + return XML_TOK_INVALID, since the BOM is still in the buffer + */ + else if (tok == XML_TOK_BOM && next == end && !ps_finalBuffer) { + *nextPtr = next; + return XML_ERROR_NONE; + } + start = next; + eventPtr = start; + } +} + +static enum XML_Error PTRCALL +externalParEntProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *next = s; + int tok; + + tok = XmlPrologTok(encoding, s, end, &next); + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + } + /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM. + However, when parsing an external subset, doProlog will not accept a BOM + as valid, and report a syntax error, so we have to skip the BOM + */ + else if (tok == XML_TOK_BOM) { + s = next; + tok = XmlPrologTok(encoding, s, end, &next); + } + + processor = prologProcessor; + return doProlog(parser, encoding, s, end, tok, next, + nextPtr, (XML_Bool)!ps_finalBuffer); +} + +static enum XML_Error PTRCALL +entityValueProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *start = s; + const char *next = s; + const ENCODING *enc = encoding; + int tok; + + for (;;) { + tok = XmlPrologTok(enc, start, end, &next); + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + /* found end of entity value - can store it now */ + return storeEntityValue(parser, enc, s, end); + } + start = next; + } +} + +#endif /* XML_DTD */ + +static enum XML_Error PTRCALL +prologProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *next = s; + int tok = XmlPrologTok(encoding, s, end, &next); + return doProlog(parser, encoding, s, end, tok, next, + nextPtr, (XML_Bool)!ps_finalBuffer); +} + +static enum XML_Error +doProlog(XML_Parser parser, + const ENCODING *enc, + const char *s, + const char *end, + int tok, + const char *next, + const char **nextPtr, + XML_Bool haveMore) +{ +#ifdef XML_DTD + static const XML_Char externalSubsetName[] = { ASCII_HASH , '\0' }; +#endif /* XML_DTD */ + static const XML_Char atypeCDATA[] = + { ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; + static const XML_Char atypeID[] = { ASCII_I, ASCII_D, '\0' }; + static const XML_Char atypeIDREF[] = + { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; + static const XML_Char atypeIDREFS[] = + { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; + static const XML_Char atypeENTITY[] = + { ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; + static const XML_Char atypeENTITIES[] = { ASCII_E, ASCII_N, + ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, '\0' }; + static const XML_Char atypeNMTOKEN[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; + static const XML_Char atypeNMTOKENS[] = { ASCII_N, ASCII_M, ASCII_T, + ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, '\0' }; + static const XML_Char notationPrefix[] = { ASCII_N, ASCII_O, ASCII_T, + ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, ASCII_LPAREN, '\0' }; + static const XML_Char enumValueSep[] = { ASCII_PIPE, '\0' }; + static const XML_Char enumValueStart[] = { ASCII_LPAREN, '\0' }; + + /* save one level of indirection */ + DTD * const dtd = _dtd; + + const char **eventPP; + const char **eventEndPP; + enum XML_Content_Quant quant; + + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + + for (;;) { + int role; + XML_Bool handleDefault = XML_TRUE; + *eventPP = s; + *eventEndPP = next; + if (tok <= 0) { + if (haveMore && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case -XML_TOK_PROLOG_S: + tok = -tok; + break; + case XML_TOK_NONE: +#ifdef XML_DTD + /* for internal PE NOT referenced between declarations */ + if (enc != encoding && !openInternalEntities->betweenDecl) { + *nextPtr = s; + return XML_ERROR_NONE; + } + /* WFC: PE Between Declarations - must check that PE contains + complete markup, not only for external PEs, but also for + internal PEs if the reference occurs between declarations. + */ + if (isParamEntity || enc != encoding) { + if (XmlTokenRole(&prologState, XML_TOK_NONE, end, end, enc) + == XML_ROLE_ERROR) + return XML_ERROR_INCOMPLETE_PE; + *nextPtr = s; + return XML_ERROR_NONE; + } +#endif /* XML_DTD */ + return XML_ERROR_NO_ELEMENTS; + default: + tok = -tok; + next = end; + break; + } + } + role = XmlTokenRole(&prologState, tok, s, next, enc); + switch (role) { + case XML_ROLE_XML_DECL: + { + enum XML_Error result = processXmlDecl(parser, 0, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_DOCTYPE_NAME: + if (startDoctypeDeclHandler) { + doctypeName = poolStoreString(&tempPool, enc, s, next); + if (!doctypeName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + doctypePubid = NULL; + handleDefault = XML_FALSE; + } + doctypeSysid = NULL; /* always initialize to NULL */ + break; + case XML_ROLE_DOCTYPE_INTERNAL_SUBSET: + if (startDoctypeDeclHandler) { + startDoctypeDeclHandler(handlerArg, doctypeName, doctypeSysid, + doctypePubid, 1); + doctypeName = NULL; + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + break; +#ifdef XML_DTD + case XML_ROLE_TEXT_DECL: + { + enum XML_Error result = processXmlDecl(parser, 1, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + handleDefault = XML_FALSE; + } + break; +#endif /* XML_DTD */ + case XML_ROLE_DOCTYPE_PUBLIC_ID: +#ifdef XML_DTD + useForeignDTD = XML_FALSE; + declEntity = (ENTITY *)lookup(parser, + &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; +#endif /* XML_DTD */ + dtd->hasParamEntityRefs = XML_TRUE; + if (startDoctypeDeclHandler) { + XML_Char *pubId; + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + pubId = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!pubId) + return XML_ERROR_NO_MEMORY; + normalizePublicId(pubId); + poolFinish(&tempPool); + doctypePubid = pubId; + handleDefault = XML_FALSE; + goto alreadyChecked; + } + /* fall through */ + case XML_ROLE_ENTITY_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + alreadyChecked: + if (dtd->keepProcessing && declEntity) { + XML_Char *tem = poolStoreString(&dtd->pool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declEntity->publicId = tem; + poolFinish(&dtd->pool); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_DOCTYPE_CLOSE: + if (doctypeName) { + startDoctypeDeclHandler(handlerArg, doctypeName, + doctypeSysid, doctypePubid, 0); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + /* doctypeSysid will be non-NULL in the case of a previous + XML_ROLE_DOCTYPE_SYSTEM_ID, even if startDoctypeDeclHandler + was not set, indicating an external subset + */ +#ifdef XML_DTD + if (doctypeSysid || useForeignDTD) { + XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; + dtd->hasParamEntityRefs = XML_TRUE; + if (paramEntityParsing && externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(parser, + &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!entity) + return XML_ERROR_NO_MEMORY; + if (useForeignDTD) + entity->base = curBase; + dtd->paramEntityRead = XML_FALSE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + if (dtd->paramEntityRead) { + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + } + /* if we didn't read the foreign DTD then this means that there + is no external subset and we must reset dtd->hasParamEntityRefs + */ + else if (!doctypeSysid) + dtd->hasParamEntityRefs = hadParamEntityRefs; + /* end of DTD - no need to update dtd->keepProcessing */ + } + useForeignDTD = XML_FALSE; + } +#endif /* XML_DTD */ + if (endDoctypeDeclHandler) { + endDoctypeDeclHandler(handlerArg); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_INSTANCE_START: +#ifdef XML_DTD + /* if there is no DOCTYPE declaration then now is the + last chance to read the foreign DTD + */ + if (useForeignDTD) { + XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; + dtd->hasParamEntityRefs = XML_TRUE; + if (paramEntityParsing && externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(parser, &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!entity) + return XML_ERROR_NO_MEMORY; + entity->base = curBase; + dtd->paramEntityRead = XML_FALSE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + if (dtd->paramEntityRead) { + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + } + /* if we didn't read the foreign DTD then this means that there + is no external subset and we must reset dtd->hasParamEntityRefs + */ + else + dtd->hasParamEntityRefs = hadParamEntityRefs; + /* end of DTD - no need to update dtd->keepProcessing */ + } + } +#endif /* XML_DTD */ + processor = contentProcessor; + return contentProcessor(parser, s, end, nextPtr); + case XML_ROLE_ATTLIST_ELEMENT_NAME: + declElementType = getElementType(parser, enc, s, next); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_NAME: + declAttributeId = getAttributeId(parser, enc, s, next); + if (!declAttributeId) + return XML_ERROR_NO_MEMORY; + declAttributeIsCdata = XML_FALSE; + declAttributeType = NULL; + declAttributeIsId = XML_FALSE; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_CDATA: + declAttributeIsCdata = XML_TRUE; + declAttributeType = atypeCDATA; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ID: + declAttributeIsId = XML_TRUE; + declAttributeType = atypeID; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_IDREF: + declAttributeType = atypeIDREF; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_IDREFS: + declAttributeType = atypeIDREFS; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ENTITY: + declAttributeType = atypeENTITY; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ENTITIES: + declAttributeType = atypeENTITIES; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN: + declAttributeType = atypeNMTOKEN; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS: + declAttributeType = atypeNMTOKENS; + checkAttListDeclHandler: + if (dtd->keepProcessing && attlistDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ATTRIBUTE_ENUM_VALUE: + case XML_ROLE_ATTRIBUTE_NOTATION_VALUE: + if (dtd->keepProcessing && attlistDeclHandler) { + const XML_Char *prefix; + if (declAttributeType) { + prefix = enumValueSep; + } + else { + prefix = (role == XML_ROLE_ATTRIBUTE_NOTATION_VALUE + ? notationPrefix + : enumValueStart); + } + if (!poolAppendString(&tempPool, prefix)) + return XML_ERROR_NO_MEMORY; + if (!poolAppend(&tempPool, enc, s, next)) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE: + case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE: + if (dtd->keepProcessing) { + if (!defineAttribute(declElementType, declAttributeId, + declAttributeIsCdata, declAttributeIsId, + 0, parser)) + return XML_ERROR_NO_MEMORY; + if (attlistDeclHandler && declAttributeType) { + if (*declAttributeType == XML_T(ASCII_LPAREN) + || (*declAttributeType == XML_T(ASCII_N) + && declAttributeType[1] == XML_T(ASCII_O))) { + /* Enumerated or Notation type */ + if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN)) + || !poolAppendChar(&tempPool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + poolFinish(&tempPool); + } + *eventEndPP = s; + attlistDeclHandler(handlerArg, declElementType->name, + declAttributeId->name, declAttributeType, + 0, role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE: + case XML_ROLE_FIXED_ATTRIBUTE_VALUE: + if (dtd->keepProcessing) { + const XML_Char *attVal; + enum XML_Error result = + storeAttributeValue(parser, enc, declAttributeIsCdata, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar, + &dtd->pool); + if (result) + return result; + attVal = poolStart(&dtd->pool); + poolFinish(&dtd->pool); + /* ID attributes aren't allowed to have a default */ + if (!defineAttribute(declElementType, declAttributeId, + declAttributeIsCdata, XML_FALSE, attVal, parser)) + return XML_ERROR_NO_MEMORY; + if (attlistDeclHandler && declAttributeType) { + if (*declAttributeType == XML_T(ASCII_LPAREN) + || (*declAttributeType == XML_T(ASCII_N) + && declAttributeType[1] == XML_T(ASCII_O))) { + /* Enumerated or Notation type */ + if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN)) + || !poolAppendChar(&tempPool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + poolFinish(&tempPool); + } + *eventEndPP = s; + attlistDeclHandler(handlerArg, declElementType->name, + declAttributeId->name, declAttributeType, + attVal, + role == XML_ROLE_FIXED_ATTRIBUTE_VALUE); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_ENTITY_VALUE: + if (dtd->keepProcessing) { + enum XML_Error result = storeEntityValue(parser, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (declEntity) { + declEntity->textPtr = poolStart(&dtd->entityValuePool); + declEntity->textLen = (int)(poolLength(&dtd->entityValuePool)); + poolFinish(&dtd->entityValuePool); + if (entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + declEntity->is_param, + declEntity->textPtr, + declEntity->textLen, + curBase, 0, 0, 0); + handleDefault = XML_FALSE; + } + } + else + poolDiscard(&dtd->entityValuePool); + if (result != XML_ERROR_NONE) + return result; + } + break; + case XML_ROLE_DOCTYPE_SYSTEM_ID: +#ifdef XML_DTD + useForeignDTD = XML_FALSE; +#endif /* XML_DTD */ + dtd->hasParamEntityRefs = XML_TRUE; + if (startDoctypeDeclHandler) { + doctypeSysid = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (doctypeSysid == NULL) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } +#ifdef XML_DTD + else + /* use externalSubsetName to make doctypeSysid non-NULL + for the case where no startDoctypeDeclHandler is set */ + doctypeSysid = externalSubsetName; +#endif /* XML_DTD */ + if (!dtd->standalone +#ifdef XML_DTD + && !paramEntityParsing +#endif /* XML_DTD */ + && notStandaloneHandler + && !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; +#ifndef XML_DTD + break; +#else /* XML_DTD */ + if (!declEntity) { + declEntity = (ENTITY *)lookup(parser, + &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + declEntity->publicId = NULL; + } + /* fall through */ +#endif /* XML_DTD */ + case XML_ROLE_ENTITY_SYSTEM_ID: + if (dtd->keepProcessing && declEntity) { + declEntity->systemId = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!declEntity->systemId) + return XML_ERROR_NO_MEMORY; + declEntity->base = curBase; + poolFinish(&dtd->pool); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_ENTITY_COMPLETE: + if (dtd->keepProcessing && declEntity && entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + declEntity->is_param, + 0,0, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + 0); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_ENTITY_NOTATION_NAME: + if (dtd->keepProcessing && declEntity) { + declEntity->notation = poolStoreString(&dtd->pool, enc, s, next); + if (!declEntity->notation) + return XML_ERROR_NO_MEMORY; + poolFinish(&dtd->pool); + if (unparsedEntityDeclHandler) { + *eventEndPP = s; + unparsedEntityDeclHandler(handlerArg, + declEntity->name, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + handleDefault = XML_FALSE; + } + else if (entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + 0,0,0, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_GENERAL_ENTITY_NAME: + { + if (XmlPredefinedEntityName(enc, s, next)) { + declEntity = NULL; + break; + } + if (dtd->keepProcessing) { + const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declEntity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + else { + poolFinish(&dtd->pool); + declEntity->publicId = NULL; + declEntity->is_param = XML_FALSE; + /* if we have a parent parser or are reading an internal parameter + entity, then the entity declaration is not considered "internal" + */ + declEntity->is_internal = !(parentParser || openInternalEntities); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + } + else { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + } + break; + case XML_ROLE_PARAM_ENTITY_NAME: +#ifdef XML_DTD + if (dtd->keepProcessing) { + const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declEntity = (ENTITY *)lookup(parser, &dtd->paramEntities, + name, sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + else { + poolFinish(&dtd->pool); + declEntity->publicId = NULL; + declEntity->is_param = XML_TRUE; + /* if we have a parent parser or are reading an internal parameter + entity, then the entity declaration is not considered "internal" + */ + declEntity->is_internal = !(parentParser || openInternalEntities); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + } + else { + poolDiscard(&dtd->pool); + declEntity = NULL; + } +#else /* not XML_DTD */ + declEntity = NULL; +#endif /* XML_DTD */ + break; + case XML_ROLE_NOTATION_NAME: + declNotationPublicId = NULL; + declNotationName = NULL; + if (notationDeclHandler) { + declNotationName = poolStoreString(&tempPool, enc, s, next); + if (!declNotationName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_NOTATION_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + if (declNotationName) { /* means notationDeclHandler != NULL */ + XML_Char *tem = poolStoreString(&tempPool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declNotationPublicId = tem; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_NOTATION_SYSTEM_ID: + if (declNotationName && notationDeclHandler) { + const XML_Char *systemId + = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!systemId) + return XML_ERROR_NO_MEMORY; + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + systemId, + declNotationPublicId); + handleDefault = XML_FALSE; + } + poolClear(&tempPool); + break; + case XML_ROLE_NOTATION_NO_SYSTEM_ID: + if (declNotationPublicId && notationDeclHandler) { + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + 0, + declNotationPublicId); + handleDefault = XML_FALSE; + } + poolClear(&tempPool); + break; + case XML_ROLE_ERROR: + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: + /* PE references in internal subset are + not allowed within declarations. */ + return XML_ERROR_PARAM_ENTITY_REF; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + default: + return XML_ERROR_SYNTAX; + } +#ifdef XML_DTD + case XML_ROLE_IGNORE_SECT: + { + enum XML_Error result; + if (defaultHandler) + reportDefault(parser, enc, s, next); + handleDefault = XML_FALSE; + result = doIgnoreSection(parser, enc, &next, end, nextPtr, haveMore); + if (result != XML_ERROR_NONE) + return result; + else if (!next) { + processor = ignoreSectionProcessor; + return result; + } + } + break; +#endif /* XML_DTD */ + case XML_ROLE_GROUP_OPEN: + if (prologState.level >= groupSize) { + if (groupSize) { + char *temp = (char *)REALLOC(groupConnector, groupSize *= 2); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + groupConnector = temp; + if (dtd->scaffIndex) { + int *temp = (int *)REALLOC(dtd->scaffIndex, + groupSize * sizeof(int)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + dtd->scaffIndex = temp; + } + } + else { + groupConnector = (char *)MALLOC(groupSize = 32); + if (!groupConnector) + return XML_ERROR_NO_MEMORY; + } + } + groupConnector[prologState.level] = 0; + if (dtd->in_eldecl) { + int myindex = nextScaffoldPart(parser); + if (myindex < 0) + return XML_ERROR_NO_MEMORY; + dtd->scaffIndex[dtd->scaffLevel] = myindex; + dtd->scaffLevel++; + dtd->scaffold[myindex].type = XML_CTYPE_SEQ; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_GROUP_SEQUENCE: + if (groupConnector[prologState.level] == ASCII_PIPE) + return XML_ERROR_SYNTAX; + groupConnector[prologState.level] = ASCII_COMMA; + if (dtd->in_eldecl && elementDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_GROUP_CHOICE: + if (groupConnector[prologState.level] == ASCII_COMMA) + return XML_ERROR_SYNTAX; + if (dtd->in_eldecl + && !groupConnector[prologState.level] + && (dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + != XML_CTYPE_MIXED) + ) { + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + = XML_CTYPE_CHOICE; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + groupConnector[prologState.level] = ASCII_PIPE; + break; + case XML_ROLE_PARAM_ENTITY_REF: +#ifdef XML_DTD + case XML_ROLE_INNER_PARAM_ENTITY_REF: + dtd->hasParamEntityRefs = XML_TRUE; + if (!paramEntityParsing) + dtd->keepProcessing = dtd->standalone; + else { + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); + poolDiscard(&dtd->pool); + /* first, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the skipped entity handler + */ + if (prologState.documentEntity && + (dtd->standalone + ? !openInternalEntities + : !dtd->hasParamEntityRefs)) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + dtd->keepProcessing = dtd->standalone; + /* cannot report skipped entities in declarations */ + if ((role == XML_ROLE_PARAM_ENTITY_REF) && skippedEntityHandler) { + skippedEntityHandler(handlerArg, name, 1); + handleDefault = XML_FALSE; + } + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + XML_Bool betweenDecl = + (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE); + result = processInternalEntity(parser, entity, betweenDecl); + if (result != XML_ERROR_NONE) + return result; + handleDefault = XML_FALSE; + break; + } + if (externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) { + entity->open = XML_FALSE; + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + } + entity->open = XML_FALSE; + handleDefault = XML_FALSE; + if (!dtd->paramEntityRead) { + dtd->keepProcessing = dtd->standalone; + break; + } + } + else { + dtd->keepProcessing = dtd->standalone; + break; + } + } +#endif /* XML_DTD */ + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + break; + + /* Element declaration stuff */ + + case XML_ROLE_ELEMENT_NAME: + if (elementDeclHandler) { + declElementType = getElementType(parser, enc, s, next); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + dtd->scaffLevel = 0; + dtd->scaffCount = 0; + dtd->in_eldecl = XML_TRUE; + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_ANY: + case XML_ROLE_CONTENT_EMPTY: + if (dtd->in_eldecl) { + if (elementDeclHandler) { + XML_Content * content = (XML_Content *) MALLOC(sizeof(XML_Content)); + if (!content) + return XML_ERROR_NO_MEMORY; + content->quant = XML_CQUANT_NONE; + content->name = NULL; + content->numchildren = 0; + content->children = NULL; + content->type = ((role == XML_ROLE_CONTENT_ANY) ? + XML_CTYPE_ANY : + XML_CTYPE_EMPTY); + *eventEndPP = s; + elementDeclHandler(handlerArg, declElementType->name, content); + handleDefault = XML_FALSE; + } + dtd->in_eldecl = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_PCDATA: + if (dtd->in_eldecl) { + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + = XML_CTYPE_MIXED; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_ELEMENT: + quant = XML_CQUANT_NONE; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_OPT: + quant = XML_CQUANT_OPT; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_REP: + quant = XML_CQUANT_REP; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_PLUS: + quant = XML_CQUANT_PLUS; + elementContent: + if (dtd->in_eldecl) { + ELEMENT_TYPE *el; + const XML_Char *name; + int nameLen; + const char *nxt = (quant == XML_CQUANT_NONE + ? next + : next - enc->minBytesPerChar); + int myindex = nextScaffoldPart(parser); + if (myindex < 0) + return XML_ERROR_NO_MEMORY; + dtd->scaffold[myindex].type = XML_CTYPE_NAME; + dtd->scaffold[myindex].quant = quant; + el = getElementType(parser, enc, s, nxt); + if (!el) + return XML_ERROR_NO_MEMORY; + name = el->name; + dtd->scaffold[myindex].name = name; + nameLen = 0; + for (; name[nameLen++]; ); + dtd->contentStringLen += nameLen; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_GROUP_CLOSE: + quant = XML_CQUANT_NONE; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_OPT: + quant = XML_CQUANT_OPT; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_REP: + quant = XML_CQUANT_REP; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_PLUS: + quant = XML_CQUANT_PLUS; + closeGroup: + if (dtd->in_eldecl) { + if (elementDeclHandler) + handleDefault = XML_FALSE; + dtd->scaffLevel--; + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel]].quant = quant; + if (dtd->scaffLevel == 0) { + if (!handleDefault) { + XML_Content *model = build_model(parser); + if (!model) + return XML_ERROR_NO_MEMORY; + *eventEndPP = s; + elementDeclHandler(handlerArg, declElementType->name, model); + } + dtd->in_eldecl = XML_FALSE; + dtd->contentStringLen = 0; + } + } + break; + /* End element declaration stuff */ + + case XML_ROLE_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + handleDefault = XML_FALSE; + break; + case XML_ROLE_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + handleDefault = XML_FALSE; + break; + case XML_ROLE_NONE: + switch (tok) { + case XML_TOK_BOM: + handleDefault = XML_FALSE; + break; + } + break; + case XML_ROLE_DOCTYPE_NONE: + if (startDoctypeDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ENTITY_NONE: + if (dtd->keepProcessing && entityDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_NOTATION_NONE: + if (notationDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ATTLIST_NONE: + if (dtd->keepProcessing && attlistDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ELEMENT_NONE: + if (elementDeclHandler) + handleDefault = XML_FALSE; + break; + } /* end of big switch */ + + if (handleDefault && defaultHandler) + reportDefault(parser, enc, s, next); + + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + s = next; + tok = XmlPrologTok(enc, s, end, &next); + } + } + /* not reached */ +} + +static enum XML_Error PTRCALL +epilogProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + processor = epilogProcessor; + eventPtr = s; + for (;;) { + const char *next = NULL; + int tok = XmlPrologTok(encoding, s, end, &next); + eventEndPtr = next; + switch (tok) { + /* report partial linebreak - it might be the last token */ + case -XML_TOK_PROLOG_S: + if (defaultHandler) { + reportDefault(parser, encoding, s, next); + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + } + *nextPtr = next; + return XML_ERROR_NONE; + case XML_TOK_NONE: + *nextPtr = s; + return XML_ERROR_NONE; + case XML_TOK_PROLOG_S: + if (defaultHandler) + reportDefault(parser, encoding, s, next); + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_INVALID: + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + default: + return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; + } + eventPtr = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } +} + +static enum XML_Error +processInternalEntity(XML_Parser parser, ENTITY *entity, + XML_Bool betweenDecl) +{ + const char *textStart, *textEnd; + const char *next; + enum XML_Error result; + OPEN_INTERNAL_ENTITY *openEntity; + + if (freeInternalEntities) { + openEntity = freeInternalEntities; + freeInternalEntities = openEntity->next; + } + else { + openEntity = (OPEN_INTERNAL_ENTITY *)MALLOC(sizeof(OPEN_INTERNAL_ENTITY)); + if (!openEntity) + return XML_ERROR_NO_MEMORY; + } + entity->open = XML_TRUE; + entity->processed = 0; + openEntity->next = openInternalEntities; + openInternalEntities = openEntity; + openEntity->entity = entity; + openEntity->startTagLevel = tagLevel; + openEntity->betweenDecl = betweenDecl; + openEntity->internalEventPtr = NULL; + openEntity->internalEventEndPtr = NULL; + textStart = (char *)entity->textPtr; + textEnd = (char *)(entity->textPtr + entity->textLen); + +#ifdef XML_DTD + if (entity->is_param) { + int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); + result = doProlog(parser, internalEncoding, textStart, textEnd, tok, + next, &next, XML_FALSE); + } + else +#endif /* XML_DTD */ + result = doContent(parser, tagLevel, internalEncoding, textStart, + textEnd, &next, XML_FALSE); + + if (result == XML_ERROR_NONE) { + if (textEnd != next && ps_parsing == XML_SUSPENDED) { + entity->processed = (int)(next - textStart); + processor = internalEntityProcessor; + } + else { + entity->open = XML_FALSE; + openInternalEntities = openEntity->next; + /* put openEntity back in list of free instances */ + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + } + return result; +} + +static enum XML_Error PTRCALL +internalEntityProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + ENTITY *entity; + const char *textStart, *textEnd; + const char *next; + enum XML_Error result; + OPEN_INTERNAL_ENTITY *openEntity = openInternalEntities; + if (!openEntity) + return XML_ERROR_UNEXPECTED_STATE; + + entity = openEntity->entity; + textStart = ((char *)entity->textPtr) + entity->processed; + textEnd = (char *)(entity->textPtr + entity->textLen); + +#ifdef XML_DTD + if (entity->is_param) { + int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); + result = doProlog(parser, internalEncoding, textStart, textEnd, tok, + next, &next, XML_FALSE); + } + else +#endif /* XML_DTD */ + result = doContent(parser, openEntity->startTagLevel, internalEncoding, + textStart, textEnd, &next, XML_FALSE); + + if (result != XML_ERROR_NONE) + return result; + else if (textEnd != next && ps_parsing == XML_SUSPENDED) { + entity->processed = (int)(next - (char *)entity->textPtr); + return result; + } + else { + entity->open = XML_FALSE; + openInternalEntities = openEntity->next; + /* put openEntity back in list of free instances */ + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + +#ifdef XML_DTD + if (entity->is_param) { + int tok; + processor = prologProcessor; + tok = XmlPrologTok(encoding, s, end, &next); + return doProlog(parser, encoding, s, end, tok, next, nextPtr, + (XML_Bool)!ps_finalBuffer); + } + else +#endif /* XML_DTD */ + { + processor = contentProcessor; + /* see externalEntityContentProcessor vs contentProcessor */ + return doContent(parser, parentParser ? 1 : 0, encoding, s, end, + nextPtr, (XML_Bool)!ps_finalBuffer); + } +} + +static enum XML_Error PTRCALL +errorProcessor(XML_Parser parser, + const char *UNUSED_P(s), + const char *UNUSED_P(end), + const char **UNUSED_P(nextPtr)) +{ + return errorCode; +} + +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, + end, pool); + if (result) + return result; + if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) + poolChop(pool); + if (!poolAppendChar(pool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + return XML_ERROR_NONE; +} + +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + for (;;) { + const char *next; + int tok = XmlAttributeValueTok(enc, ptr, end, &next); + switch (tok) { + case XML_TOK_NONE: + return XML_ERROR_NONE; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, ptr); + if (n < 0) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + if (!isCdata + && n == 0x20 /* space */ + && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + for (i = 0; i < n; i++) { + if (!poolAppendChar(pool, buf[i])) + return XML_ERROR_NO_MEMORY; + } + } + break; + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, ptr, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_TRAILING_CR: + next = ptr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_ATTRIBUTE_VALUE_S: + case XML_TOK_DATA_NEWLINE: + if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + if (!poolAppendChar(pool, 0x20)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + char checkEntityDecl; + XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (!poolAppendChar(pool, ch)) + return XML_ERROR_NO_MEMORY; + break; + } + name = poolStoreString(&temp2Pool, enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); + poolDiscard(&temp2Pool); + /* First, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal. + */ + if (pool == &dtd->pool) /* are we called from prolog? */ + checkEntityDecl = +#ifdef XML_DTD + prologState.documentEntity && +#endif /* XML_DTD */ + (dtd->standalone + ? !openInternalEntities + : !dtd->hasParamEntityRefs); + else /* if (pool == &tempPool): we are called from content */ + checkEntityDecl = !dtd->hasParamEntityRefs || dtd->standalone; + if (checkEntityDecl) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + /* Cannot report skipped entity here - see comments on + skippedEntityHandler. + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + */ + /* Cannot call the default handler because this would be + out of sync with the call to the startElementHandler. + if ((pool == &tempPool) && defaultHandler) + reportDefault(parser, enc, ptr, next); + */ + break; + } + if (entity->open) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_RECURSIVE_ENTITY_REF; + } + if (entity->notation) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BINARY_ENTITY_REF; + } + if (!entity->textPtr) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; + } + else { + enum XML_Error result; + const XML_Char *textEnd = entity->textPtr + entity->textLen; + entity->open = XML_TRUE; + result = appendAttributeValue(parser, internalEncoding, isCdata, + (char *)entity->textPtr, + (char *)textEnd, pool); + entity->open = XML_FALSE; + if (result) + return result; + } + } + break; + default: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_UNEXPECTED_STATE; + } + ptr = next; + } + /* not reached */ +} + +static enum XML_Error +storeEntityValue(XML_Parser parser, + const ENCODING *enc, + const char *entityTextPtr, + const char *entityTextEnd) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + STRING_POOL *pool = &(dtd->entityValuePool); + enum XML_Error result = XML_ERROR_NONE; +#ifdef XML_DTD + int oldInEntityValue = prologState.inEntityValue; + prologState.inEntityValue = 1; +#endif /* XML_DTD */ + /* never return Null for the value argument in EntityDeclHandler, + since this would indicate an external entity; therefore we + have to make sure that entityValuePool.start is not null */ + if (!pool->blocks) { + if (!poolGrow(pool)) + return XML_ERROR_NO_MEMORY; + } + + for (;;) { + const char *next; + int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: +#ifdef XML_DTD + if (isParamEntity || enc != encoding) { + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&tempPool, enc, + entityTextPtr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); + poolDiscard(&tempPool); + if (!entity) { + /* not a well-formedness error - see XML 1.0: WFC Entity Declared */ + /* cannot report skipped entity here - see comments on + skippedEntityHandler + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + */ + dtd->keepProcessing = dtd->standalone; + goto endEntityValue; + } + if (entity->open) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_RECURSIVE_ENTITY_REF; + goto endEntityValue; + } + if (entity->systemId) { + if (externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) { + entity->open = XML_FALSE; + result = XML_ERROR_EXTERNAL_ENTITY_HANDLING; + goto endEntityValue; + } + entity->open = XML_FALSE; + if (!dtd->paramEntityRead) + dtd->keepProcessing = dtd->standalone; + } + else + dtd->keepProcessing = dtd->standalone; + } + else { + entity->open = XML_TRUE; + result = storeEntityValue(parser, + internalEncoding, + (char *)entity->textPtr, + (char *)(entity->textPtr + + entity->textLen)); + entity->open = XML_FALSE; + if (result) + goto endEntityValue; + } + break; + } +#endif /* XML_DTD */ + /* In the internal subset, PE references are not legal + within markup declarations, e.g entity values in this case. */ + eventPtr = entityTextPtr; + result = XML_ERROR_PARAM_ENTITY_REF; + goto endEntityValue; + case XML_TOK_NONE: + result = XML_ERROR_NONE; + goto endEntityValue; + case XML_TOK_ENTITY_REF: + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, entityTextPtr, next)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + break; + case XML_TOK_TRAILING_CR: + next = entityTextPtr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_DATA_NEWLINE: + if (pool->end == pool->ptr && !poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + *(pool->ptr)++ = 0xA; + break; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, entityTextPtr); + if (n < 0) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_BAD_CHAR_REF; + goto endEntityValue; + } + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_BAD_CHAR_REF; + goto endEntityValue; + } + for (i = 0; i < n; i++) { + if (pool->end == pool->ptr && !poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + *(pool->ptr)++ = buf[i]; + } + } + break; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_INVALID_TOKEN; + goto endEntityValue; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + result = XML_ERROR_INVALID_TOKEN; + goto endEntityValue; + default: + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_UNEXPECTED_STATE; + goto endEntityValue; + } + entityTextPtr = next; + } +endEntityValue: +#ifdef XML_DTD + prologState.inEntityValue = oldInEntityValue; +#endif /* XML_DTD */ + return result; +} + +static void FASTCALL +normalizeLines(XML_Char *s) +{ + XML_Char *p; + for (;; s++) { + if (*s == XML_T('\0')) + return; + if (*s == 0xD) + break; + } + p = s; + do { + if (*s == 0xD) { + *p++ = 0xA; + if (*++s == 0xA) + s++; + } + else + *p++ = *s++; + } while (*s); + *p = XML_T('\0'); +} + +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + const XML_Char *target; + XML_Char *data; + const char *tem; + if (!processingInstructionHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + start += enc->minBytesPerChar * 2; + tem = start + XmlNameLength(enc, start); + target = poolStoreString(&tempPool, enc, start, tem); + if (!target) + return 0; + poolFinish(&tempPool); + data = poolStoreString(&tempPool, enc, + XmlSkipS(enc, tem), + end - enc->minBytesPerChar*2); + if (!data) + return 0; + normalizeLines(data); + processingInstructionHandler(handlerArg, target, data); + poolClear(&tempPool); + return 1; +} + +static int +reportComment(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + XML_Char *data; + if (!commentHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + data = poolStoreString(&tempPool, + enc, + start + enc->minBytesPerChar * 4, + end - enc->minBytesPerChar * 3); + if (!data) + return 0; + normalizeLines(data); + commentHandler(handlerArg, data); + poolClear(&tempPool); + return 1; +} + +static void +reportDefault(XML_Parser parser, const ENCODING *enc, + const char *s, const char *end) +{ + if (MUST_CONVERT(enc, s)) { + enum XML_Convert_Result convert_res; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + do { + ICHAR *dataPtr = (ICHAR *)dataBuf; + convert_res = XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + defaultHandler(handlerArg, dataBuf, (int)(dataPtr - (ICHAR *)dataBuf)); + *eventPP = s; + } while ((convert_res != XML_CONVERT_COMPLETED) && (convert_res != XML_CONVERT_INPUT_INCOMPLETE)); + } + else + defaultHandler(handlerArg, (XML_Char *)s, (int)((XML_Char *)end - (XML_Char *)s)); +} + + +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, + XML_Bool isId, const XML_Char *value, XML_Parser parser) +{ + DEFAULT_ATTRIBUTE *att; + if (value || isId) { + /* The handling of default attributes gets messed up if we have + a default which duplicates a non-default. */ + int i; + for (i = 0; i < type->nDefaultAtts; i++) + if (attId == type->defaultAtts[i].id) + return 1; + if (isId && !type->idAtt && !attId->xmlns) + type->idAtt = attId; + } + if (type->nDefaultAtts == type->allocDefaultAtts) { + if (type->allocDefaultAtts == 0) { + type->allocDefaultAtts = 8; + type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(type->allocDefaultAtts + * sizeof(DEFAULT_ATTRIBUTE)); + if (!type->defaultAtts) + return 0; + } + else { + DEFAULT_ATTRIBUTE *temp; + int count = type->allocDefaultAtts * 2; + temp = (DEFAULT_ATTRIBUTE *) + REALLOC(type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE))); + if (temp == NULL) + return 0; + type->allocDefaultAtts = count; + type->defaultAtts = temp; + } + } + att = type->defaultAtts + type->nDefaultAtts; + att->id = attId; + att->value = value; + att->isCdata = isCdata; + if (!isCdata) + attId->maybeTokenized = XML_TRUE; + type->nDefaultAtts += 1; + return 1; +} + +static int +setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *name; + for (name = elementType->name; *name; name++) { + if (*name == XML_T(ASCII_COLON)) { + PREFIX *prefix; + const XML_Char *s; + for (s = elementType->name; s != name; s++) { + if (!poolAppendChar(&dtd->pool, *s)) + return 0; + } + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return 0; + prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), + sizeof(PREFIX)); + if (!prefix) + return 0; + if (prefix->name == poolStart(&dtd->pool)) + poolFinish(&dtd->pool); + else + poolDiscard(&dtd->pool); + elementType->prefix = prefix; + + } + } + return 1; +} + +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + ATTRIBUTE_ID *id; + const XML_Char *name; + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return NULL; + name = poolStoreString(&dtd->pool, enc, start, end); + if (!name) + return NULL; + /* skip quotation mark - its storage will be re-used (like in name[-1]) */ + ++name; + id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, name, sizeof(ATTRIBUTE_ID)); + if (!id) + return NULL; + if (id->name != name) + poolDiscard(&dtd->pool); + else { + poolFinish(&dtd->pool); + if (!ns) + ; + else if (name[0] == XML_T(ASCII_x) + && name[1] == XML_T(ASCII_m) + && name[2] == XML_T(ASCII_l) + && name[3] == XML_T(ASCII_n) + && name[4] == XML_T(ASCII_s) + && (name[5] == XML_T('\0') || name[5] == XML_T(ASCII_COLON))) { + if (name[5] == XML_T('\0')) + id->prefix = &dtd->defaultPrefix; + else + id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, name + 6, sizeof(PREFIX)); + id->xmlns = XML_TRUE; + } + else { + int i; + for (i = 0; name[i]; i++) { + /* attributes without prefix are *not* in the default namespace */ + if (name[i] == XML_T(ASCII_COLON)) { + int j; + for (j = 0; j < i; j++) { + if (!poolAppendChar(&dtd->pool, name[j])) + return NULL; + } + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return NULL; + id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), + sizeof(PREFIX)); + if (!id->prefix) + return NULL; + if (id->prefix->name == poolStart(&dtd->pool)) + poolFinish(&dtd->pool); + else + poolDiscard(&dtd->pool); + break; + } + } + } + } + return id; +} + +#define CONTEXT_SEP XML_T(ASCII_FF) + +static const XML_Char * +getContext(XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + HASH_TABLE_ITER iter; + XML_Bool needSep = XML_FALSE; + + if (dtd->defaultPrefix.binding) { + int i; + int len; + if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS))) + return NULL; + len = dtd->defaultPrefix.binding->uriLen; + if (namespaceSeparator) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, dtd->defaultPrefix.binding->uri[i])) + return NULL; + needSep = XML_TRUE; + } + + hashTableIterInit(&iter, &(dtd->prefixes)); + for (;;) { + int i; + int len; + const XML_Char *s; + PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter); + if (!prefix) + break; + if (!prefix->binding) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return NULL; + for (s = prefix->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return NULL; + if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS))) + return NULL; + len = prefix->binding->uriLen; + if (namespaceSeparator) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, prefix->binding->uri[i])) + return NULL; + needSep = XML_TRUE; + } + + + hashTableIterInit(&iter, &(dtd->generalEntities)); + for (;;) { + const XML_Char *s; + ENTITY *e = (ENTITY *)hashTableIterNext(&iter); + if (!e) + break; + if (!e->open) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return NULL; + for (s = e->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return 0; + needSep = XML_TRUE; + } + + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return NULL; + return tempPool.start; +} + +static XML_Bool +setContext(XML_Parser parser, const XML_Char *context) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *s = context; + + while (*context != XML_T('\0')) { + if (*s == CONTEXT_SEP || *s == XML_T('\0')) { + ENTITY *e; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + e = (ENTITY *)lookup(parser, &dtd->generalEntities, poolStart(&tempPool), 0); + if (e) + e->open = XML_TRUE; + if (*s != XML_T('\0')) + s++; + context = s; + poolDiscard(&tempPool); + } + else if (*s == XML_T(ASCII_EQUALS)) { + PREFIX *prefix; + if (poolLength(&tempPool) == 0) + prefix = &dtd->defaultPrefix; + else { + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&tempPool), + sizeof(PREFIX)); + if (!prefix) + return XML_FALSE; + if (prefix->name == poolStart(&tempPool)) { + prefix->name = poolCopyString(&dtd->pool, prefix->name); + if (!prefix->name) + return XML_FALSE; + } + poolDiscard(&tempPool); + } + for (context = s + 1; + *context != CONTEXT_SEP && *context != XML_T('\0'); + context++) + if (!poolAppendChar(&tempPool, *context)) + return XML_FALSE; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + if (addBinding(parser, prefix, NULL, poolStart(&tempPool), + &inheritedBindings) != XML_ERROR_NONE) + return XML_FALSE; + poolDiscard(&tempPool); + if (*context != XML_T('\0')) + ++context; + s = context; + } + else { + if (!poolAppendChar(&tempPool, *s)) + return XML_FALSE; + s++; + } + } + return XML_TRUE; +} + +static void FASTCALL +normalizePublicId(XML_Char *publicId) +{ + XML_Char *p = publicId; + XML_Char *s; + for (s = publicId; *s; s++) { + switch (*s) { + case 0x20: + case 0xD: + case 0xA: + if (p != publicId && p[-1] != 0x20) + *p++ = 0x20; + break; + default: + *p++ = *s; + } + } + if (p != publicId && p[-1] == 0x20) + --p; + *p = XML_T('\0'); +} + +static DTD * +dtdCreate(const XML_Memory_Handling_Suite *ms) +{ + DTD *p = (DTD *)ms->malloc_fcn(sizeof(DTD)); + if (p == NULL) + return p; + poolInit(&(p->pool), ms); + poolInit(&(p->entityValuePool), ms); + hashTableInit(&(p->generalEntities), ms); + hashTableInit(&(p->elementTypes), ms); + hashTableInit(&(p->attributeIds), ms); + hashTableInit(&(p->prefixes), ms); +#ifdef XML_DTD + p->paramEntityRead = XML_FALSE; + hashTableInit(&(p->paramEntities), ms); +#endif /* XML_DTD */ + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; + + p->in_eldecl = XML_FALSE; + p->scaffIndex = NULL; + p->scaffold = NULL; + p->scaffLevel = 0; + p->scaffSize = 0; + p->scaffCount = 0; + p->contentStringLen = 0; + + p->keepProcessing = XML_TRUE; + p->hasParamEntityRefs = XML_FALSE; + p->standalone = XML_FALSE; + return p; +} + +static void +dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + ms->free_fcn(e->defaultAtts); + } + hashTableClear(&(p->generalEntities)); +#ifdef XML_DTD + p->paramEntityRead = XML_FALSE; + hashTableClear(&(p->paramEntities)); +#endif /* XML_DTD */ + hashTableClear(&(p->elementTypes)); + hashTableClear(&(p->attributeIds)); + hashTableClear(&(p->prefixes)); + poolClear(&(p->pool)); + poolClear(&(p->entityValuePool)); + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; + + p->in_eldecl = XML_FALSE; + + ms->free_fcn(p->scaffIndex); + p->scaffIndex = NULL; + ms->free_fcn(p->scaffold); + p->scaffold = NULL; + + p->scaffLevel = 0; + p->scaffSize = 0; + p->scaffCount = 0; + p->contentStringLen = 0; + + p->keepProcessing = XML_TRUE; + p->hasParamEntityRefs = XML_FALSE; + p->standalone = XML_FALSE; +} + +static void +dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + ms->free_fcn(e->defaultAtts); + } + hashTableDestroy(&(p->generalEntities)); +#ifdef XML_DTD + hashTableDestroy(&(p->paramEntities)); +#endif /* XML_DTD */ + hashTableDestroy(&(p->elementTypes)); + hashTableDestroy(&(p->attributeIds)); + hashTableDestroy(&(p->prefixes)); + poolDestroy(&(p->pool)); + poolDestroy(&(p->entityValuePool)); + if (isDocEntity) { + ms->free_fcn(p->scaffIndex); + ms->free_fcn(p->scaffold); + } + ms->free_fcn(p); +} + +/* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. + The new DTD has already been initialized. +*/ +static int +dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + + /* Copy the prefix table. */ + + hashTableIterInit(&iter, &(oldDtd->prefixes)); + for (;;) { + const XML_Char *name; + const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter); + if (!oldP) + break; + name = poolCopyString(&(newDtd->pool), oldP->name); + if (!name) + return 0; + if (!lookup(oldParser, &(newDtd->prefixes), name, sizeof(PREFIX))) + return 0; + } + + hashTableIterInit(&iter, &(oldDtd->attributeIds)); + + /* Copy the attribute id table. */ + + for (;;) { + ATTRIBUTE_ID *newA; + const XML_Char *name; + const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter); + + if (!oldA) + break; + /* Remember to allocate the scratch byte before the name. */ + if (!poolAppendChar(&(newDtd->pool), XML_T('\0'))) + return 0; + name = poolCopyString(&(newDtd->pool), oldA->name); + if (!name) + return 0; + ++name; + newA = (ATTRIBUTE_ID *)lookup(oldParser, &(newDtd->attributeIds), name, + sizeof(ATTRIBUTE_ID)); + if (!newA) + return 0; + newA->maybeTokenized = oldA->maybeTokenized; + if (oldA->prefix) { + newA->xmlns = oldA->xmlns; + if (oldA->prefix == &oldDtd->defaultPrefix) + newA->prefix = &newDtd->defaultPrefix; + else + newA->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), + oldA->prefix->name, 0); + } + } + + /* Copy the element type table. */ + + hashTableIterInit(&iter, &(oldDtd->elementTypes)); + + for (;;) { + int i; + ELEMENT_TYPE *newE; + const XML_Char *name; + const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(&(newDtd->pool), oldE->name); + if (!name) + return 0; + newE = (ELEMENT_TYPE *)lookup(oldParser, &(newDtd->elementTypes), name, + sizeof(ELEMENT_TYPE)); + if (!newE) + return 0; + if (oldE->nDefaultAtts) { + newE->defaultAtts = (DEFAULT_ATTRIBUTE *) + ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + if (!newE->defaultAtts) { + ms->free_fcn(newE); + return 0; + } + } + if (oldE->idAtt) + newE->idAtt = (ATTRIBUTE_ID *) + lookup(oldParser, &(newDtd->attributeIds), oldE->idAtt->name, 0); + newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts; + if (oldE->prefix) + newE->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), + oldE->prefix->name, 0); + for (i = 0; i < newE->nDefaultAtts; i++) { + newE->defaultAtts[i].id = (ATTRIBUTE_ID *) + lookup(oldParser, &(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0); + newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata; + if (oldE->defaultAtts[i].value) { + newE->defaultAtts[i].value + = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value); + if (!newE->defaultAtts[i].value) + return 0; + } + else + newE->defaultAtts[i].value = NULL; + } + } + + /* Copy the entity tables. */ + if (!copyEntityTable(oldParser, + &(newDtd->generalEntities), + &(newDtd->pool), + &(oldDtd->generalEntities))) + return 0; + +#ifdef XML_DTD + if (!copyEntityTable(oldParser, + &(newDtd->paramEntities), + &(newDtd->pool), + &(oldDtd->paramEntities))) + return 0; + newDtd->paramEntityRead = oldDtd->paramEntityRead; +#endif /* XML_DTD */ + + newDtd->keepProcessing = oldDtd->keepProcessing; + newDtd->hasParamEntityRefs = oldDtd->hasParamEntityRefs; + newDtd->standalone = oldDtd->standalone; + + /* Don't want deep copying for scaffolding */ + newDtd->in_eldecl = oldDtd->in_eldecl; + newDtd->scaffold = oldDtd->scaffold; + newDtd->contentStringLen = oldDtd->contentStringLen; + newDtd->scaffSize = oldDtd->scaffSize; + newDtd->scaffLevel = oldDtd->scaffLevel; + newDtd->scaffIndex = oldDtd->scaffIndex; + + return 1; +} /* End dtdCopy */ + +static int +copyEntityTable(XML_Parser oldParser, + HASH_TABLE *newTable, + STRING_POOL *newPool, + const HASH_TABLE *oldTable) +{ + HASH_TABLE_ITER iter; + const XML_Char *cachedOldBase = NULL; + const XML_Char *cachedNewBase = NULL; + + hashTableIterInit(&iter, oldTable); + + for (;;) { + ENTITY *newE; + const XML_Char *name; + const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(newPool, oldE->name); + if (!name) + return 0; + newE = (ENTITY *)lookup(oldParser, newTable, name, sizeof(ENTITY)); + if (!newE) + return 0; + if (oldE->systemId) { + const XML_Char *tem = poolCopyString(newPool, oldE->systemId); + if (!tem) + return 0; + newE->systemId = tem; + if (oldE->base) { + if (oldE->base == cachedOldBase) + newE->base = cachedNewBase; + else { + cachedOldBase = oldE->base; + tem = poolCopyString(newPool, cachedOldBase); + if (!tem) + return 0; + cachedNewBase = newE->base = tem; + } + } + if (oldE->publicId) { + tem = poolCopyString(newPool, oldE->publicId); + if (!tem) + return 0; + newE->publicId = tem; + } + } + else { + const XML_Char *tem = poolCopyStringN(newPool, oldE->textPtr, + oldE->textLen); + if (!tem) + return 0; + newE->textPtr = tem; + newE->textLen = oldE->textLen; + } + if (oldE->notation) { + const XML_Char *tem = poolCopyString(newPool, oldE->notation); + if (!tem) + return 0; + newE->notation = tem; + } + newE->is_param = oldE->is_param; + newE->is_internal = oldE->is_internal; + } + return 1; +} + +#define INIT_POWER 6 + +static XML_Bool FASTCALL +keyeq(KEY s1, KEY s2) +{ + for (; *s1 == *s2; s1++, s2++) + if (*s1 == 0) + return XML_TRUE; + return XML_FALSE; +} + +static unsigned long FASTCALL +hash(XML_Parser parser, KEY s) +{ + unsigned long h = hash_secret_salt; + while (*s) + h = CHAR_HASH(h, *s++); + return h; +} + +static NAMED * +lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) +{ + size_t i; + if (table->size == 0) { + size_t tsize; + if (!createSize) + return NULL; + table->power = INIT_POWER; + /* table->size is a power of 2 */ + table->size = (size_t)1 << INIT_POWER; + tsize = table->size * sizeof(NAMED *); + table->v = (NAMED **)table->mem->malloc_fcn(tsize); + if (!table->v) { + table->size = 0; + return NULL; + } + memset(table->v, 0, tsize); + i = hash(parser, name) & ((unsigned long)table->size - 1); + } + else { + unsigned long h = hash(parser, name); + unsigned long mask = (unsigned long)table->size - 1; + unsigned char step = 0; + i = h & mask; + while (table->v[i]) { + if (keyeq(name, table->v[i]->name)) + return table->v[i]; + if (!step) + step = PROBE_STEP(h, mask, table->power); + i < step ? (i += table->size - step) : (i -= step); + } + if (!createSize) + return NULL; + + /* check for overflow (table is half full) */ + if (table->used >> (table->power - 1)) { + unsigned char newPower = table->power + 1; + size_t newSize = (size_t)1 << newPower; + unsigned long newMask = (unsigned long)newSize - 1; + size_t tsize = newSize * sizeof(NAMED *); + NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize); + if (!newV) + return NULL; + memset(newV, 0, tsize); + for (i = 0; i < table->size; i++) + if (table->v[i]) { + unsigned long newHash = hash(parser, table->v[i]->name); + size_t j = newHash & newMask; + step = 0; + while (newV[j]) { + if (!step) + step = PROBE_STEP(newHash, newMask, newPower); + j < step ? (j += newSize - step) : (j -= step); + } + newV[j] = table->v[i]; + } + table->mem->free_fcn(table->v); + table->v = newV; + table->power = newPower; + table->size = newSize; + i = h & newMask; + step = 0; + while (table->v[i]) { + if (!step) + step = PROBE_STEP(h, newMask, newPower); + i < step ? (i += newSize - step) : (i -= step); + } + } + } + table->v[i] = (NAMED *)table->mem->malloc_fcn(createSize); + if (!table->v[i]) + return NULL; + memset(table->v[i], 0, createSize); + table->v[i]->name = name; + (table->used)++; + return table->v[i]; +} + +static void FASTCALL +hashTableClear(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) { + table->mem->free_fcn(table->v[i]); + table->v[i] = NULL; + } + table->used = 0; +} + +static void FASTCALL +hashTableDestroy(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) + table->mem->free_fcn(table->v[i]); + table->mem->free_fcn(table->v); +} + +static void FASTCALL +hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) +{ + p->power = 0; + p->size = 0; + p->used = 0; + p->v = NULL; + p->mem = ms; +} + +static void FASTCALL +hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) +{ + iter->p = table->v; + iter->end = iter->p + table->size; +} + +static NAMED * FASTCALL +hashTableIterNext(HASH_TABLE_ITER *iter) +{ + while (iter->p != iter->end) { + NAMED *tem = *(iter->p)++; + if (tem) + return tem; + } + return NULL; +} + +static void FASTCALL +poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) +{ + pool->blocks = NULL; + pool->freeBlocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; + pool->mem = ms; +} + +static void FASTCALL +poolClear(STRING_POOL *pool) +{ + if (!pool->freeBlocks) + pool->freeBlocks = pool->blocks; + else { + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + p->next = pool->freeBlocks; + pool->freeBlocks = p; + p = tem; + } + } + pool->blocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; +} + +static void FASTCALL +poolDestroy(STRING_POOL *pool) +{ + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + pool->mem->free_fcn(p); + p = tem; + } + p = pool->freeBlocks; + while (p) { + BLOCK *tem = p->next; + pool->mem->free_fcn(p); + p = tem; + } +} + +static XML_Char * +poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!pool->ptr && !poolGrow(pool)) + return NULL; + for (;;) { + const enum XML_Convert_Result convert_res = XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end); + if ((convert_res == XML_CONVERT_COMPLETED) || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) + break; + if (!poolGrow(pool)) + return NULL; + } + return pool->start; +} + +static const XML_Char * FASTCALL +poolCopyString(STRING_POOL *pool, const XML_Char *s) +{ + do { + if (!poolAppendChar(pool, *s)) + return NULL; + } while (*s++); + s = pool->start; + poolFinish(pool); + return s; +} + +static const XML_Char * +poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) +{ + if (!pool->ptr && !poolGrow(pool)) + return NULL; + for (; n > 0; --n, s++) { + if (!poolAppendChar(pool, *s)) + return NULL; + } + s = pool->start; + poolFinish(pool); + return s; +} + +static const XML_Char * FASTCALL +poolAppendString(STRING_POOL *pool, const XML_Char *s) +{ + while (*s) { + if (!poolAppendChar(pool, *s)) + return NULL; + s++; + } + return pool->start; +} + +static XML_Char * +poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!poolAppend(pool, enc, ptr, end)) + return NULL; + if (pool->ptr == pool->end && !poolGrow(pool)) + return NULL; + *(pool->ptr)++ = 0; + return pool->start; +} + +static XML_Bool FASTCALL +poolGrow(STRING_POOL *pool) +{ + if (pool->freeBlocks) { + if (pool->start == 0) { + pool->blocks = pool->freeBlocks; + pool->freeBlocks = pool->freeBlocks->next; + pool->blocks->next = NULL; + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + pool->ptr = pool->start; + return XML_TRUE; + } + if (pool->end - pool->start < pool->freeBlocks->size) { + BLOCK *tem = pool->freeBlocks->next; + pool->freeBlocks->next = pool->blocks; + pool->blocks = pool->freeBlocks; + pool->freeBlocks = tem; + memcpy(pool->blocks->s, pool->start, + (pool->end - pool->start) * sizeof(XML_Char)); + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + return XML_TRUE; + } + } + if (pool->blocks && pool->start == pool->blocks->s) { + BLOCK *temp; + int blockSize = (int)((unsigned)(pool->end - pool->start)*2U); + + if (blockSize < 0) + return XML_FALSE; + + temp = (BLOCK *) + pool->mem->realloc_fcn(pool->blocks, + (offsetof(BLOCK, s) + + blockSize * sizeof(XML_Char))); + if (temp == NULL) + return XML_FALSE; + pool->blocks = temp; + pool->blocks->size = blockSize; + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + blockSize; + } + else { + BLOCK *tem; + int blockSize = (int)(pool->end - pool->start); + + if (blockSize < 0) + return XML_FALSE; + + if (blockSize < INIT_BLOCK_SIZE) + blockSize = INIT_BLOCK_SIZE; + else + blockSize *= 2; + tem = (BLOCK *)pool->mem->malloc_fcn(offsetof(BLOCK, s) + + blockSize * sizeof(XML_Char)); + if (!tem) + return XML_FALSE; + tem->size = blockSize; + tem->next = pool->blocks; + pool->blocks = tem; + if (pool->ptr != pool->start) + memcpy(tem->s, pool->start, + (pool->ptr - pool->start) * sizeof(XML_Char)); + pool->ptr = tem->s + (pool->ptr - pool->start); + pool->start = tem->s; + pool->end = tem->s + blockSize; + } + return XML_TRUE; +} + +static int FASTCALL +nextScaffoldPart(XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + CONTENT_SCAFFOLD * me; + int next; + + if (!dtd->scaffIndex) { + dtd->scaffIndex = (int *)MALLOC(groupSize * sizeof(int)); + if (!dtd->scaffIndex) + return -1; + dtd->scaffIndex[0] = 0; + } + + if (dtd->scaffCount >= dtd->scaffSize) { + CONTENT_SCAFFOLD *temp; + if (dtd->scaffold) { + temp = (CONTENT_SCAFFOLD *) + REALLOC(dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD)); + if (temp == NULL) + return -1; + dtd->scaffSize *= 2; + } + else { + temp = (CONTENT_SCAFFOLD *)MALLOC(INIT_SCAFFOLD_ELEMENTS + * sizeof(CONTENT_SCAFFOLD)); + if (temp == NULL) + return -1; + dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS; + } + dtd->scaffold = temp; + } + next = dtd->scaffCount++; + me = &dtd->scaffold[next]; + if (dtd->scaffLevel) { + CONTENT_SCAFFOLD *parent = &dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel-1]]; + if (parent->lastchild) { + dtd->scaffold[parent->lastchild].nextsib = next; + } + if (!parent->childcnt) + parent->firstchild = next; + parent->lastchild = next; + parent->childcnt++; + } + me->firstchild = me->lastchild = me->childcnt = me->nextsib = 0; + return next; +} + +static void +build_node(XML_Parser parser, + int src_node, + XML_Content *dest, + XML_Content **contpos, + XML_Char **strpos) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + dest->type = dtd->scaffold[src_node].type; + dest->quant = dtd->scaffold[src_node].quant; + if (dest->type == XML_CTYPE_NAME) { + const XML_Char *src; + dest->name = *strpos; + src = dtd->scaffold[src_node].name; + for (;;) { + *(*strpos)++ = *src; + if (!*src) + break; + src++; + } + dest->numchildren = 0; + dest->children = NULL; + } + else { + unsigned int i; + int cn; + dest->numchildren = dtd->scaffold[src_node].childcnt; + dest->children = *contpos; + *contpos += dest->numchildren; + for (i = 0, cn = dtd->scaffold[src_node].firstchild; + i < dest->numchildren; + i++, cn = dtd->scaffold[cn].nextsib) { + build_node(parser, cn, &(dest->children[i]), contpos, strpos); + } + dest->name = NULL; + } +} + +static XML_Content * +build_model (XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + XML_Content *ret; + XML_Content *cpos; + XML_Char * str; + int allocsize = (dtd->scaffCount * sizeof(XML_Content) + + (dtd->contentStringLen * sizeof(XML_Char))); + + ret = (XML_Content *)MALLOC(allocsize); + if (!ret) + return NULL; + + str = (XML_Char *) (&ret[dtd->scaffCount]); + cpos = &ret[1]; + + build_node(parser, 0, ret, &cpos, &str); + return ret; +} + +static ELEMENT_TYPE * +getElementType(XML_Parser parser, + const ENCODING *enc, + const char *ptr, + const char *end) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *name = poolStoreString(&dtd->pool, enc, ptr, end); + ELEMENT_TYPE *ret; + + if (!name) + return NULL; + ret = (ELEMENT_TYPE *) lookup(parser, &dtd->elementTypes, name, sizeof(ELEMENT_TYPE)); + if (!ret) + return NULL; + if (ret->name != name) + poolDiscard(&dtd->pool); + else { + poolFinish(&dtd->pool); + if (!setElementTypePrefix(parser, ret)) + return NULL; + } + return ret; +} diff --git a/deps/EXPAT/expat/xmlrole.c b/deps/EXPAT/expat/xmlrole.c new file mode 100644 index 0000000000..8475238d3e --- /dev/null +++ b/deps/EXPAT/expat/xmlrole.c @@ -0,0 +1,1322 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include +#include "expat_config.h" +#include "expat_external.h" +#include "internal.h" +#include "xmlrole.h" +#include "ascii.h" + +/* Doesn't check: + + that ,| are not mixed in a model group + content of literals + +*/ + +static const char KW_ANY[] = { + ASCII_A, ASCII_N, ASCII_Y, '\0' }; +static const char KW_ATTLIST[] = { + ASCII_A, ASCII_T, ASCII_T, ASCII_L, ASCII_I, ASCII_S, ASCII_T, '\0' }; +static const char KW_CDATA[] = { + ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_DOCTYPE[] = { + ASCII_D, ASCII_O, ASCII_C, ASCII_T, ASCII_Y, ASCII_P, ASCII_E, '\0' }; +static const char KW_ELEMENT[] = { + ASCII_E, ASCII_L, ASCII_E, ASCII_M, ASCII_E, ASCII_N, ASCII_T, '\0' }; +static const char KW_EMPTY[] = { + ASCII_E, ASCII_M, ASCII_P, ASCII_T, ASCII_Y, '\0' }; +static const char KW_ENTITIES[] = { + ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, + '\0' }; +static const char KW_ENTITY[] = { + ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; +static const char KW_FIXED[] = { + ASCII_F, ASCII_I, ASCII_X, ASCII_E, ASCII_D, '\0' }; +static const char KW_ID[] = { + ASCII_I, ASCII_D, '\0' }; +static const char KW_IDREF[] = { + ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; +static const char KW_IDREFS[] = { + ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; +#ifdef XML_DTD +static const char KW_IGNORE[] = { + ASCII_I, ASCII_G, ASCII_N, ASCII_O, ASCII_R, ASCII_E, '\0' }; +#endif +static const char KW_IMPLIED[] = { + ASCII_I, ASCII_M, ASCII_P, ASCII_L, ASCII_I, ASCII_E, ASCII_D, '\0' }; +#ifdef XML_DTD +static const char KW_INCLUDE[] = { + ASCII_I, ASCII_N, ASCII_C, ASCII_L, ASCII_U, ASCII_D, ASCII_E, '\0' }; +#endif +static const char KW_NDATA[] = { + ASCII_N, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_NMTOKEN[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; +static const char KW_NMTOKENS[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, + '\0' }; +static const char KW_NOTATION[] = + { ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, + '\0' }; +static const char KW_PCDATA[] = { + ASCII_P, ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_PUBLIC[] = { + ASCII_P, ASCII_U, ASCII_B, ASCII_L, ASCII_I, ASCII_C, '\0' }; +static const char KW_REQUIRED[] = { + ASCII_R, ASCII_E, ASCII_Q, ASCII_U, ASCII_I, ASCII_R, ASCII_E, ASCII_D, + '\0' }; +static const char KW_SYSTEM[] = { + ASCII_S, ASCII_Y, ASCII_S, ASCII_T, ASCII_E, ASCII_M, '\0' }; + +#ifndef MIN_BYTES_PER_CHAR +#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar) +#endif + +#ifdef XML_DTD +#define setTopLevel(state) \ + ((state)->handler = ((state)->documentEntity \ + ? internalSubset \ + : externalSubset1)) +#else /* not XML_DTD */ +#define setTopLevel(state) ((state)->handler = internalSubset) +#endif /* not XML_DTD */ + +typedef int PTRCALL PROLOG_HANDLER(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + +static PROLOG_HANDLER + prolog0, prolog1, prolog2, + doctype0, doctype1, doctype2, doctype3, doctype4, doctype5, + internalSubset, + entity0, entity1, entity2, entity3, entity4, entity5, entity6, + entity7, entity8, entity9, entity10, + notation0, notation1, notation2, notation3, notation4, + attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6, + attlist7, attlist8, attlist9, + element0, element1, element2, element3, element4, element5, element6, + element7, +#ifdef XML_DTD + externalSubset0, externalSubset1, + condSect0, condSect1, condSect2, +#endif /* XML_DTD */ + declClose, + error; + +static int FASTCALL common(PROLOG_STATE *state, int tok); + +static int PTRCALL +prolog0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + state->handler = prolog1; + return XML_ROLE_NONE; + case XML_TOK_XML_DECL: + state->handler = prolog1; + return XML_ROLE_XML_DECL; + case XML_TOK_PI: + state->handler = prolog1; + return XML_ROLE_PI; + case XML_TOK_COMMENT: + state->handler = prolog1; + return XML_ROLE_COMMENT; + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_DOCTYPE)) + break; + state->handler = doctype0; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +prolog1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_DOCTYPE)) + break; + state->handler = doctype0; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +prolog2(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +doctype0(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = doctype1; + return XML_ROLE_DOCTYPE_NAME; + } + return common(state, tok); +} + +static int PTRCALL +doctype1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = doctype3; + return XML_ROLE_DOCTYPE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = doctype2; + return XML_ROLE_DOCTYPE_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +doctype2(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype3; + return XML_ROLE_DOCTYPE_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +doctype3(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype4; + return XML_ROLE_DOCTYPE_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +doctype4(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return common(state, tok); +} + +static int PTRCALL +doctype5(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return common(state, tok); +} + +static int PTRCALL +internalSubset(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ENTITY)) { + state->handler = entity0; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ATTLIST)) { + state->handler = attlist0; + return XML_ROLE_ATTLIST_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ELEMENT)) { + state->handler = element0; + return XML_ROLE_ELEMENT_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_NOTATION)) { + state->handler = notation0; + return XML_ROLE_NOTATION_NONE; + } + break; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_PARAM_ENTITY_REF: + return XML_ROLE_PARAM_ENTITY_REF; + case XML_TOK_CLOSE_BRACKET: + state->handler = doctype5; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_NONE: + return XML_ROLE_NONE; + } + return common(state, tok); +} + +#ifdef XML_DTD + +static int PTRCALL +externalSubset0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + state->handler = externalSubset1; + if (tok == XML_TOK_XML_DECL) + return XML_ROLE_TEXT_DECL; + return externalSubset1(state, tok, ptr, end, enc); +} + +static int PTRCALL +externalSubset1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_COND_SECT_OPEN: + state->handler = condSect0; + return XML_ROLE_NONE; + case XML_TOK_COND_SECT_CLOSE: + if (state->includeLevel == 0) + break; + state->includeLevel -= 1; + return XML_ROLE_NONE; + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_BRACKET: + break; + case XML_TOK_NONE: + if (state->includeLevel) + break; + return XML_ROLE_NONE; + default: + return internalSubset(state, tok, ptr, end, enc); + } + return common(state, tok); +} + +#endif /* XML_DTD */ + +static int PTRCALL +entity0(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_PERCENT: + state->handler = entity1; + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = entity2; + return XML_ROLE_GENERAL_ENTITY_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity1(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = entity7; + return XML_ROLE_PARAM_ENTITY_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = entity4; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = entity3; + return XML_ROLE_ENTITY_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +entity3(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity4; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity4(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity5; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ENTITY_COMPLETE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_NDATA)) { + state->handler = entity6; + return XML_ROLE_ENTITY_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +entity6(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_NOTATION_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = entity9; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = entity8; + return XML_ROLE_ENTITY_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +entity8(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity9; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity9(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity10; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity10(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ENTITY_COMPLETE; + } + return common(state, tok); +} + +static int PTRCALL +notation0(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_NAME: + state->handler = notation1; + return XML_ROLE_NOTATION_NAME; + } + return common(state, tok); +} + +static int PTRCALL +notation1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = notation3; + return XML_ROLE_NOTATION_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = notation2; + return XML_ROLE_NOTATION_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +notation2(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = notation4; + return XML_ROLE_NOTATION_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +notation3(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_NOTATION_NONE; + return XML_ROLE_NOTATION_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +notation4(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_NOTATION_NONE; + return XML_ROLE_NOTATION_SYSTEM_ID; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_NOTATION_NO_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +attlist0(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist1; + return XML_ROLE_ATTLIST_ELEMENT_NAME; + } + return common(state, tok); +} + +static int PTRCALL +attlist1(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist2; + return XML_ROLE_ATTRIBUTE_NAME; + } + return common(state, tok); +} + +static int PTRCALL +attlist2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + { + static const char * const types[] = { + KW_CDATA, + KW_ID, + KW_IDREF, + KW_IDREFS, + KW_ENTITY, + KW_ENTITIES, + KW_NMTOKEN, + KW_NMTOKENS, + }; + int i; + for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++) + if (XmlNameMatchesAscii(enc, ptr, end, types[i])) { + state->handler = attlist8; + return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i; + } + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_NOTATION)) { + state->handler = attlist5; + return XML_ROLE_ATTLIST_NONE; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = attlist3; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist3(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NMTOKEN: + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist4; + return XML_ROLE_ATTRIBUTE_ENUM_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist4(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OR: + state->handler = attlist3; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist5(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OPEN_PAREN: + state->handler = attlist6; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist6(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + state->handler = attlist7; + return XML_ROLE_ATTRIBUTE_NOTATION_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist7(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OR: + state->handler = attlist6; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +/* default value */ +static int PTRCALL +attlist8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_IMPLIED)) { + state->handler = attlist1; + return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_REQUIRED)) { + state->handler = attlist1; + return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_FIXED)) { + state->handler = attlist9; + return XML_ROLE_ATTLIST_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist9(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_FIXED_ATTRIBUTE_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +element0(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element1; + return XML_ROLE_ELEMENT_NAME; + } + return common(state, tok); +} + +static int PTRCALL +element1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_EMPTY)) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_CONTENT_EMPTY; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_ANY)) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_CONTENT_ANY; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = element2; + state->level = 1; + return XML_ROLE_GROUP_OPEN; + } + return common(state, tok); +} + +static int PTRCALL +element2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_PCDATA)) { + state->handler = element3; + return XML_ROLE_CONTENT_PCDATA; + } + break; + case XML_TOK_OPEN_PAREN: + state->level = 2; + state->handler = element6; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return common(state, tok); +} + +static int PTRCALL +element3(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_ELEMENT_NONE; + } + return common(state, tok); +} + +static int PTRCALL +element4(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element5; + return XML_ROLE_CONTENT_ELEMENT; + } + return common(state, tok); +} + +static int PTRCALL +element5(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_ELEMENT_NONE; + } + return common(state, tok); +} + +static int PTRCALL +element6(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_OPEN_PAREN: + state->level += 1; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return common(state, tok); +} + +static int PTRCALL +element7(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_CLOSE_PAREN_QUESTION: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_OPT; + case XML_TOK_CLOSE_PAREN_PLUS: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_PLUS; + case XML_TOK_COMMA: + state->handler = element6; + return XML_ROLE_GROUP_SEQUENCE; + case XML_TOK_OR: + state->handler = element6; + return XML_ROLE_GROUP_CHOICE; + } + return common(state, tok); +} + +#ifdef XML_DTD + +static int PTRCALL +condSect0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_INCLUDE)) { + state->handler = condSect1; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_IGNORE)) { + state->handler = condSect2; + return XML_ROLE_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +condSect1(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = externalSubset1; + state->includeLevel += 1; + return XML_ROLE_NONE; + } + return common(state, tok); +} + +static int PTRCALL +condSect2(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = externalSubset1; + return XML_ROLE_IGNORE_SECT; + } + return common(state, tok); +} + +#endif /* XML_DTD */ + +static int PTRCALL +declClose(PROLOG_STATE *state, + int tok, + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return state->role_none; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return state->role_none; + } + return common(state, tok); +} + +static int PTRCALL +error(PROLOG_STATE *UNUSED_P(state), + int UNUSED_P(tok), + const char *UNUSED_P(ptr), + const char *UNUSED_P(end), + const ENCODING *UNUSED_P(enc)) +{ + return XML_ROLE_NONE; +} + +static int FASTCALL +common(PROLOG_STATE *state, int tok) +{ +#ifdef XML_DTD + if (!state->documentEntity && tok == XML_TOK_PARAM_ENTITY_REF) + return XML_ROLE_INNER_PARAM_ENTITY_REF; +#endif + state->handler = error; + return XML_ROLE_ERROR; +} + +void +XmlPrologStateInit(PROLOG_STATE *state) +{ + state->handler = prolog0; +#ifdef XML_DTD + state->documentEntity = 1; + state->includeLevel = 0; + state->inEntityValue = 0; +#endif /* XML_DTD */ +} + +#ifdef XML_DTD + +void +XmlPrologStateInitExternalEntity(PROLOG_STATE *state) +{ + state->handler = externalSubset0; + state->documentEntity = 0; + state->includeLevel = 0; +} + +#endif /* XML_DTD */ diff --git a/deps/EXPAT/expat/xmlrole.h b/deps/EXPAT/expat/xmlrole.h new file mode 100644 index 0000000000..4dd9f06f97 --- /dev/null +++ b/deps/EXPAT/expat/xmlrole.h @@ -0,0 +1,114 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef XmlRole_INCLUDED +#define XmlRole_INCLUDED 1 + +#ifdef __VMS +/* 0 1 2 3 0 1 2 3 + 1234567890123456789012345678901 1234567890123456789012345678901 */ +#define XmlPrologStateInitExternalEntity XmlPrologStateInitExternalEnt +#endif + +#include "xmltok.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + XML_ROLE_ERROR = -1, + XML_ROLE_NONE = 0, + XML_ROLE_XML_DECL, + XML_ROLE_INSTANCE_START, + XML_ROLE_DOCTYPE_NONE, + XML_ROLE_DOCTYPE_NAME, + XML_ROLE_DOCTYPE_SYSTEM_ID, + XML_ROLE_DOCTYPE_PUBLIC_ID, + XML_ROLE_DOCTYPE_INTERNAL_SUBSET, + XML_ROLE_DOCTYPE_CLOSE, + XML_ROLE_GENERAL_ENTITY_NAME, + XML_ROLE_PARAM_ENTITY_NAME, + XML_ROLE_ENTITY_NONE, + XML_ROLE_ENTITY_VALUE, + XML_ROLE_ENTITY_SYSTEM_ID, + XML_ROLE_ENTITY_PUBLIC_ID, + XML_ROLE_ENTITY_COMPLETE, + XML_ROLE_ENTITY_NOTATION_NAME, + XML_ROLE_NOTATION_NONE, + XML_ROLE_NOTATION_NAME, + XML_ROLE_NOTATION_SYSTEM_ID, + XML_ROLE_NOTATION_NO_SYSTEM_ID, + XML_ROLE_NOTATION_PUBLIC_ID, + XML_ROLE_ATTRIBUTE_NAME, + XML_ROLE_ATTRIBUTE_TYPE_CDATA, + XML_ROLE_ATTRIBUTE_TYPE_ID, + XML_ROLE_ATTRIBUTE_TYPE_IDREF, + XML_ROLE_ATTRIBUTE_TYPE_IDREFS, + XML_ROLE_ATTRIBUTE_TYPE_ENTITY, + XML_ROLE_ATTRIBUTE_TYPE_ENTITIES, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS, + XML_ROLE_ATTRIBUTE_ENUM_VALUE, + XML_ROLE_ATTRIBUTE_NOTATION_VALUE, + XML_ROLE_ATTLIST_NONE, + XML_ROLE_ATTLIST_ELEMENT_NAME, + XML_ROLE_IMPLIED_ATTRIBUTE_VALUE, + XML_ROLE_REQUIRED_ATTRIBUTE_VALUE, + XML_ROLE_DEFAULT_ATTRIBUTE_VALUE, + XML_ROLE_FIXED_ATTRIBUTE_VALUE, + XML_ROLE_ELEMENT_NONE, + XML_ROLE_ELEMENT_NAME, + XML_ROLE_CONTENT_ANY, + XML_ROLE_CONTENT_EMPTY, + XML_ROLE_CONTENT_PCDATA, + XML_ROLE_GROUP_OPEN, + XML_ROLE_GROUP_CLOSE, + XML_ROLE_GROUP_CLOSE_REP, + XML_ROLE_GROUP_CLOSE_OPT, + XML_ROLE_GROUP_CLOSE_PLUS, + XML_ROLE_GROUP_CHOICE, + XML_ROLE_GROUP_SEQUENCE, + XML_ROLE_CONTENT_ELEMENT, + XML_ROLE_CONTENT_ELEMENT_REP, + XML_ROLE_CONTENT_ELEMENT_OPT, + XML_ROLE_CONTENT_ELEMENT_PLUS, + XML_ROLE_PI, + XML_ROLE_COMMENT, +#ifdef XML_DTD + XML_ROLE_TEXT_DECL, + XML_ROLE_IGNORE_SECT, + XML_ROLE_INNER_PARAM_ENTITY_REF, +#endif /* XML_DTD */ + XML_ROLE_PARAM_ENTITY_REF +}; + +typedef struct prolog_state { + int (PTRCALL *handler) (struct prolog_state *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + unsigned level; + int role_none; +#ifdef XML_DTD + unsigned includeLevel; + int documentEntity; + int inEntityValue; +#endif /* XML_DTD */ +} PROLOG_STATE; + +void XmlPrologStateInit(PROLOG_STATE *); +#ifdef XML_DTD +void XmlPrologStateInitExternalEntity(PROLOG_STATE *); +#endif /* XML_DTD */ + +#define XmlTokenRole(state, tok, ptr, end, enc) \ + (((state)->handler)(state, tok, ptr, end, enc)) + +#ifdef __cplusplus +} +#endif + +#endif /* not XmlRole_INCLUDED */ diff --git a/deps/EXPAT/expat/xmltok.c b/deps/EXPAT/expat/xmltok.c new file mode 100644 index 0000000000..f10b459ffd --- /dev/null +++ b/deps/EXPAT/expat/xmltok.c @@ -0,0 +1,1737 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include +#include "expat_config.h" +#include "expat_external.h" +#include "internal.h" +#include "xmltok.h" +#include "nametab.h" + +#ifdef XML_DTD +#define IGNORE_SECTION_TOK_VTABLE , PREFIX(ignoreSectionTok) +#else +#define IGNORE_SECTION_TOK_VTABLE /* as nothing */ +#endif + +#define VTABLE1 \ + { PREFIX(prologTok), PREFIX(contentTok), \ + PREFIX(cdataSectionTok) IGNORE_SECTION_TOK_VTABLE }, \ + { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \ + PREFIX(sameName), \ + PREFIX(nameMatchesAscii), \ + PREFIX(nameLength), \ + PREFIX(skipS), \ + PREFIX(getAtts), \ + PREFIX(charRefNumber), \ + PREFIX(predefinedEntityName), \ + PREFIX(updatePosition), \ + PREFIX(isPublicId) + +#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16) + +#define UCS2_GET_NAMING(pages, hi, lo) \ + (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1u << ((lo) & 0x1F))) + +/* A 2 byte UTF-8 representation splits the characters 11 bits between + the bottom 5 and 6 bits of the bytes. We need 8 bits to index into + pages, 3 bits to add to that index and 5 bits to generate the mask. +*/ +#define UTF8_GET_NAMING2(pages, byte) \ + (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \ + + ((((byte)[0]) & 3) << 1) \ + + ((((byte)[1]) >> 5) & 1)] \ + & (1u << (((byte)[1]) & 0x1F))) + +/* A 3 byte UTF-8 representation splits the characters 16 bits between + the bottom 4, 6 and 6 bits of the bytes. We need 8 bits to index + into pages, 3 bits to add to that index and 5 bits to generate the + mask. +*/ +#define UTF8_GET_NAMING3(pages, byte) \ + (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \ + + ((((byte)[1]) >> 2) & 0xF)] \ + << 3) \ + + ((((byte)[1]) & 3) << 1) \ + + ((((byte)[2]) >> 5) & 1)] \ + & (1u << (((byte)[2]) & 0x1F))) + +#define UTF8_GET_NAMING(pages, p, n) \ + ((n) == 2 \ + ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \ + : ((n) == 3 \ + ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \ + : 0)) + +/* Detection of invalid UTF-8 sequences is based on Table 3.1B + of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/ + with the additional restriction of not allowing the Unicode + code points 0xFFFF and 0xFFFE (sequences EF,BF,BF and EF,BF,BE). + Implementation details: + (A & 0x80) == 0 means A < 0x80 + and + (A & 0xC0) == 0xC0 means A > 0xBF +*/ + +#define UTF8_INVALID2(p) \ + ((*p) < 0xC2 || ((p)[1] & 0x80) == 0 || ((p)[1] & 0xC0) == 0xC0) + +#define UTF8_INVALID3(p) \ + (((p)[2] & 0x80) == 0 \ + || \ + ((*p) == 0xEF && (p)[1] == 0xBF \ + ? \ + (p)[2] > 0xBD \ + : \ + ((p)[2] & 0xC0) == 0xC0) \ + || \ + ((*p) == 0xE0 \ + ? \ + (p)[1] < 0xA0 || ((p)[1] & 0xC0) == 0xC0 \ + : \ + ((p)[1] & 0x80) == 0 \ + || \ + ((*p) == 0xED ? (p)[1] > 0x9F : ((p)[1] & 0xC0) == 0xC0))) + +#define UTF8_INVALID4(p) \ + (((p)[3] & 0x80) == 0 || ((p)[3] & 0xC0) == 0xC0 \ + || \ + ((p)[2] & 0x80) == 0 || ((p)[2] & 0xC0) == 0xC0 \ + || \ + ((*p) == 0xF0 \ + ? \ + (p)[1] < 0x90 || ((p)[1] & 0xC0) == 0xC0 \ + : \ + ((p)[1] & 0x80) == 0 \ + || \ + ((*p) == 0xF4 ? (p)[1] > 0x8F : ((p)[1] & 0xC0) == 0xC0))) + +static int PTRFASTCALL +isNever(const ENCODING *UNUSED_P(enc), const char *UNUSED_P(p)) +{ + return 0; +} + +static int PTRFASTCALL +utf8_isName2(const ENCODING *UNUSED_P(enc), const char *p) +{ + return UTF8_GET_NAMING2(namePages, (const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isName3(const ENCODING *UNUSED_P(enc), const char *p) +{ + return UTF8_GET_NAMING3(namePages, (const unsigned char *)p); +} + +#define utf8_isName4 isNever + +static int PTRFASTCALL +utf8_isNmstrt2(const ENCODING *UNUSED_P(enc), const char *p) +{ + return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isNmstrt3(const ENCODING *UNUSED_P(enc), const char *p) +{ + return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p); +} + +#define utf8_isNmstrt4 isNever + +static int PTRFASTCALL +utf8_isInvalid2(const ENCODING *UNUSED_P(enc), const char *p) +{ + return UTF8_INVALID2((const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isInvalid3(const ENCODING *UNUSED_P(enc), const char *p) +{ + return UTF8_INVALID3((const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isInvalid4(const ENCODING *UNUSED_P(enc), const char *p) +{ + return UTF8_INVALID4((const unsigned char *)p); +} + +struct normal_encoding { + ENCODING enc; + unsigned char type[256]; +#ifdef XML_MIN_SIZE + int (PTRFASTCALL *byteType)(const ENCODING *, const char *); + int (PTRFASTCALL *isNameMin)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrtMin)(const ENCODING *, const char *); + int (PTRFASTCALL *byteToAscii)(const ENCODING *, const char *); + int (PTRCALL *charMatches)(const ENCODING *, const char *, int); +#endif /* XML_MIN_SIZE */ + int (PTRFASTCALL *isName2)(const ENCODING *, const char *); + int (PTRFASTCALL *isName3)(const ENCODING *, const char *); + int (PTRFASTCALL *isName4)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt2)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt3)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt4)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid2)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid3)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid4)(const ENCODING *, const char *); +}; + +#define AS_NORMAL_ENCODING(enc) ((const struct normal_encoding *) (enc)) + +#ifdef XML_MIN_SIZE + +#define STANDARD_VTABLE(E) \ + E ## byteType, \ + E ## isNameMin, \ + E ## isNmstrtMin, \ + E ## byteToAscii, \ + E ## charMatches, + +#else + +#define STANDARD_VTABLE(E) /* as nothing */ + +#endif + +#define NORMAL_VTABLE(E) \ + E ## isName2, \ + E ## isName3, \ + E ## isName4, \ + E ## isNmstrt2, \ + E ## isNmstrt3, \ + E ## isNmstrt4, \ + E ## isInvalid2, \ + E ## isInvalid3, \ + E ## isInvalid4 + +#define NULL_VTABLE \ + /* isName2 */ NULL, \ + /* isName3 */ NULL, \ + /* isName4 */ NULL, \ + /* isNmstrt2 */ NULL, \ + /* isNmstrt3 */ NULL, \ + /* isNmstrt4 */ NULL, \ + /* isInvalid2 */ NULL, \ + /* isInvalid3 */ NULL, \ + /* isInvalid4 */ NULL + +static int FASTCALL checkCharRefNumber(int); + +#include "xmltok_impl.h" +#include "ascii.h" + +#ifdef XML_MIN_SIZE +#define sb_isNameMin isNever +#define sb_isNmstrtMin isNever +#endif + +#ifdef XML_MIN_SIZE +#define MINBPC(enc) ((enc)->minBytesPerChar) +#else +/* minimum bytes per character */ +#define MINBPC(enc) 1 +#endif + +#define SB_BYTE_TYPE(enc, p) \ + (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) + +#ifdef XML_MIN_SIZE +static int PTRFASTCALL +sb_byteType(const ENCODING *enc, const char *p) +{ + return SB_BYTE_TYPE(enc, p); +} +#define BYTE_TYPE(enc, p) \ + (AS_NORMAL_ENCODING(enc)->byteType(enc, p)) +#else +#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p) +#endif + +#ifdef XML_MIN_SIZE +#define BYTE_TO_ASCII(enc, p) \ + (AS_NORMAL_ENCODING(enc)->byteToAscii(enc, p)) +static int PTRFASTCALL +sb_byteToAscii(const ENCODING *enc, const char *p) +{ + return *p; +} +#else +#define BYTE_TO_ASCII(enc, p) (*(p)) +#endif + +#define IS_NAME_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isName ## n(enc, p)) +#define IS_NMSTRT_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isNmstrt ## n(enc, p)) +#define IS_INVALID_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isInvalid ## n(enc, p)) + +#ifdef XML_MIN_SIZE +#define IS_NAME_CHAR_MINBPC(enc, p) \ + (AS_NORMAL_ENCODING(enc)->isNameMin(enc, p)) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) \ + (AS_NORMAL_ENCODING(enc)->isNmstrtMin(enc, p)) +#else +#define IS_NAME_CHAR_MINBPC(enc, p) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0) +#endif + +#ifdef XML_MIN_SIZE +#define CHAR_MATCHES(enc, p, c) \ + (AS_NORMAL_ENCODING(enc)->charMatches(enc, p, c)) +static int PTRCALL +sb_charMatches(const ENCODING *enc, const char *p, int c) +{ + return *p == c; +} +#else +/* c is an ASCII character */ +#define CHAR_MATCHES(enc, p, c) (*(p) == c) +#endif + +#define PREFIX(ident) normal_ ## ident +#define XML_TOK_IMPL_C +#include "xmltok_impl.inc" +#undef XML_TOK_IMPL_C + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */ + UTF8_cval1 = 0x00, + UTF8_cval2 = 0xc0, + UTF8_cval3 = 0xe0, + UTF8_cval4 = 0xf0 +}; + +void +align_limit_to_full_utf8_characters(const char * from, const char ** fromLimRef) +{ + const char * fromLim = *fromLimRef; + size_t walked = 0; + for (; fromLim > from; fromLim--, walked++) { + const unsigned char prev = (unsigned char)fromLim[-1]; + if ((prev & 0xf8u) == 0xf0u) { /* 4-byte character, lead by 0b11110xxx byte */ + if (walked + 1 >= 4) { + fromLim += 4 - 1; + break; + } else { + walked = 0; + } + } else if ((prev & 0xf0u) == 0xe0u) { /* 3-byte character, lead by 0b1110xxxx byte */ + if (walked + 1 >= 3) { + fromLim += 3 - 1; + break; + } else { + walked = 0; + } + } else if ((prev & 0xe0u) == 0xc0u) { /* 2-byte character, lead by 0b110xxxxx byte */ + if (walked + 1 >= 2) { + fromLim += 2 - 1; + break; + } else { + walked = 0; + } + } else if ((prev & 0x80u) == 0x00u) { /* 1-byte character, matching 0b0xxxxxxx */ + break; + } + } + *fromLimRef = fromLim; +} + +static enum XML_Convert_Result PTRCALL +utf8_toUtf8(const ENCODING *UNUSED_P(enc), + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + enum XML_Convert_Result res = XML_CONVERT_COMPLETED; + char *to; + const char *from; + if (fromLim - *fromP > toLim - *toP) { + /* Avoid copying partial characters. */ + res = XML_CONVERT_OUTPUT_EXHAUSTED; + fromLim = *fromP + (toLim - *toP); + align_limit_to_full_utf8_characters(*fromP, &fromLim); + } + for (to = *toP, from = *fromP; (from < fromLim) && (to < toLim); from++, to++) + *to = *from; + *fromP = from; + *toP = to; + + if ((to == toLim) && (from < fromLim)) + return XML_CONVERT_OUTPUT_EXHAUSTED; + else + return res; +} + +static enum XML_Convert_Result PTRCALL +utf8_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + enum XML_Convert_Result res = XML_CONVERT_COMPLETED; + unsigned short *to = *toP; + const char *from = *fromP; + while (from < fromLim && to < toLim) { + switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) { + case BT_LEAD2: + if (fromLim - from < 2) { + res = XML_CONVERT_INPUT_INCOMPLETE; + break; + } + *to++ = (unsigned short)(((from[0] & 0x1f) << 6) | (from[1] & 0x3f)); + from += 2; + break; + case BT_LEAD3: + if (fromLim - from < 3) { + res = XML_CONVERT_INPUT_INCOMPLETE; + break; + } + *to++ = (unsigned short)(((from[0] & 0xf) << 12) + | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f)); + from += 3; + break; + case BT_LEAD4: + { + unsigned long n; + if (toLim - to < 2) { + res = XML_CONVERT_OUTPUT_EXHAUSTED; + goto after; + } + if (fromLim - from < 4) { + res = XML_CONVERT_INPUT_INCOMPLETE; + goto after; + } + n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12) + | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f); + n -= 0x10000; + to[0] = (unsigned short)((n >> 10) | 0xD800); + to[1] = (unsigned short)((n & 0x3FF) | 0xDC00); + to += 2; + from += 4; + } + break; + default: + *to++ = *from++; + break; + } + } +after: + *fromP = from; + *toP = to; + return res; +} + +#ifdef XML_NS +static const struct normal_encoding utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; +#endif + +static const struct normal_encoding utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +#ifdef XML_NS + +static const struct normal_encoding internal_utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "iasciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +#endif + +static const struct normal_encoding internal_utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +static enum XML_Convert_Result PTRCALL +latin1_toUtf8(const ENCODING *UNUSED_P(enc), + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + for (;;) { + unsigned char c; + if (*fromP == fromLim) + return XML_CONVERT_COMPLETED; + c = (unsigned char)**fromP; + if (c & 0x80) { + if (toLim - *toP < 2) + return XML_CONVERT_OUTPUT_EXHAUSTED; + *(*toP)++ = (char)((c >> 6) | UTF8_cval2); + *(*toP)++ = (char)((c & 0x3f) | 0x80); + (*fromP)++; + } + else { + if (*toP == toLim) + return XML_CONVERT_OUTPUT_EXHAUSTED; + *(*toP)++ = *(*fromP)++; + } + } +} + +static enum XML_Convert_Result PTRCALL +latin1_toUtf16(const ENCODING *UNUSED_P(enc), + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + while (*fromP < fromLim && *toP < toLim) + *(*toP)++ = (unsigned char)*(*fromP)++; + + if ((*toP == toLim) && (*fromP < fromLim)) + return XML_CONVERT_OUTPUT_EXHAUSTED; + else + return XML_CONVERT_COMPLETED; +} + +#ifdef XML_NS + +static const struct normal_encoding latin1_encoding_ns = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) NULL_VTABLE +}; + +#endif + +static const struct normal_encoding latin1_encoding = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) NULL_VTABLE +}; + +static enum XML_Convert_Result PTRCALL +ascii_toUtf8(const ENCODING *UNUSED_P(enc), + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + while (*fromP < fromLim && *toP < toLim) + *(*toP)++ = *(*fromP)++; + + if ((*toP == toLim) && (*fromP < fromLim)) + return XML_CONVERT_OUTPUT_EXHAUSTED; + else + return XML_CONVERT_COMPLETED; +} + +#ifdef XML_NS + +static const struct normal_encoding ascii_encoding_ns = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +/* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) NULL_VTABLE +}; + +#endif + +static const struct normal_encoding ascii_encoding = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +/* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) NULL_VTABLE +}; + +static int PTRFASTCALL +unicode_byte_type(char hi, char lo) +{ + switch ((unsigned char)hi) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + return BT_LEAD4; + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return BT_TRAIL; + case 0xFF: + switch ((unsigned char)lo) { + case 0xFF: + case 0xFE: + return BT_NONXML; + } + break; + } + return BT_NONASCII; +} + +#define DEFINE_UTF16_TO_UTF8(E) \ +static enum XML_Convert_Result PTRCALL \ +E ## toUtf8(const ENCODING *UNUSED_P(enc), \ + const char **fromP, const char *fromLim, \ + char **toP, const char *toLim) \ +{ \ + const char *from = *fromP; \ + fromLim = from + (((fromLim - from) >> 1) << 1); /* shrink to even */ \ + for (; from < fromLim; from += 2) { \ + int plane; \ + unsigned char lo2; \ + unsigned char lo = GET_LO(from); \ + unsigned char hi = GET_HI(from); \ + switch (hi) { \ + case 0: \ + if (lo < 0x80) { \ + if (*toP == toLim) { \ + *fromP = from; \ + return XML_CONVERT_OUTPUT_EXHAUSTED; \ + } \ + *(*toP)++ = lo; \ + break; \ + } \ + /* fall through */ \ + case 0x1: case 0x2: case 0x3: \ + case 0x4: case 0x5: case 0x6: case 0x7: \ + if (toLim - *toP < 2) { \ + *fromP = from; \ + return XML_CONVERT_OUTPUT_EXHAUSTED; \ + } \ + *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + default: \ + if (toLim - *toP < 3) { \ + *fromP = from; \ + return XML_CONVERT_OUTPUT_EXHAUSTED; \ + } \ + /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \ + *(*toP)++ = ((hi >> 4) | UTF8_cval3); \ + *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + case 0xD8: case 0xD9: case 0xDA: case 0xDB: \ + if (toLim - *toP < 4) { \ + *fromP = from; \ + return XML_CONVERT_OUTPUT_EXHAUSTED; \ + } \ + if (fromLim - from < 4) { \ + *fromP = from; \ + return XML_CONVERT_INPUT_INCOMPLETE; \ + } \ + plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \ + *(*toP)++ = ((plane >> 2) | UTF8_cval4); \ + *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \ + from += 2; \ + lo2 = GET_LO(from); \ + *(*toP)++ = (((lo & 0x3) << 4) \ + | ((GET_HI(from) & 0x3) << 2) \ + | (lo2 >> 6) \ + | 0x80); \ + *(*toP)++ = ((lo2 & 0x3f) | 0x80); \ + break; \ + } \ + } \ + *fromP = from; \ + if (from < fromLim) \ + return XML_CONVERT_INPUT_INCOMPLETE; \ + else \ + return XML_CONVERT_COMPLETED; \ +} + +#define DEFINE_UTF16_TO_UTF16(E) \ +static enum XML_Convert_Result PTRCALL \ +E ## toUtf16(const ENCODING *UNUSED_P(enc), \ + const char **fromP, const char *fromLim, \ + unsigned short **toP, const unsigned short *toLim) \ +{ \ + enum XML_Convert_Result res = XML_CONVERT_COMPLETED; \ + fromLim = *fromP + (((fromLim - *fromP) >> 1) << 1); /* shrink to even */ \ + /* Avoid copying first half only of surrogate */ \ + if (fromLim - *fromP > ((toLim - *toP) << 1) \ + && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) { \ + fromLim -= 2; \ + res = XML_CONVERT_INPUT_INCOMPLETE; \ + } \ + for (; *fromP < fromLim && *toP < toLim; *fromP += 2) \ + *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \ + if ((*toP == toLim) && (*fromP < fromLim)) \ + return XML_CONVERT_OUTPUT_EXHAUSTED; \ + else \ + return res; \ +} + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8))) +#define GET_LO(ptr) ((unsigned char)(ptr)[0]) +#define GET_HI(ptr) ((unsigned char)(ptr)[1]) + +DEFINE_UTF16_TO_UTF8(little2_) +DEFINE_UTF16_TO_UTF16(little2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF))) +#define GET_LO(ptr) ((unsigned char)(ptr)[1]) +#define GET_HI(ptr) ((unsigned char)(ptr)[0]) + +DEFINE_UTF16_TO_UTF8(big2_) +DEFINE_UTF16_TO_UTF16(big2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define LITTLE2_BYTE_TYPE(enc, p) \ + ((p)[1] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \ + : unicode_byte_type((p)[1], (p)[0])) +#define LITTLE2_BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1) +#define LITTLE2_CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c) +#define LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0]) +#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0]) + +#ifdef XML_MIN_SIZE + +static int PTRFASTCALL +little2_byteType(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TYPE(enc, p); +} + +static int PTRFASTCALL +little2_byteToAscii(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TO_ASCII(enc, p); +} + +static int PTRCALL +little2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return LITTLE2_CHAR_MATCHES(enc, p, c); +} + +static int PTRFASTCALL +little2_isNameMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static int PTRFASTCALL +little2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) little2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#define XML_TOK_IMPL_C +#include "xmltok_impl.inc" +#undef XML_TOK_IMPL_C + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding little2_encoding_ns = { + { VTABLE, 2, 0, +#if BYTEORDER == 1234 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) NULL_VTABLE +}; + +#endif + +static const struct normal_encoding little2_encoding = { + { VTABLE, 2, 0, +#if BYTEORDER == 1234 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) NULL_VTABLE +}; + +#if BYTEORDER != 4321 + +#ifdef XML_NS + +static const struct normal_encoding internal_little2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) NULL_VTABLE +}; + +#endif + +static const struct normal_encoding internal_little2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) NULL_VTABLE +}; + +#endif + + +#define BIG2_BYTE_TYPE(enc, p) \ + ((p)[0] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \ + : unicode_byte_type((p)[0], (p)[1])) +#define BIG2_BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1) +#define BIG2_CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c) +#define BIG2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1]) +#define BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1]) + +#ifdef XML_MIN_SIZE + +static int PTRFASTCALL +big2_byteType(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TYPE(enc, p); +} + +static int PTRFASTCALL +big2_byteToAscii(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TO_ASCII(enc, p); +} + +static int PTRCALL +big2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return BIG2_CHAR_MATCHES(enc, p, c); +} + +static int PTRFASTCALL +big2_isNameMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static int PTRFASTCALL +big2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) big2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#define XML_TOK_IMPL_C +#include "xmltok_impl.inc" +#undef XML_TOK_IMPL_C + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding big2_encoding_ns = { + { VTABLE, 2, 0, +#if BYTEORDER == 4321 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) NULL_VTABLE +}; + +#endif + +static const struct normal_encoding big2_encoding = { + { VTABLE, 2, 0, +#if BYTEORDER == 4321 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) NULL_VTABLE +}; + +#if BYTEORDER != 1234 + +#ifdef XML_NS + +static const struct normal_encoding internal_big2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) NULL_VTABLE +}; + +#endif + +static const struct normal_encoding internal_big2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) NULL_VTABLE +}; + +#endif + +#undef PREFIX + +static int FASTCALL +streqci(const char *s1, const char *s2) +{ + for (;;) { + char c1 = *s1++; + char c2 = *s2++; + if (ASCII_a <= c1 && c1 <= ASCII_z) + c1 += ASCII_A - ASCII_a; + if (ASCII_a <= c2 && c2 <= ASCII_z) + c2 += ASCII_A - ASCII_a; + if (c1 != c2) + return 0; + if (!c1) + break; + } + return 1; +} + +static void PTRCALL +initUpdatePosition(const ENCODING *UNUSED_P(enc), const char *ptr, + const char *end, POSITION *pos) +{ + normal_updatePosition(&utf8_encoding.enc, ptr, end, pos); +} + +static int +toAscii(const ENCODING *enc, const char *ptr, const char *end) +{ + char buf[1]; + char *p = buf; + XmlUtf8Convert(enc, &ptr, end, &p, p + 1); + if (p == buf) + return -1; + else + return buf[0]; +} + +static int FASTCALL +isSpace(int c) +{ + switch (c) { + case 0x20: + case 0xD: + case 0xA: + case 0x9: + return 1; + } + return 0; +} + +/* Return 1 if there's just optional white space or there's an S + followed by name=val. +*/ +static int +parsePseudoAttribute(const ENCODING *enc, + const char *ptr, + const char *end, + const char **namePtr, + const char **nameEndPtr, + const char **valPtr, + const char **nextTokPtr) +{ + int c; + char open; + if (ptr == end) { + *namePtr = NULL; + return 1; + } + if (!isSpace(toAscii(enc, ptr, end))) { + *nextTokPtr = ptr; + return 0; + } + do { + ptr += enc->minBytesPerChar; + } while (isSpace(toAscii(enc, ptr, end))); + if (ptr == end) { + *namePtr = NULL; + return 1; + } + *namePtr = ptr; + for (;;) { + c = toAscii(enc, ptr, end); + if (c == -1) { + *nextTokPtr = ptr; + return 0; + } + if (c == ASCII_EQUALS) { + *nameEndPtr = ptr; + break; + } + if (isSpace(c)) { + *nameEndPtr = ptr; + do { + ptr += enc->minBytesPerChar; + } while (isSpace(c = toAscii(enc, ptr, end))); + if (c != ASCII_EQUALS) { + *nextTokPtr = ptr; + return 0; + } + break; + } + ptr += enc->minBytesPerChar; + } + if (ptr == *namePtr) { + *nextTokPtr = ptr; + return 0; + } + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + while (isSpace(c)) { + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + } + if (c != ASCII_QUOT && c != ASCII_APOS) { + *nextTokPtr = ptr; + return 0; + } + open = (char)c; + ptr += enc->minBytesPerChar; + *valPtr = ptr; + for (;; ptr += enc->minBytesPerChar) { + c = toAscii(enc, ptr, end); + if (c == open) + break; + if (!(ASCII_a <= c && c <= ASCII_z) + && !(ASCII_A <= c && c <= ASCII_Z) + && !(ASCII_0 <= c && c <= ASCII_9) + && c != ASCII_PERIOD + && c != ASCII_MINUS + && c != ASCII_UNDERSCORE) { + *nextTokPtr = ptr; + return 0; + } + } + *nextTokPtr = ptr + enc->minBytesPerChar; + return 1; +} + +static const char KW_version[] = { + ASCII_v, ASCII_e, ASCII_r, ASCII_s, ASCII_i, ASCII_o, ASCII_n, '\0' +}; + +static const char KW_encoding[] = { + ASCII_e, ASCII_n, ASCII_c, ASCII_o, ASCII_d, ASCII_i, ASCII_n, ASCII_g, '\0' +}; + +static const char KW_standalone[] = { + ASCII_s, ASCII_t, ASCII_a, ASCII_n, ASCII_d, ASCII_a, ASCII_l, ASCII_o, + ASCII_n, ASCII_e, '\0' +}; + +static const char KW_yes[] = { + ASCII_y, ASCII_e, ASCII_s, '\0' +}; + +static const char KW_no[] = { + ASCII_n, ASCII_o, '\0' +}; + +static int +doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, + const char *, + const char *), + int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + const char *val = NULL; + const char *name = NULL; + const char *nameEnd = NULL; + ptr += 5 * enc->minBytesPerChar; + end -= 2 * enc->minBytesPerChar; + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr) + || !name) { + *badPtr = ptr; + return 0; + } + if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_version)) { + if (!isGeneralTextEntity) { + *badPtr = name; + return 0; + } + } + else { + if (versionPtr) + *versionPtr = val; + if (versionEndPtr) + *versionEndPtr = ptr; + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) { + if (isGeneralTextEntity) { + /* a TextDecl must have an EncodingDecl */ + *badPtr = ptr; + return 0; + } + return 1; + } + } + if (XmlNameMatchesAscii(enc, name, nameEnd, KW_encoding)) { + int c = toAscii(enc, val, end); + if (!(ASCII_a <= c && c <= ASCII_z) && !(ASCII_A <= c && c <= ASCII_Z)) { + *badPtr = val; + return 0; + } + if (encodingName) + *encodingName = val; + if (encoding) + *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar); + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) + return 1; + } + if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_standalone) + || isGeneralTextEntity) { + *badPtr = name; + return 0; + } + if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_yes)) { + if (standalone) + *standalone = 1; + } + else if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_no)) { + if (standalone) + *standalone = 0; + } + else { + *badPtr = val; + return 0; + } + while (isSpace(toAscii(enc, ptr, end))) + ptr += enc->minBytesPerChar; + if (ptr != end) { + *badPtr = ptr; + return 0; + } + return 1; +} + +static int FASTCALL +checkCharRefNumber(int result) +{ + switch (result >> 8) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return -1; + case 0: + if (latin1_encoding.type[result] == BT_NONXML) + return -1; + break; + case 0xFF: + if (result == 0xFFFE || result == 0xFFFF) + return -1; + break; + } + return result; +} + +int FASTCALL +XmlUtf8Encode(int c, char *buf) +{ + enum { + /* minN is minimum legal resulting value for N byte sequence */ + min2 = 0x80, + min3 = 0x800, + min4 = 0x10000 + }; + + if (c < 0) + return 0; + if (c < min2) { + buf[0] = (char)(c | UTF8_cval1); + return 1; + } + if (c < min3) { + buf[0] = (char)((c >> 6) | UTF8_cval2); + buf[1] = (char)((c & 0x3f) | 0x80); + return 2; + } + if (c < min4) { + buf[0] = (char)((c >> 12) | UTF8_cval3); + buf[1] = (char)(((c >> 6) & 0x3f) | 0x80); + buf[2] = (char)((c & 0x3f) | 0x80); + return 3; + } + if (c < 0x110000) { + buf[0] = (char)((c >> 18) | UTF8_cval4); + buf[1] = (char)(((c >> 12) & 0x3f) | 0x80); + buf[2] = (char)(((c >> 6) & 0x3f) | 0x80); + buf[3] = (char)((c & 0x3f) | 0x80); + return 4; + } + return 0; +} + +int FASTCALL +XmlUtf16Encode(int charNum, unsigned short *buf) +{ + if (charNum < 0) + return 0; + if (charNum < 0x10000) { + buf[0] = (unsigned short)charNum; + return 1; + } + if (charNum < 0x110000) { + charNum -= 0x10000; + buf[0] = (unsigned short)((charNum >> 10) + 0xD800); + buf[1] = (unsigned short)((charNum & 0x3FF) + 0xDC00); + return 2; + } + return 0; +} + +struct unknown_encoding { + struct normal_encoding normal; + CONVERTER convert; + void *userData; + unsigned short utf16[256]; + char utf8[256][4]; +}; + +#define AS_UNKNOWN_ENCODING(enc) ((const struct unknown_encoding *) (enc)) + +int +XmlSizeOfUnknownEncoding(void) +{ + return sizeof(struct unknown_encoding); +} + +static int PTRFASTCALL +unknown_isName(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF); +} + +static int PTRFASTCALL +unknown_isNmstrt(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF); +} + +static int PTRFASTCALL +unknown_isInvalid(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + return (c & ~0xFFFF) || checkCharRefNumber(c) < 0; +} + +static enum XML_Convert_Result PTRCALL +unknown_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + char buf[XML_UTF8_ENCODE_MAX]; + for (;;) { + const char *utf8; + int n; + if (*fromP == fromLim) + return XML_CONVERT_COMPLETED; + utf8 = uenc->utf8[(unsigned char)**fromP]; + n = *utf8++; + if (n == 0) { + int c = uenc->convert(uenc->userData, *fromP); + n = XmlUtf8Encode(c, buf); + if (n > toLim - *toP) + return XML_CONVERT_OUTPUT_EXHAUSTED; + utf8 = buf; + *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2)); + } + else { + if (n > toLim - *toP) + return XML_CONVERT_OUTPUT_EXHAUSTED; + (*fromP)++; + } + do { + *(*toP)++ = *utf8++; + } while (--n != 0); + } +} + +static enum XML_Convert_Result PTRCALL +unknown_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + while (*fromP < fromLim && *toP < toLim) { + unsigned short c = uenc->utf16[(unsigned char)**fromP]; + if (c == 0) { + c = (unsigned short) + uenc->convert(uenc->userData, *fromP); + *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2)); + } + else + (*fromP)++; + *(*toP)++ = c; + } + + if ((*toP == toLim) && (*fromP < fromLim)) + return XML_CONVERT_OUTPUT_EXHAUSTED; + else + return XML_CONVERT_COMPLETED; +} + +ENCODING * +XmlInitUnknownEncoding(void *mem, + int *table, + CONVERTER convert, + void *userData) +{ + int i; + struct unknown_encoding *e = (struct unknown_encoding *)mem; + for (i = 0; i < (int)sizeof(struct normal_encoding); i++) + ((char *)mem)[i] = ((char *)&latin1_encoding)[i]; + for (i = 0; i < 128; i++) + if (latin1_encoding.type[i] != BT_OTHER + && latin1_encoding.type[i] != BT_NONXML + && table[i] != i) + return 0; + for (i = 0; i < 256; i++) { + int c = table[i]; + if (c == -1) { + e->normal.type[i] = BT_MALFORM; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else if (c < 0) { + if (c < -4) + return 0; + e->normal.type[i] = (unsigned char)(BT_LEAD2 - (c + 2)); + e->utf8[i][0] = 0; + e->utf16[i] = 0; + } + else if (c < 0x80) { + if (latin1_encoding.type[c] != BT_OTHER + && latin1_encoding.type[c] != BT_NONXML + && c != i) + return 0; + e->normal.type[i] = latin1_encoding.type[c]; + e->utf8[i][0] = 1; + e->utf8[i][1] = (char)c; + e->utf16[i] = (unsigned short)(c == 0 ? 0xFFFF : c); + } + else if (checkCharRefNumber(c) < 0) { + e->normal.type[i] = BT_NONXML; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else { + if (c > 0xFFFF) + return 0; + if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NMSTRT; + else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NAME; + else + e->normal.type[i] = BT_OTHER; + e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1); + e->utf16[i] = (unsigned short)c; + } + } + e->userData = userData; + e->convert = convert; + if (convert) { + e->normal.isName2 = unknown_isName; + e->normal.isName3 = unknown_isName; + e->normal.isName4 = unknown_isName; + e->normal.isNmstrt2 = unknown_isNmstrt; + e->normal.isNmstrt3 = unknown_isNmstrt; + e->normal.isNmstrt4 = unknown_isNmstrt; + e->normal.isInvalid2 = unknown_isInvalid; + e->normal.isInvalid3 = unknown_isInvalid; + e->normal.isInvalid4 = unknown_isInvalid; + } + e->normal.enc.utf8Convert = unknown_toUtf8; + e->normal.enc.utf16Convert = unknown_toUtf16; + return &(e->normal.enc); +} + +/* If this enumeration is changed, getEncodingIndex and encodings +must also be changed. */ +enum { + UNKNOWN_ENC = -1, + ISO_8859_1_ENC = 0, + US_ASCII_ENC, + UTF_8_ENC, + UTF_16_ENC, + UTF_16BE_ENC, + UTF_16LE_ENC, + /* must match encodingNames up to here */ + NO_ENC +}; + +static const char KW_ISO_8859_1[] = { + ASCII_I, ASCII_S, ASCII_O, ASCII_MINUS, ASCII_8, ASCII_8, ASCII_5, ASCII_9, + ASCII_MINUS, ASCII_1, '\0' +}; +static const char KW_US_ASCII[] = { + ASCII_U, ASCII_S, ASCII_MINUS, ASCII_A, ASCII_S, ASCII_C, ASCII_I, ASCII_I, + '\0' +}; +static const char KW_UTF_8[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_8, '\0' +}; +static const char KW_UTF_16[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, '\0' +}; +static const char KW_UTF_16BE[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_B, ASCII_E, + '\0' +}; +static const char KW_UTF_16LE[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_L, ASCII_E, + '\0' +}; + +static int FASTCALL +getEncodingIndex(const char *name) +{ + static const char * const encodingNames[] = { + KW_ISO_8859_1, + KW_US_ASCII, + KW_UTF_8, + KW_UTF_16, + KW_UTF_16BE, + KW_UTF_16LE, + }; + int i; + if (name == NULL) + return NO_ENC; + for (i = 0; i < (int)(sizeof(encodingNames)/sizeof(encodingNames[0])); i++) + if (streqci(name, encodingNames[i])) + return i; + return UNKNOWN_ENC; +} + +/* For binary compatibility, we store the index of the encoding + specified at initialization in the isUtf16 member. +*/ + +#define INIT_ENC_INDEX(enc) ((int)(enc)->initEnc.isUtf16) +#define SET_INIT_ENC_INDEX(enc, i) ((enc)->initEnc.isUtf16 = (char)i) + +/* This is what detects the encoding. encodingTable maps from + encoding indices to encodings; INIT_ENC_INDEX(enc) is the index of + the external (protocol) specified encoding; state is + XML_CONTENT_STATE if we're parsing an external text entity, and + XML_PROLOG_STATE otherwise. +*/ + + +static int +initScan(const ENCODING * const *encodingTable, + const INIT_ENCODING *enc, + int state, + const char *ptr, + const char *end, + const char **nextTokPtr) +{ + const ENCODING **encPtr; + + if (ptr >= end) + return XML_TOK_NONE; + encPtr = enc->encPtr; + if (ptr + 1 == end) { + /* only a single byte available for auto-detection */ +#ifndef XML_DTD /* FIXME */ + /* a well-formed document entity must have more than one byte */ + if (state != XML_CONTENT_STATE) + return XML_TOK_PARTIAL; +#endif + /* so we're parsing an external text entity... */ + /* if UTF-16 was externally specified, then we need at least 2 bytes */ + switch (INIT_ENC_INDEX(enc)) { + case UTF_16_ENC: + case UTF_16LE_ENC: + case UTF_16BE_ENC: + return XML_TOK_PARTIAL; + } + switch ((unsigned char)*ptr) { + case 0xFE: + case 0xFF: + case 0xEF: /* possibly first byte of UTF-8 BOM */ + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + /* fall through */ + case 0x00: + case 0x3C: + return XML_TOK_PARTIAL; + } + } + else { + switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) { + case 0xFEFF: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XML_TOK_BOM; + /* 00 3C is handled in the default case */ + case 0x3C00: + if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC + || INIT_ENC_INDEX(enc) == UTF_16_ENC) + && state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + case 0xFFFE: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XML_TOK_BOM; + case 0xEFBB: + /* Maybe a UTF-8 BOM (EF BB BF) */ + /* If there's an explicitly specified (external) encoding + of ISO-8859-1 or some flavour of UTF-16 + and this is an external text entity, + don't look for the BOM, + because it might be a legal data. + */ + if (state == XML_CONTENT_STATE) { + int e = INIT_ENC_INDEX(enc); + if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC + || e == UTF_16LE_ENC || e == UTF_16_ENC) + break; + } + if (ptr + 2 == end) + return XML_TOK_PARTIAL; + if ((unsigned char)ptr[2] == 0xBF) { + *nextTokPtr = ptr + 3; + *encPtr = encodingTable[UTF_8_ENC]; + return XML_TOK_BOM; + } + break; + default: + if (ptr[0] == '\0') { + /* 0 isn't a legal data character. Furthermore a document + entity can only start with ASCII characters. So the only + way this can fail to be big-endian UTF-16 if it it's an + external parsed general entity that's labelled as + UTF-16LE. + */ + if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC) + break; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + else if (ptr[1] == '\0') { + /* We could recover here in the case: + - parsing an external entity + - second byte is 0 + - no externally specified encoding + - no encoding declaration + by assuming UTF-16LE. But we don't, because this would mean when + presented just with a single byte, we couldn't reliably determine + whether we needed further bytes. + */ + if (state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + break; + } + } + *encPtr = encodingTable[INIT_ENC_INDEX(enc)]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); +} + + +#define NS(x) x +#define ns(x) x +#define XML_TOK_NS_C +#include "xmltok_ns.inc" +#undef XML_TOK_NS_C +#undef NS +#undef ns + +#ifdef XML_NS + +#define NS(x) x ## NS +#define ns(x) x ## _ns + +#define XML_TOK_NS_C +#include "xmltok_ns.inc" +#undef XML_TOK_NS_C + +#undef NS +#undef ns + +ENCODING * +XmlInitUnknownEncodingNS(void *mem, + int *table, + CONVERTER convert, + void *userData) +{ + ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData); + if (enc) + ((struct normal_encoding *)enc)->type[ASCII_COLON] = BT_COLON; + return enc; +} + +#endif /* XML_NS */ diff --git a/deps/EXPAT/expat/xmltok.h b/deps/EXPAT/expat/xmltok.h new file mode 100644 index 0000000000..752007e8b9 --- /dev/null +++ b/deps/EXPAT/expat/xmltok.h @@ -0,0 +1,322 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef XmlTok_INCLUDED +#define XmlTok_INCLUDED 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* The following token may be returned by XmlContentTok */ +#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be + start of illegal ]]> sequence */ +/* The following tokens may be returned by both XmlPrologTok and + XmlContentTok. +*/ +#define XML_TOK_NONE -4 /* The string to be scanned is empty */ +#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan; + might be part of CRLF sequence */ +#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */ +#define XML_TOK_PARTIAL -1 /* only part of a token */ +#define XML_TOK_INVALID 0 + +/* The following tokens are returned by XmlContentTok; some are also + returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok. +*/ +#define XML_TOK_START_TAG_WITH_ATTS 1 +#define XML_TOK_START_TAG_NO_ATTS 2 +#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag */ +#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4 +#define XML_TOK_END_TAG 5 +#define XML_TOK_DATA_CHARS 6 +#define XML_TOK_DATA_NEWLINE 7 +#define XML_TOK_CDATA_SECT_OPEN 8 +#define XML_TOK_ENTITY_REF 9 +#define XML_TOK_CHAR_REF 10 /* numeric character reference */ + +/* The following tokens may be returned by both XmlPrologTok and + XmlContentTok. +*/ +#define XML_TOK_PI 11 /* processing instruction */ +#define XML_TOK_XML_DECL 12 /* XML decl or text decl */ +#define XML_TOK_COMMENT 13 +#define XML_TOK_BOM 14 /* Byte order mark */ + +/* The following tokens are returned only by XmlPrologTok */ +#define XML_TOK_PROLOG_S 15 +#define XML_TOK_DECL_OPEN 16 /* */ +#define XML_TOK_NAME 18 +#define XML_TOK_NMTOKEN 19 +#define XML_TOK_POUND_NAME 20 /* #name */ +#define XML_TOK_OR 21 /* | */ +#define XML_TOK_PERCENT 22 +#define XML_TOK_OPEN_PAREN 23 +#define XML_TOK_CLOSE_PAREN 24 +#define XML_TOK_OPEN_BRACKET 25 +#define XML_TOK_CLOSE_BRACKET 26 +#define XML_TOK_LITERAL 27 +#define XML_TOK_PARAM_ENTITY_REF 28 +#define XML_TOK_INSTANCE_START 29 + +/* The following occur only in element type declarations */ +#define XML_TOK_NAME_QUESTION 30 /* name? */ +#define XML_TOK_NAME_ASTERISK 31 /* name* */ +#define XML_TOK_NAME_PLUS 32 /* name+ */ +#define XML_TOK_COND_SECT_OPEN 33 /* */ +#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */ +#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */ +#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */ +#define XML_TOK_COMMA 38 + +/* The following token is returned only by XmlAttributeValueTok */ +#define XML_TOK_ATTRIBUTE_VALUE_S 39 + +/* The following token is returned only by XmlCdataSectionTok */ +#define XML_TOK_CDATA_SECT_CLOSE 40 + +/* With namespace processing this is returned by XmlPrologTok for a + name with a colon. +*/ +#define XML_TOK_PREFIXED_NAME 41 + +#ifdef XML_DTD +#define XML_TOK_IGNORE_SECT 42 +#endif /* XML_DTD */ + +#ifdef XML_DTD +#define XML_N_STATES 4 +#else /* not XML_DTD */ +#define XML_N_STATES 3 +#endif /* not XML_DTD */ + +#define XML_PROLOG_STATE 0 +#define XML_CONTENT_STATE 1 +#define XML_CDATA_SECTION_STATE 2 +#ifdef XML_DTD +#define XML_IGNORE_SECTION_STATE 3 +#endif /* XML_DTD */ + +#define XML_N_LITERAL_TYPES 2 +#define XML_ATTRIBUTE_VALUE_LITERAL 0 +#define XML_ENTITY_VALUE_LITERAL 1 + +/* The size of the buffer passed to XmlUtf8Encode must be at least this. */ +#define XML_UTF8_ENCODE_MAX 4 +/* The size of the buffer passed to XmlUtf16Encode must be at least this. */ +#define XML_UTF16_ENCODE_MAX 2 + +typedef struct position { + /* first line and first column are 0 not 1 */ + XML_Size lineNumber; + XML_Size columnNumber; +} POSITION; + +typedef struct { + const char *name; + const char *valuePtr; + const char *valueEnd; + char normalized; +} ATTRIBUTE; + +struct encoding; +typedef struct encoding ENCODING; + +typedef int (PTRCALL *SCANNER)(const ENCODING *, + const char *, + const char *, + const char **); + +enum XML_Convert_Result { + XML_CONVERT_COMPLETED = 0, + XML_CONVERT_INPUT_INCOMPLETE = 1, + XML_CONVERT_OUTPUT_EXHAUSTED = 2 /* and therefore potentially input remaining as well */ +}; + +struct encoding { + SCANNER scanners[XML_N_STATES]; + SCANNER literalScanners[XML_N_LITERAL_TYPES]; + int (PTRCALL *sameName)(const ENCODING *, + const char *, + const char *); + int (PTRCALL *nameMatchesAscii)(const ENCODING *, + const char *, + const char *, + const char *); + int (PTRFASTCALL *nameLength)(const ENCODING *, const char *); + const char *(PTRFASTCALL *skipS)(const ENCODING *, const char *); + int (PTRCALL *getAtts)(const ENCODING *enc, + const char *ptr, + int attsMax, + ATTRIBUTE *atts); + int (PTRFASTCALL *charRefNumber)(const ENCODING *enc, const char *ptr); + int (PTRCALL *predefinedEntityName)(const ENCODING *, + const char *, + const char *); + void (PTRCALL *updatePosition)(const ENCODING *, + const char *ptr, + const char *end, + POSITION *); + int (PTRCALL *isPublicId)(const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr); + enum XML_Convert_Result (PTRCALL *utf8Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + char **toP, + const char *toLim); + enum XML_Convert_Result (PTRCALL *utf16Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + unsigned short **toP, + const unsigned short *toLim); + int minBytesPerChar; + char isUtf8; + char isUtf16; +}; + +/* Scan the string starting at ptr until the end of the next complete + token, but do not scan past eptr. Return an integer giving the + type of token. + + Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set. + + Return XML_TOK_PARTIAL when the string does not contain a complete + token; nextTokPtr will not be set. + + Return XML_TOK_INVALID when the string does not start a valid + token; nextTokPtr will be set to point to the character which made + the token invalid. + + Otherwise the string starts with a valid token; nextTokPtr will be + set to point to the character following the end of that token. + + Each data character counts as a single token, but adjacent data + characters may be returned together. Similarly for characters in + the prolog outside literals, comments and processing instructions. +*/ + + +#define XmlTok(enc, state, ptr, end, nextTokPtr) \ + (((enc)->scanners[state])(enc, ptr, end, nextTokPtr)) + +#define XmlPrologTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr) + +#define XmlContentTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr) + +#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr) + +#ifdef XML_DTD + +#define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr) + +#endif /* XML_DTD */ + +/* This is used for performing a 2nd-level tokenization on the content + of a literal that has already been returned by XmlTok. +*/ +#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \ + (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr)) + +#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2)) + +#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \ + (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2)) + +#define XmlNameLength(enc, ptr) \ + (((enc)->nameLength)(enc, ptr)) + +#define XmlSkipS(enc, ptr) \ + (((enc)->skipS)(enc, ptr)) + +#define XmlGetAttributes(enc, ptr, attsMax, atts) \ + (((enc)->getAtts)(enc, ptr, attsMax, atts)) + +#define XmlCharRefNumber(enc, ptr) \ + (((enc)->charRefNumber)(enc, ptr)) + +#define XmlPredefinedEntityName(enc, ptr, end) \ + (((enc)->predefinedEntityName)(enc, ptr, end)) + +#define XmlUpdatePosition(enc, ptr, end, pos) \ + (((enc)->updatePosition)(enc, ptr, end, pos)) + +#define XmlIsPublicId(enc, ptr, end, badPtr) \ + (((enc)->isPublicId)(enc, ptr, end, badPtr)) + +#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim)) + +#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim)) + +typedef struct { + ENCODING initEnc; + const ENCODING **encPtr; +} INIT_ENCODING; + +int XmlParseXmlDecl(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); + +int XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING *XmlGetUtf8InternalEncoding(void); +const ENCODING *XmlGetUtf16InternalEncoding(void); +int FASTCALL XmlUtf8Encode(int charNumber, char *buf); +int FASTCALL XmlUtf16Encode(int charNumber, unsigned short *buf); +int XmlSizeOfUnknownEncoding(void); + + +typedef int (XMLCALL *CONVERTER) (void *userData, const char *p); + +ENCODING * +XmlInitUnknownEncoding(void *mem, + int *table, + CONVERTER convert, + void *userData); + +int XmlParseXmlDeclNS(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); + +int XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING *XmlGetUtf8InternalEncodingNS(void); +const ENCODING *XmlGetUtf16InternalEncodingNS(void); +ENCODING * +XmlInitUnknownEncodingNS(void *mem, + int *table, + CONVERTER convert, + void *userData); +#ifdef __cplusplus +} +#endif + +#endif /* not XmlTok_INCLUDED */ diff --git a/deps/EXPAT/expat/xmltok_impl.h b/deps/EXPAT/expat/xmltok_impl.h new file mode 100644 index 0000000000..da0ea60a65 --- /dev/null +++ b/deps/EXPAT/expat/xmltok_impl.h @@ -0,0 +1,46 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file COPYING for copying permission. +*/ + +enum { + BT_NONXML, + BT_MALFORM, + BT_LT, + BT_AMP, + BT_RSQB, + BT_LEAD2, + BT_LEAD3, + BT_LEAD4, + BT_TRAIL, + BT_CR, + BT_LF, + BT_GT, + BT_QUOT, + BT_APOS, + BT_EQUALS, + BT_QUEST, + BT_EXCL, + BT_SOL, + BT_SEMI, + BT_NUM, + BT_LSQB, + BT_S, + BT_NMSTRT, + BT_COLON, + BT_HEX, + BT_DIGIT, + BT_NAME, + BT_MINUS, + BT_OTHER, /* known not to be a name or name start character */ + BT_NONASCII, /* might be a name or name start character */ + BT_PERCNT, + BT_LPAR, + BT_RPAR, + BT_AST, + BT_PLUS, + BT_COMMA, + BT_VERBAR +}; + +#include diff --git a/deps/EXPAT/expat/xmltok_impl.inc b/deps/EXPAT/expat/xmltok_impl.inc new file mode 100644 index 0000000000..5f779c0571 --- /dev/null +++ b/deps/EXPAT/expat/xmltok_impl.inc @@ -0,0 +1,1779 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* This file is included! */ +#ifdef XML_TOK_IMPL_C + +#ifndef IS_INVALID_CHAR +#define IS_INVALID_CHAR(enc, ptr, n) (0) +#endif + +#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_INVALID_CHAR(enc, ptr, n)) { \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define INVALID_CASES(ptr, nextTokPtr) \ + INVALID_LEAD_CASE(2, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(3, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(4, ptr, nextTokPtr) \ + case BT_NONXML: \ + case BT_MALFORM: \ + case BT_TRAIL: \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; + +#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NAME_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + case BT_DIGIT: \ + case BT_NAME: \ + case BT_MINUS: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr) + +#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr) + +#ifndef PREFIX +#define PREFIX(ident) ident +#endif + + +#define HAS_CHARS(enc, ptr, end, count) \ + (end - ptr >= count * MINBPC(enc)) + +#define HAS_CHAR(enc, ptr, end) \ + HAS_CHARS(enc, ptr, end, 1) + +#define REQUIRE_CHARS(enc, ptr, end, count) \ + { \ + if (! HAS_CHARS(enc, ptr, end, count)) { \ + return XML_TOK_PARTIAL; \ + } \ + } + +#define REQUIRE_CHAR(enc, ptr, end) \ + REQUIRE_CHARS(enc, ptr, end, 1) + + +/* ptr points to character following " */ + switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) { + case BT_S: case BT_CR: case BT_LF: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + /* fall through */ + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DECL_OPEN; + case BT_NMSTRT: + case BT_HEX: + ptr += MINBPC(enc); + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(checkPiTarget)(const ENCODING *UNUSED_P(enc), const char *ptr, + const char *end, int *tokPtr) +{ + int upper = 0; + *tokPtr = XML_TOK_PI; + if (end - ptr != MINBPC(enc)*3) + return 1; + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_x: + break; + case ASCII_X: + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_m: + break; + case ASCII_M: + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_l: + break; + case ASCII_L: + upper = 1; + break; + default: + return 1; + } + if (upper) + return 0; + *tokPtr = XML_TOK_XML_DECL; + return 1; +} + +/* ptr points to character following "= end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_RSQB: + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + break; + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CDATA_SECT_CLOSE; + case BT_CR: + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + case BT_RSQB: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following "= end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_LT: + return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_AMP: + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_CR: + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + case BT_RSQB: + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + break; + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_RSQB: + if (HAS_CHARS(enc, ptr, end, 2)) { + if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_RSQB)) { + ptr += MINBPC(enc); + break; + } + if (HAS_CHARS(enc, ptr, end, 3)) { + if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), ASCII_GT)) { + ptr += MINBPC(enc); + break; + } + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_INVALID; + } + } + /* fall through */ + case BT_AMP: + case BT_LT: + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following "%" */ + +static int PTRCALL +PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + REQUIRE_CHAR(enc, ptr, end); + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_S: case BT_LF: case BT_CR: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_PERCENT; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_SEMI: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_PARAM_ENTITY_REF; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + REQUIRE_CHAR(enc, ptr, end); + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_CR: case BT_LF: case BT_S: + case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR: + *nextTokPtr = ptr; + return XML_TOK_POUND_NAME; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return -XML_TOK_POUND_NAME; +} + +static int PTRCALL +PREFIX(scanLit)(int open, const ENCODING *enc, + const char *ptr, const char *end, + const char **nextTokPtr) +{ + while (HAS_CHAR(enc, ptr, end)) { + int t = BYTE_TYPE(enc, ptr); + switch (t) { + INVALID_CASES(ptr, nextTokPtr) + case BT_QUOT: + case BT_APOS: + ptr += MINBPC(enc); + if (t != open) + break; + if (! HAS_CHAR(enc, ptr, end)) + return -XML_TOK_LITERAL; + *nextTokPtr = ptr; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_CR: case BT_LF: + case BT_GT: case BT_PERCNT: case BT_LSQB: + return XML_TOK_LITERAL; + default: + return XML_TOK_INVALID; + } + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + int tok; + if (ptr >= end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_QUOT: + return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_APOS: + return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_LT: + { + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + switch (BYTE_TYPE(enc, ptr)) { + case BT_EXCL: + return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_QUEST: + return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_NMSTRT: + case BT_HEX: + case BT_NONASCII: + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + *nextTokPtr = ptr - MINBPC(enc); + return XML_TOK_INSTANCE_START; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + case BT_CR: + if (ptr + MINBPC(enc) == end) { + *nextTokPtr = end; + /* indicate that this might be part of a CR/LF pair */ + return -XML_TOK_PROLOG_S; + } + /* fall through */ + case BT_S: case BT_LF: + for (;;) { + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + break; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_LF: + break; + case BT_CR: + /* don't split CR/LF pair */ + if (ptr + MINBPC(enc) != end) + break; + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + } + } + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + case BT_PERCNT: + return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_COMMA: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_COMMA; + case BT_LSQB: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_BRACKET; + case BT_RSQB: + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + return -XML_TOK_CLOSE_BRACKET; + if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { + REQUIRE_CHARS(enc, ptr, end, 2); + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_GT)) { + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_COND_SECT_CLOSE; + } + } + *nextTokPtr = ptr; + return XML_TOK_CLOSE_BRACKET; + case BT_LPAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_PAREN; + case BT_RPAR: + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + return -XML_TOK_CLOSE_PAREN; + switch (BYTE_TYPE(enc, ptr)) { + case BT_AST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_ASTERISK; + case BT_QUEST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_QUESTION; + case BT_PLUS: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_PLUS; + case BT_CR: case BT_LF: case BT_S: + case BT_GT: case BT_COMMA: case BT_VERBAR: + case BT_RPAR: + *nextTokPtr = ptr; + return XML_TOK_CLOSE_PAREN; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_VERBAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OR; + case BT_GT: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DECL_CLOSE; + case BT_NUM: + return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr); +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_NMSTRT_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NAME; \ + break; \ + } \ + if (IS_NAME_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NMTOKEN; \ + break; \ + } \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NMSTRT: + case BT_HEX: + tok = XML_TOK_NAME; + ptr += MINBPC(enc); + break; + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: +#ifdef XML_NS + case BT_COLON: +#endif + tok = XML_TOK_NMTOKEN; + ptr += MINBPC(enc); + break; + case BT_NONASCII: + if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NAME; + break; + } + if (IS_NAME_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NMTOKEN; + break; + } + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_GT: case BT_RPAR: case BT_COMMA: + case BT_VERBAR: case BT_LSQB: case BT_PERCNT: + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return tok; +#ifdef XML_NS + case BT_COLON: + ptr += MINBPC(enc); + switch (tok) { + case XML_TOK_NAME: + REQUIRE_CHAR(enc, ptr, end); + tok = XML_TOK_PREFIXED_NAME; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + default: + tok = XML_TOK_NMTOKEN; + break; + } + break; + case XML_TOK_PREFIXED_NAME: + tok = XML_TOK_NMTOKEN; + break; + } + break; +#endif + case BT_PLUS: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_PLUS; + case BT_AST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_ASTERISK; + case BT_QUEST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_QUESTION; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return -tok; +} + +static int PTRCALL +PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + const char *start; + if (ptr >= end) + return XML_TOK_NONE; + else if (! HAS_CHAR(enc, ptr, end)) + return XML_TOK_PARTIAL; + start = ptr; + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LT: + /* this is for inside entity references */ + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_S: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_ATTRIBUTE_VALUE_S; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +static int PTRCALL +PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + const char *start; + if (ptr >= end) + return XML_TOK_NONE; + else if (! HAS_CHAR(enc, ptr, end)) + return XML_TOK_PARTIAL; + start = ptr; + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_PERCNT: + if (ptr == start) { + int tok = PREFIX(scanPercent)(enc, ptr + MINBPC(enc), + end, nextTokPtr); + return (tok == XML_TOK_PERCENT) ? XML_TOK_INVALID : tok; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (! HAS_CHAR(enc, ptr, end)) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +#ifdef XML_DTD + +static int PTRCALL +PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + int level = 0; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + end = ptr + n; + } + } + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { + INVALID_CASES(ptr, nextTokPtr) + case BT_LT: + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + if (CHAR_MATCHES(enc, ptr, ASCII_EXCL)) { + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + if (CHAR_MATCHES(enc, ptr, ASCII_LSQB)) { + ++level; + ptr += MINBPC(enc); + } + } + break; + case BT_RSQB: + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { + ptr += MINBPC(enc); + REQUIRE_CHAR(enc, ptr, end); + if (CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr += MINBPC(enc); + if (level == 0) { + *nextTokPtr = ptr; + return XML_TOK_IGNORE_SECT; + } + --level; + } + } + break; + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; +} + +#endif /* XML_DTD */ + +static int PTRCALL +PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, + const char **badPtr) +{ + ptr += MINBPC(enc); + end -= MINBPC(enc); + for (; HAS_CHAR(enc, ptr, end); ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + case BT_HEX: + case BT_MINUS: + case BT_APOS: + case BT_LPAR: + case BT_RPAR: + case BT_PLUS: + case BT_COMMA: + case BT_SOL: + case BT_EQUALS: + case BT_QUEST: + case BT_CR: + case BT_LF: + case BT_SEMI: + case BT_EXCL: + case BT_AST: + case BT_PERCNT: + case BT_NUM: +#ifdef XML_NS + case BT_COLON: +#endif + break; + case BT_S: + if (CHAR_MATCHES(enc, ptr, ASCII_TAB)) { + *badPtr = ptr; + return 0; + } + break; + case BT_NAME: + case BT_NMSTRT: + if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f)) + break; + default: + switch (BYTE_TO_ASCII(enc, ptr)) { + case 0x24: /* $ */ + case 0x40: /* @ */ + break; + default: + *badPtr = ptr; + return 0; + } + break; + } + } + return 1; +} + +/* This must only be called for a well-formed start-tag or empty + element tag. Returns the number of attributes. Pointers to the + first attsMax attributes are stored in atts. +*/ + +static int PTRCALL +PREFIX(getAtts)(const ENCODING *enc, const char *ptr, + int attsMax, ATTRIBUTE *atts) +{ + enum { other, inName, inValue } state = inName; + int nAtts = 0; + int open = 0; /* defined when state == inValue; + initialization just to shut up compilers */ + + for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { +#define START_NAME \ + if (state == other) { \ + if (nAtts < attsMax) { \ + atts[nAtts].name = ptr; \ + atts[nAtts].normalized = 1; \ + } \ + state = inName; \ + } +#define LEAD_CASE(n) \ + case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: + case BT_HEX: + START_NAME + break; +#undef START_NAME + case BT_QUOT: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_QUOT; + } + else if (open == BT_QUOT) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_APOS: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_APOS; + } + else if (open == BT_APOS) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_AMP: + if (nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_S: + if (state == inName) + state = other; + else if (state == inValue + && nAtts < attsMax + && atts[nAtts].normalized + && (ptr == atts[nAtts].valuePtr + || BYTE_TO_ASCII(enc, ptr) != ASCII_SPACE + || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ASCII_SPACE + || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open)) + atts[nAtts].normalized = 0; + break; + case BT_CR: case BT_LF: + /* This case ensures that the first attribute name is counted + Apart from that we could just change state on the quote. */ + if (state == inName) + state = other; + else if (state == inValue && nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_GT: + case BT_SOL: + if (state != inValue) + return nAtts; + break; + default: + break; + } + } + /* not reached */ +} + +static int PTRFASTCALL +PREFIX(charRefNumber)(const ENCODING *UNUSED_P(enc), const char *ptr) +{ + int result = 0; + /* skip &# */ + ptr += 2*MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_x)) { + for (ptr += MINBPC(enc); + !CHAR_MATCHES(enc, ptr, ASCII_SEMI); + ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + switch (c) { + case ASCII_0: case ASCII_1: case ASCII_2: case ASCII_3: case ASCII_4: + case ASCII_5: case ASCII_6: case ASCII_7: case ASCII_8: case ASCII_9: + result <<= 4; + result |= (c - ASCII_0); + break; + case ASCII_A: case ASCII_B: case ASCII_C: + case ASCII_D: case ASCII_E: case ASCII_F: + result <<= 4; + result += 10 + (c - ASCII_A); + break; + case ASCII_a: case ASCII_b: case ASCII_c: + case ASCII_d: case ASCII_e: case ASCII_f: + result <<= 4; + result += 10 + (c - ASCII_a); + break; + } + if (result >= 0x110000) + return -1; + } + } + else { + for (; !CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + result *= 10; + result += (c - ASCII_0); + if (result >= 0x110000) + return -1; + } + } + return checkCharRefNumber(result); +} + +static int PTRCALL +PREFIX(predefinedEntityName)(const ENCODING *UNUSED_P(enc), const char *ptr, + const char *end) +{ + switch ((end - ptr)/MINBPC(enc)) { + case 2: + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_t)) { + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_l: + return ASCII_LT; + case ASCII_g: + return ASCII_GT; + } + } + break; + case 3: + if (CHAR_MATCHES(enc, ptr, ASCII_a)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_m)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_p)) + return ASCII_AMP; + } + } + break; + case 4: + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_q: + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_u)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_o)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_t)) + return ASCII_QUOT; + } + } + break; + case ASCII_a: + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_p)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_o)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_s)) + return ASCII_APOS; + } + } + break; + } + } + return 0; +} + +static int PTRCALL +PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr1)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (*ptr1++ != *ptr2++) \ + return 0; + LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2) +#undef LEAD_CASE + /* fall through */ + if (*ptr1++ != *ptr2++) + return 0; + break; + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 1) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 2) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 3) { + if (*ptr2++ != *ptr1++) + return 0; + } + } + } + break; + default: + if (MINBPC(enc) == 1 && *ptr1 == *ptr2) + return 1; + switch (BYTE_TYPE(enc, ptr2)) { + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + return 0; + default: + return 1; + } + } + } + /* not reached */ +} + +static int PTRCALL +PREFIX(nameMatchesAscii)(const ENCODING *UNUSED_P(enc), const char *ptr1, + const char *end1, const char *ptr2) +{ + for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) { + if (end1 - ptr1 < MINBPC(enc)) + return 0; + if (!CHAR_MATCHES(enc, ptr1, *ptr2)) + return 0; + } + return ptr1 == end1; +} + +static int PTRFASTCALL +PREFIX(nameLength)(const ENCODING *enc, const char *ptr) +{ + const char *start = ptr; + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + ptr += MINBPC(enc); + break; + default: + return (int)(ptr - start); + } + } +} + +static const char * PTRFASTCALL +PREFIX(skipS)(const ENCODING *enc, const char *ptr) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_LF: + case BT_CR: + case BT_S: + ptr += MINBPC(enc); + break; + default: + return ptr; + } + } +} + +static void PTRCALL +PREFIX(updatePosition)(const ENCODING *enc, + const char *ptr, + const char *end, + POSITION *pos) +{ + while (HAS_CHAR(enc, ptr, end)) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_LF: + pos->columnNumber = (XML_Size)-1; + pos->lineNumber++; + ptr += MINBPC(enc); + break; + case BT_CR: + pos->lineNumber++; + ptr += MINBPC(enc); + if (HAS_CHAR(enc, ptr, end) && BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + pos->columnNumber = (XML_Size)-1; + break; + default: + ptr += MINBPC(enc); + break; + } + pos->columnNumber++; + } +} + +#undef DO_LEAD_CASE +#undef MULTIBYTE_CASES +#undef INVALID_CASES +#undef CHECK_NAME_CASE +#undef CHECK_NAME_CASES +#undef CHECK_NMSTRT_CASE +#undef CHECK_NMSTRT_CASES + +#endif /* XML_TOK_IMPL_C */ diff --git a/deps/EXPAT/expat/xmltok_ns.inc b/deps/EXPAT/expat/xmltok_ns.inc new file mode 100644 index 0000000000..c3b88fdf4e --- /dev/null +++ b/deps/EXPAT/expat/xmltok_ns.inc @@ -0,0 +1,115 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* This file is included! */ +#ifdef XML_TOK_NS_C + +const ENCODING * +NS(XmlGetUtf8InternalEncoding)(void) +{ + return &ns(internal_utf8_encoding).enc; +} + +const ENCODING * +NS(XmlGetUtf16InternalEncoding)(void) +{ +#if BYTEORDER == 1234 + return &ns(internal_little2_encoding).enc; +#elif BYTEORDER == 4321 + return &ns(internal_big2_encoding).enc; +#else + const short n = 1; + return (*(const char *)&n + ? &ns(internal_little2_encoding).enc + : &ns(internal_big2_encoding).enc); +#endif +} + +static const ENCODING * const NS(encodings)[] = { + &ns(latin1_encoding).enc, + &ns(ascii_encoding).enc, + &ns(utf8_encoding).enc, + &ns(big2_encoding).enc, + &ns(big2_encoding).enc, + &ns(little2_encoding).enc, + &ns(utf8_encoding).enc /* NO_ENC */ +}; + +static int PTRCALL +NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, + XML_PROLOG_STATE, ptr, end, nextTokPtr); +} + +static int PTRCALL +NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, + XML_CONTENT_STATE, ptr, end, nextTokPtr); +} + +int +NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, + const char *name) +{ + int i = getEncodingIndex(name); + if (i == UNKNOWN_ENC) + return 0; + SET_INIT_ENC_INDEX(p, i); + p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog); + p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent); + p->initEnc.updatePosition = initUpdatePosition; + p->encPtr = encPtr; + *encPtr = &(p->initEnc); + return 1; +} + +static const ENCODING * +NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) +{ +#define ENCODING_MAX 128 + char buf[ENCODING_MAX]; + char *p = buf; + int i; + XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1); + if (ptr != end) + return 0; + *p = 0; + if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2) + return enc; + i = getEncodingIndex(buf); + if (i == UNKNOWN_ENC) + return 0; + return NS(encodings)[i]; +} + +int +NS(XmlParseXmlDecl)(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + return doParseXmlDecl(NS(findEncoding), + isGeneralTextEntity, + enc, + ptr, + end, + badPtr, + versionPtr, + versionEndPtr, + encodingName, + encoding, + standalone); +} + +#endif /* XML_TOK_NS_C */ diff --git a/deps/GLEW/GLEW.cmake b/deps/GLEW/GLEW.cmake index fcbdcd20c3..285b9be4d3 100644 --- a/deps/GLEW/GLEW.cmake +++ b/deps/GLEW/GLEW.cmake @@ -1,5 +1,6 @@ # We have to check for OpenGL to compile GLEW -find_package(OpenGL QUIET REQUIRED) +set(OpenGL_GL_PREFERENCE "LEGACY") # to prevent a nasty warning by cmake +find_package(OpenGL QUIET) prusaslicer_add_cmake_project( GLEW diff --git a/deps/OpenCSG/OpenCSG.cmake b/deps/OpenCSG/OpenCSG.cmake index d9de5e1526..fda74cd45e 100644 --- a/deps/OpenCSG/OpenCSG.cmake +++ b/deps/OpenCSG/OpenCSG.cmake @@ -6,8 +6,8 @@ prusaslicer_add_cmake_project(OpenCSG DEPENDS dep_GLEW ) -if (TARGET dep_ZLIB) - add_dependencies(dep_OpenCSG dep_ZLIB) +if (TARGET ${ZLIB_PKG}) + add_dependencies(dep_OpenCSG ${ZLIB_PKG}) endif() if (MSVC) diff --git a/deps/PNG/PNG.cmake b/deps/PNG/PNG.cmake new file mode 100644 index 0000000000..01bd984998 --- /dev/null +++ b/deps/PNG/PNG.cmake @@ -0,0 +1,13 @@ +prusaslicer_add_cmake_project(PNG + GIT_REPOSITORY https://github.com/glennrp/libpng.git + GIT_TAG v1.6.35 + DEPENDS ${ZLIB_PKG} + CMAKE_ARGS + -DPNG_SHARED=OFF + -DPNG_STATIC=ON + -DPNG_TESTS=OFF +) + +if (MSVC) + add_debug_dep(dep_PNG) +endif () diff --git a/deps/deps-linux.cmake b/deps/deps-linux.cmake index 01f7ab21b8..3ad3cca64f 100644 --- a/deps/deps-linux.cmake +++ b/deps/deps-linux.cmake @@ -3,6 +3,13 @@ set(DEP_CMAKE_OPTS "-DCMAKE_POSITION_INDEPENDENT_CODE=ON") include("deps-unix-common.cmake") +find_package(PNG QUIET) +if (NOT PNG_FOUND) + message(WARNING "No PNG dev package found in system, building static library. You should install the system package.") +endif () + +#TODO UDEV + ExternalProject_Add(dep_boost EXCLUDE_FROM_ALL 1 URL "https://dl.bintray.com/boostorg/release/1.70.0/source/boost_1_70_0.tar.gz" @@ -93,45 +100,4 @@ ExternalProject_Add(dep_libcurl INSTALL_COMMAND make install "DESTDIR=${DESTDIR}" ) -if (DEP_WX_STABLE) - set(DEP_WX_TAG "v3.0.4") -else () - set(DEP_WX_TAG "v3.1.1-patched") -endif() - -if (DEP_WX_GTK3) - set(WX_GTK_VERSION "3") -else () - set(WX_GTK_VERSION "2") -endif() - -ExternalProject_Add(dep_wxwidgets - EXCLUDE_FROM_ALL 1 - GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" - GIT_TAG "${DEP_WX_TAG}" - BUILD_IN_SOURCE 1 - # PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/wxwidgets-pngprefix.h" src/png/pngprefix.h - CONFIGURE_COMMAND ./configure - "--prefix=${DESTDIR}/usr/local" - --disable-shared - --with-gtk=${WX_GTK_VERSION} - --with-opengl - --enable-unicode - --enable-graphics_ctx - --with-regex=builtin - --with-libpng=builtin - --with-libxpm=builtin - --with-libjpeg=builtin - --with-libtiff=builtin - --with-zlib - --with-expat=builtin - --disable-precomp-headers - --enable-debug_info - --enable-debug_gdb - --disable-debug - --disable-debug_flag - BUILD_COMMAND make "-j${NPROC}" && make -C locale allmo - INSTALL_COMMAND make install -) - add_dependencies(dep_openvdb dep_boost) diff --git a/deps/deps-macos.cmake b/deps/deps-macos.cmake index 17300b247e..ef00b80d50 100644 --- a/deps/deps-macos.cmake +++ b/deps/deps-macos.cmake @@ -86,30 +86,4 @@ ExternalProject_Add(dep_libcurl INSTALL_COMMAND make install "DESTDIR=${DESTDIR}" ) -ExternalProject_Add(dep_wxwidgets - EXCLUDE_FROM_ALL 1 - GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" - GIT_TAG v3.1.3-patched - BUILD_IN_SOURCE 1 -# PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}/wxwidgets-pngprefix.h" src/png/pngprefix.h - CONFIGURE_COMMAND env "CXXFLAGS=${DEP_WERRORS_SDK}" "CFLAGS=${DEP_WERRORS_SDK}" ./configure - "--prefix=${DESTDIR}/usr/local" - --disable-shared - --with-osx_cocoa - --with-macosx-sdk=${CMAKE_OSX_SYSROOT} - "--with-macosx-version-min=${DEP_OSX_TARGET}" - --with-opengl - --with-regex=builtin - --with-libpng=builtin - --with-libxpm=builtin - --with-libjpeg=builtin - --with-libtiff=builtin - --with-zlib - --with-expat=builtin - --disable-debug - --disable-debug_flag - BUILD_COMMAND make "-j${NPROC}" && PATH=/usr/local/opt/gettext/bin/:$ENV{PATH} make -C locale allmo - INSTALL_COMMAND make install -) - add_dependencies(dep_openvdb dep_boost) \ No newline at end of file diff --git a/deps/deps-mingw.cmake b/deps/deps-mingw.cmake index bfe5f9fe43..89b7e2b437 100644 --- a/deps/deps-mingw.cmake +++ b/deps/deps-mingw.cmake @@ -57,20 +57,4 @@ ExternalProject_Add(dep_libcurl -DCURL_DISABLE_GOPHER=ON -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local ${DEP_CMAKE_OPTS} -) - -ExternalProject_Add(dep_wxwidgets - EXCLUDE_FROM_ALL 1 - GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" - GIT_TAG v3.1.1-patched -# URL "https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.1/wxWidgets-3.1.1.tar.bz2" -# URL_HASH SHA256=c925dfe17e8f8b09eb7ea9bfdcfcc13696a3e14e92750effd839f5e10726159e -# PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}\\wxwidgets-pngprefix.h" src\\png\\pngprefix.h - CMAKE_ARGS - -DBUILD_SHARED_LIBS=OFF - -DwxUSE_LIBPNG=builtin - -DwxUSE_ZLIB=builtin - -DwxUSE_OPENGL=ON - -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local - ${DEP_CMAKE_OPTS} ) \ No newline at end of file diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake index 3f3a70c6a7..46c9f8864e 100644 --- a/deps/deps-unix-common.cmake +++ b/deps/deps-unix-common.cmake @@ -9,9 +9,15 @@ endif () find_package(ZLIB QUIET) if (NOT ZLIB_FOUND) - include(ZLIB/ZLIB.cmake) + message(WARNING "No ZLIB dev package found in system, building static library. You should install the system package.") endif () +# TODO Evaluate expat modifications in the bundled version and test with system versions in various distros and OSX SDKs +# find_package(EXPAT QUIET) +# if (NOT EXPAT_FOUND) +# message(WARNING "No EXPAT dev package found in system, building static library. Consider installing the system package.") +# endif () + ExternalProject_Add(dep_tbb EXCLUDE_FROM_ALL 1 URL "https://github.com/wjakob/tbb/archive/a0dc9bf76d0120f917b641ed095360448cabc85b.tar.gz" diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 600168c1af..8be9888bc5 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -149,38 +149,6 @@ ExternalProject_Add(dep_nlopt add_debug_dep(dep_nlopt) -include(ZLIB/ZLIB.cmake) -# ExternalProject_Add(dep_zlib -# EXCLUDE_FROM_ALL 1 -# URL "https://zlib.net/zlib-1.2.11.tar.xz" -# URL_HASH SHA256=4ff941449631ace0d4d203e3483be9dbc9da454084111f97ea0a2114e19bf066 -# CMAKE_GENERATOR "${DEP_MSVC_GEN}" -# CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" -# CMAKE_ARGS -# -DSKIP_INSTALL_FILES=ON # Prevent installation of man pages et al. -# "-DINSTALL_BIN_DIR=${CMAKE_CURRENT_BINARY_DIR}\\fallout" # I found no better way of preventing zlib from creating & installing DLLs :-/ -# -DCMAKE_POSITION_INDEPENDENT_CODE=ON -# "-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local" -# BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj -# INSTALL_COMMAND "" -# ) - -add_debug_dep(dep_ZLIB) - -# The following steps are unfortunately needed to remove the _static suffix on libraries -# ExternalProject_Add_Step(dep_zlib fix_static -# DEPENDEES install -# COMMAND "${CMAKE_COMMAND}" -E rename zlibstatic.lib zlib.lib -# WORKING_DIRECTORY "${DESTDIR}\\usr\\local\\lib\\" -# ) -# if (${DEP_DEBUG}) -# ExternalProject_Add_Step(dep_zlib fix_static_debug -# DEPENDEES install -# COMMAND "${CMAKE_COMMAND}" -E rename zlibstaticd.lib zlibd.lib -# WORKING_DIRECTORY "${DESTDIR}\\usr\\local\\lib\\" -# ) -# endif () - if (${DEPS_BITS} EQUAL 32) set(DEP_LIBCURL_TARGET "x86") else () @@ -243,36 +211,13 @@ endif () find_package(Git REQUIRED) -ExternalProject_Add(dep_wxwidgets - EXCLUDE_FROM_ALL 1 - GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" - GIT_TAG v3.1.1-patched -# URL "https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.1/wxWidgets-3.1.1.tar.bz2" -# URL_HASH SHA256=c925dfe17e8f8b09eb7ea9bfdcfcc13696a3e14e92750effd839f5e10726159e - BUILD_IN_SOURCE 1 -# PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}\\wxwidgets-pngprefix.h" src\\png\\pngprefix.h - CONFIGURE_COMMAND "" - BUILD_COMMAND cd build\\msw && nmake /f makefile.vc BUILD=release SHARED=0 UNICODE=1 USE_GUI=1 "${DEP_WXWIDGETS_TARGET}" - INSTALL_COMMAND "${CMAKE_COMMAND}" -E copy_directory include "${DESTDIR}\\usr\\local\\include" - && "${CMAKE_COMMAND}" -E copy_directory "lib\\${DEP_WXWIDGETS_LIBDIR}" "${DESTDIR}\\usr\\local\\lib\\${DEP_WXWIDGETS_LIBDIR}" -) -if (${DEP_DEBUG}) - ExternalProject_Get_Property(dep_wxwidgets SOURCE_DIR) - ExternalProject_Add_Step(dep_wxwidgets build_debug - DEPENDEES build - DEPENDERS install - COMMAND cd build\\msw && nmake /f makefile.vc BUILD=debug SHARED=0 UNICODE=1 USE_GUI=1 "${DEP_WXWIDGETS_TARGET}" - WORKING_DIRECTORY "${SOURCE_DIR}" - ) -endif () - ExternalProject_Add(dep_blosc EXCLUDE_FROM_ALL 1 #URL https://github.com/Blosc/c-blosc/archive/v1.17.0.zip #URL_HASH SHA256=7463a1df566704f212263312717ab2c36b45d45cba6cd0dccebf91b2cc4b4da9 GIT_REPOSITORY https://github.com/Blosc/c-blosc.git GIT_TAG e63775855294b50820ef44d1b157f4de1cc38d3e #v1.17.0 - DEPENDS dep_ZLIB + DEPENDS ${ZLIB_PKG} CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS @@ -299,7 +244,7 @@ ExternalProject_Add(dep_openexr EXCLUDE_FROM_ALL 1 GIT_REPOSITORY https://github.com/openexr/openexr.git GIT_TAG eae0e337c9f5117e78114fd05f7a415819df413a #v2.4.0 - DEPENDS dep_ZLIB + DEPENDS ${ZLIB_PKG} CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS @@ -352,4 +297,4 @@ if (${DEP_DEBUG}) COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj WORKING_DIRECTORY "${BINARY_DIR}" ) -endif () +endif () \ No newline at end of file diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake new file mode 100644 index 0000000000..4f89fe82c1 --- /dev/null +++ b/deps/wxWidgets/wxWidgets.cmake @@ -0,0 +1,36 @@ +set(_wx_git_tag v3.1.3-patched) + +# set(_patch_command "") +set(_wx_toolkit "") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(_gtk_ver 2) + if (DEP_WX_GTK3) + set(_gtk_ver 3) + endif () + set(_wx_toolkit "-DwxBUILD_TOOLKIT=gtk${_gtk_ver}") +endif() + +prusaslicer_add_cmake_project(wxWidgets + GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" + GIT_TAG ${_wx_git_tag} + # PATCH_COMMAND "${_patch_command}" + DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} + CMAKE_ARGS + -DwxBUILD_PRECOMP=ON + ${_wx_toolkit} + "-DCMAKE_DEBUG_POSTFIX:STRING=" + -DwxUSE_DETECT_SM=OFF + -DwxUSE_UNICODE=ON + -DwxUSE_OPENGL=ON + -DwxUSE_LIBPNG=sys + -DwxUSE_ZLIB=sys + -DwxUSE_REGEX=builtin + -DwxUSE_LIBXPM=builtin + -DwxUSE_LIBJPEG=builtin + -DwxUSE_LIBTIFF=builtin + -DwxUSE_EXPAT=sys +) + +if (MSVC) + add_debug_dep(dep_wxWidgets) +endif () \ No newline at end of file diff --git a/resources/localization/PrusaSlicer.pot b/resources/localization/PrusaSlicer.pot index 6e72e438cf..aef39c253f 100644 --- a/resources/localization/PrusaSlicer.pot +++ b/resources/localization/PrusaSlicer.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-03-16 13:41+0100\n" +"POT-Creation-Date: 2020-04-16 11:29+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,109 +18,109 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: src/slic3r/GUI/AboutDialog.cpp:41 src/slic3r/GUI/AboutDialog.cpp:294 +#: src/slic3r/GUI/AboutDialog.cpp:42 src/slic3r/GUI/AboutDialog.cpp:295 msgid "Portions copyright" msgstr "" -#: src/slic3r/GUI/AboutDialog.cpp:129 src/slic3r/GUI/AboutDialog.cpp:258 +#: src/slic3r/GUI/AboutDialog.cpp:130 src/slic3r/GUI/AboutDialog.cpp:259 msgid "Copyright" msgstr "" #. TRN "Slic3r _is licensed under the_ License" -#: src/slic3r/GUI/AboutDialog.cpp:131 +#: src/slic3r/GUI/AboutDialog.cpp:132 msgid "" "License agreements of all following programs (libraries) are part of " "application license agreement" msgstr "" -#: src/slic3r/GUI/AboutDialog.cpp:201 +#: src/slic3r/GUI/AboutDialog.cpp:202 #, possible-c-format msgid "About %s" msgstr "" -#: src/slic3r/GUI/AboutDialog.cpp:233 src/slic3r/GUI/MainFrame.cpp:69 +#: src/slic3r/GUI/AboutDialog.cpp:234 src/slic3r/GUI/MainFrame.cpp:70 msgid "Version" msgstr "" #. TRN "Slic3r _is licensed under the_ License" -#: src/slic3r/GUI/AboutDialog.cpp:260 +#: src/slic3r/GUI/AboutDialog.cpp:261 msgid "is licensed under the" msgstr "" -#: src/slic3r/GUI/AboutDialog.cpp:261 +#: src/slic3r/GUI/AboutDialog.cpp:262 msgid "GNU Affero General Public License, version 3" msgstr "" -#: src/slic3r/GUI/AboutDialog.cpp:262 +#: src/slic3r/GUI/AboutDialog.cpp:263 msgid "" "PrusaSlicer is based on Slic3r by Alessandro Ranellucci and the RepRap " "community." msgstr "" -#: src/slic3r/GUI/AboutDialog.cpp:263 +#: src/slic3r/GUI/AboutDialog.cpp:264 msgid "" "Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, " "Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and " "numerous others." msgstr "" -#: src/slic3r/GUI/AppConfig.cpp:114 +#: src/slic3r/GUI/AppConfig.cpp:110 msgid "" "Error parsing PrusaSlicer config file, it is probably corrupted. Try to " "manually delete the file to recover from the error. Your user profiles will " "not be affected." msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:108 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:101 msgid "" "Copying of the temporary G-code to the output G-code failed. Maybe the SD " "card is write locked?" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:111 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:104 msgid "" "Copying of the temporary G-code to the output G-code failed. There might be " "problem with target device, please try exporting again or using different " "device. The corrupted output G-code is at %1%.tmp." msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:114 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:107 msgid "" "Renaming of the G-code after copying to the selected destination folder has " "failed. Current path is %1%.tmp. Please try exporting again." msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:117 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:110 msgid "" "Copying of the temporary G-code has finished but the original code at %1% " "couldn't be opened during copy check. The output G-code is at %2%.tmp." msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:120 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:113 msgid "" "Copying of the temporary G-code has finished but the exported code couldn't " "be opened during copy check. The output G-code is at %1%.tmp." msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:127 -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:479 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:120 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:468 msgid "Running post-processing scripts" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:129 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:122 msgid "G-code file exported to %1%" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:133 -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:183 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:126 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:172 msgid "Slicing complete" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:179 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:168 msgid "Masked SLA file exported to %1%" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:221 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:210 #, possible-c-format msgid "" "%s has encountered an error. It was likely caused by running out of memory. " @@ -128,15 +128,15 @@ msgid "" "and we would be glad if you reported it." msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:481 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:470 msgid "Copying of the temporary G-code to the output G-code failed" msgstr "" -#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:506 +#: src/slic3r/GUI/BackgroundSlicingProcess.cpp:493 msgid "Scheduling upload to `%1%`. See Window -> Print Host Upload Queue" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:66 src/slic3r/GUI/GUI_ObjectList.cpp:2067 +#: src/slic3r/GUI/BedShapeDialog.cpp:66 src/slic3r/GUI/GUI_ObjectList.cpp:2073 msgid "Shape" msgstr "" @@ -144,36 +144,36 @@ msgstr "" msgid "Rectangular" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:77 -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:232 src/slic3r/GUI/Plater.cpp:160 -#: src/slic3r/GUI/Tab.cpp:2309 +#: src/slic3r/GUI/BedShapeDialog.cpp:79 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:232 src/slic3r/GUI/Plater.cpp:162 +#: src/slic3r/GUI/Tab.cpp:2319 msgid "Size" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:78 +#: src/slic3r/GUI/BedShapeDialog.cpp:80 msgid "Size in X and Y of the rectangular plate." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:84 +#: src/slic3r/GUI/BedShapeDialog.cpp:88 msgid "Origin" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:85 +#: src/slic3r/GUI/BedShapeDialog.cpp:89 msgid "" "Distance of the 0,0 G-code coordinate from the front left corner of the " "rectangle." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:89 +#: src/slic3r/GUI/BedShapeDialog.cpp:93 msgid "Circular" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:92 src/slic3r/GUI/ConfigWizard.cpp:233 +#: src/slic3r/GUI/BedShapeDialog.cpp:96 src/slic3r/GUI/ConfigWizard.cpp:233 #: src/slic3r/GUI/ConfigWizard.cpp:985 src/slic3r/GUI/ConfigWizard.cpp:999 #: src/slic3r/GUI/ExtruderSequenceDialog.cpp:87 #: src/slic3r/GUI/GUI_ObjectLayers.cpp:142 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:333 -#: src/slic3r/GUI/ObjectDataViewModel.cpp:94 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:96 #: src/slic3r/GUI/WipeTowerDialog.cpp:85 src/libslic3r/PrintConfig.cpp:75 #: src/libslic3r/PrintConfig.cpp:82 src/libslic3r/PrintConfig.cpp:91 #: src/libslic3r/PrintConfig.cpp:122 src/libslic3r/PrintConfig.cpp:188 @@ -208,77 +208,77 @@ msgstr "" msgid "mm" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:93 src/libslic3r/PrintConfig.cpp:709 +#: src/slic3r/GUI/BedShapeDialog.cpp:97 src/libslic3r/PrintConfig.cpp:709 msgid "Diameter" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:94 +#: src/slic3r/GUI/BedShapeDialog.cpp:98 msgid "" "Diameter of the print bed. It is assumed that origin (0,0) is located in the " "center." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:98 src/slic3r/GUI/GUI_Preview.cpp:249 +#: src/slic3r/GUI/BedShapeDialog.cpp:102 src/slic3r/GUI/GUI_Preview.cpp:308 #: src/libslic3r/ExtrusionEntity.cpp:322 msgid "Custom" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:102 +#: src/slic3r/GUI/BedShapeDialog.cpp:106 msgid "Load shape from STL..." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:155 +#: src/slic3r/GUI/BedShapeDialog.cpp:159 msgid "Settings" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:172 +#: src/slic3r/GUI/BedShapeDialog.cpp:176 msgid "Texture" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:182 src/slic3r/GUI/BedShapeDialog.cpp:261 +#: src/slic3r/GUI/BedShapeDialog.cpp:186 src/slic3r/GUI/BedShapeDialog.cpp:265 msgid "Load..." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:190 src/slic3r/GUI/BedShapeDialog.cpp:269 -#: src/slic3r/GUI/Tab.cpp:3145 +#: src/slic3r/GUI/BedShapeDialog.cpp:194 src/slic3r/GUI/BedShapeDialog.cpp:273 +#: src/slic3r/GUI/Tab.cpp:3155 msgid "Remove" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:223 src/slic3r/GUI/BedShapeDialog.cpp:302 +#: src/slic3r/GUI/BedShapeDialog.cpp:227 src/slic3r/GUI/BedShapeDialog.cpp:306 msgid "Not found:" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:251 +#: src/slic3r/GUI/BedShapeDialog.cpp:255 msgid "Model" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:487 +#: src/slic3r/GUI/BedShapeDialog.cpp:491 msgid "Choose an STL file to import bed shape from:" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:494 src/slic3r/GUI/BedShapeDialog.cpp:543 -#: src/slic3r/GUI/BedShapeDialog.cpp:566 +#: src/slic3r/GUI/BedShapeDialog.cpp:498 src/slic3r/GUI/BedShapeDialog.cpp:547 +#: src/slic3r/GUI/BedShapeDialog.cpp:570 msgid "Invalid file format." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:505 +#: src/slic3r/GUI/BedShapeDialog.cpp:509 msgid "Error! Invalid model" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:513 +#: src/slic3r/GUI/BedShapeDialog.cpp:517 msgid "The selected file contains no geometry." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:517 +#: src/slic3r/GUI/BedShapeDialog.cpp:521 msgid "" "The selected file contains several disjoint areas. This is not supported." msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:532 +#: src/slic3r/GUI/BedShapeDialog.cpp:536 msgid "Choose a file to import bed texture from (PNG/SVG):" msgstr "" -#: src/slic3r/GUI/BedShapeDialog.cpp:555 +#: src/slic3r/GUI/BedShapeDialog.cpp:559 msgid "Choose an STL file to import bed model from:" msgstr "" @@ -336,7 +336,7 @@ msgid "" msgstr "" #: src/slic3r/GUI/ConfigManipulation.cpp:49 -#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 src/slic3r/GUI/Tab.cpp:1109 +#: src/slic3r/GUI/GUI_ObjectLayers.cpp:27 src/slic3r/GUI/Tab.cpp:1116 #: src/libslic3r/PrintConfig.cpp:71 msgid "Layer height" msgstr "" @@ -424,8 +424,8 @@ msgstr "" #: src/slic3r/GUI/ConfigManipulation.cpp:211 #: src/slic3r/GUI/GUI_ObjectList.cpp:35 src/slic3r/GUI/GUI_ObjectList.cpp:96 -#: src/slic3r/GUI/GUI_ObjectList.cpp:612 src/slic3r/GUI/Plater.cpp:532 -#: src/slic3r/GUI/Tab.cpp:1151 src/slic3r/GUI/Tab.cpp:1152 +#: src/slic3r/GUI/GUI_ObjectList.cpp:618 src/slic3r/GUI/Plater.cpp:534 +#: src/slic3r/GUI/Tab.cpp:1158 src/slic3r/GUI/Tab.cpp:1159 #: src/libslic3r/PrintConfig.cpp:203 src/libslic3r/PrintConfig.cpp:416 #: src/libslic3r/PrintConfig.cpp:436 src/libslic3r/PrintConfig.cpp:776 #: src/libslic3r/PrintConfig.cpp:790 src/libslic3r/PrintConfig.cpp:827 @@ -480,7 +480,7 @@ msgstr "" msgid "PrusaSlicer version" msgstr "" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:51 src/slic3r/GUI/Preset.cpp:1562 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:51 src/slic3r/GUI/Preset.cpp:1563 msgid "print" msgstr "" @@ -488,11 +488,11 @@ msgstr "" msgid "filaments" msgstr "" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:53 src/slic3r/GUI/Preset.cpp:1566 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:53 src/slic3r/GUI/Preset.cpp:1567 msgid "printer" msgstr "" -#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:57 src/slic3r/GUI/Tab.cpp:1027 +#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:57 src/slic3r/GUI/Tab.cpp:1034 msgid "vendor" msgstr "" @@ -546,12 +546,12 @@ msgid "Standard" msgstr "" #: src/slic3r/GUI/ConfigWizard.cpp:305 src/slic3r/GUI/ConfigWizard.cpp:588 -#: src/slic3r/GUI/Tab.cpp:3204 +#: src/slic3r/GUI/Tab.cpp:3214 msgid "All" msgstr "" #: src/slic3r/GUI/ConfigWizard.cpp:306 src/slic3r/GUI/ConfigWizard.cpp:589 -#: src/slic3r/GUI/Plater.cpp:504 src/slic3r/GUI/Plater.cpp:644 +#: src/slic3r/GUI/Plater.cpp:506 src/slic3r/GUI/Plater.cpp:646 #: src/libslic3r/ExtrusionEntity.cpp:309 msgid "None" msgstr "" @@ -660,9 +660,9 @@ msgid "" "an update is applied." msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:813 src/slic3r/GUI/GUI_ObjectList.cpp:1672 -#: src/slic3r/GUI/GUI_ObjectList.cpp:4025 src/slic3r/GUI/Plater.cpp:3292 -#: src/slic3r/GUI/Plater.cpp:4001 src/slic3r/GUI/Plater.cpp:4030 +#: src/slic3r/GUI/ConfigWizard.cpp:813 src/slic3r/GUI/GUI_ObjectList.cpp:1678 +#: src/slic3r/GUI/GUI_ObjectList.cpp:4040 src/slic3r/GUI/Plater.cpp:3225 +#: src/slic3r/GUI/Plater.cpp:3938 src/slic3r/GUI/Plater.cpp:3967 msgid "Reload from disk" msgstr "" @@ -717,7 +717,7 @@ msgstr "" msgid "Firmware Type" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:906 src/slic3r/GUI/Tab.cpp:1970 +#: src/slic3r/GUI/ConfigWizard.cpp:906 src/slic3r/GUI/Tab.cpp:1980 msgid "Firmware" msgstr "" @@ -819,8 +819,8 @@ msgstr "" msgid "SLA Technology Printers" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:1810 src/slic3r/GUI/DoubleSlider.cpp:1905 -#: src/slic3r/GUI/DoubleSlider.cpp:1926 src/slic3r/GUI/GUI.cpp:246 +#: src/slic3r/GUI/ConfigWizard.cpp:1810 src/slic3r/GUI/DoubleSlider.cpp:1907 +#: src/slic3r/GUI/DoubleSlider.cpp:1928 src/slic3r/GUI/GUI.cpp:246 msgid "Notice" msgstr "" @@ -873,7 +873,7 @@ msgstr "" msgid "Filament Profiles Selection" msgstr "" -#: src/slic3r/GUI/ConfigWizard.cpp:2097 src/slic3r/GUI/GUI_ObjectList.cpp:3622 +#: src/slic3r/GUI/ConfigWizard.cpp:2097 src/slic3r/GUI/GUI_ObjectList.cpp:3637 msgid "Type:" msgstr "" @@ -901,63 +901,63 @@ msgstr "" msgid "Configuration &Wizard" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:79 +#: src/slic3r/GUI/DoubleSlider.cpp:81 msgid "Place bearings in slots and resume printing" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:950 +#: src/slic3r/GUI/DoubleSlider.cpp:952 msgid "One layer mode" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:952 +#: src/slic3r/GUI/DoubleSlider.cpp:954 msgid "Discard all custom changes" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:955 +#: src/slic3r/GUI/DoubleSlider.cpp:957 #, possible-c-format msgid "Jump to height %s or Set extruder sequence for the entire print" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:957 src/slic3r/GUI/DoubleSlider.cpp:1529 -#: src/slic3r/GUI/DoubleSlider.cpp:1651 +#: src/slic3r/GUI/DoubleSlider.cpp:959 src/slic3r/GUI/DoubleSlider.cpp:1531 +#: src/slic3r/GUI/DoubleSlider.cpp:1653 msgid "Jump to height" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:960 +#: src/slic3r/GUI/DoubleSlider.cpp:962 msgid "Edit current color - Right click the colored slider segment" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:970 +#: src/slic3r/GUI/DoubleSlider.cpp:972 msgid "Print mode" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:984 +#: src/slic3r/GUI/DoubleSlider.cpp:986 msgid "Add extruder change - Left click" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:986 +#: src/slic3r/GUI/DoubleSlider.cpp:988 msgid "" "Add color change - Left click for predefined color or Shift + Left click for " "custom color selection" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:988 +#: src/slic3r/GUI/DoubleSlider.cpp:990 msgid "Add color change - Left click" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:989 +#: src/slic3r/GUI/DoubleSlider.cpp:991 msgid "or press \"+\" key" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:991 +#: src/slic3r/GUI/DoubleSlider.cpp:993 msgid "Add another code - Ctrl + Left click" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:992 +#: src/slic3r/GUI/DoubleSlider.cpp:994 msgid "Add another code - Right click" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:998 +#: src/slic3r/GUI/DoubleSlider.cpp:1000 msgid "" "The sequential print is on.\n" "It's impossible to apply any custom G-code for objects printing " @@ -965,200 +965,200 @@ msgid "" "This code won't be processed during G-code generation." msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1005 +#: src/slic3r/GUI/DoubleSlider.cpp:1009 msgid "Color change (\"%1%\")" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1006 +#: src/slic3r/GUI/DoubleSlider.cpp:1010 msgid "Color change (\"%1%\") for Extruder %2%" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1009 +#: src/slic3r/GUI/DoubleSlider.cpp:1012 msgid "Pause print (\"%1%\")" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1011 +#: src/slic3r/GUI/DoubleSlider.cpp:1014 msgid "Extruder (tool) is changed to Extruder \"%1%\"" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1019 +#: src/slic3r/GUI/DoubleSlider.cpp:1021 msgid "Note" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1021 +#: src/slic3r/GUI/DoubleSlider.cpp:1023 msgid "" "G-code associated to this tick mark is in a conflict with print mode.\n" "Editing it will cause changes of Slider data." msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1024 +#: src/slic3r/GUI/DoubleSlider.cpp:1026 msgid "" "There is a color change for extruder that won't be used till the end of " "print job.\n" "This code won't be processed during G-code generation." msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1027 +#: src/slic3r/GUI/DoubleSlider.cpp:1029 msgid "" "There is an extruder change set to the same extruder.\n" "This code won't be processed during G-code generation." msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1030 +#: src/slic3r/GUI/DoubleSlider.cpp:1032 msgid "" "There is a color change for extruder that has not been used before.\n" "Check your settings to avoid redundant color changes." msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1035 +#: src/slic3r/GUI/DoubleSlider.cpp:1037 msgid "Delete tick mark - Left click or press \"-\" key" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1037 +#: src/slic3r/GUI/DoubleSlider.cpp:1039 msgid "Edit tick mark - Ctrl + Left click" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1038 +#: src/slic3r/GUI/DoubleSlider.cpp:1040 msgid "Edit tick mark - Right click" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1134 src/slic3r/GUI/DoubleSlider.cpp:1170 -#: src/slic3r/GUI/GLCanvas3D.cpp:977 src/slic3r/GUI/GUI_ObjectList.cpp:1712 -#: src/slic3r/GUI/Tab.cpp:2305 src/libslic3r/GCode/PreviewData.cpp:445 +#: src/slic3r/GUI/DoubleSlider.cpp:1136 src/slic3r/GUI/DoubleSlider.cpp:1172 +#: src/slic3r/GUI/GLCanvas3D.cpp:996 src/slic3r/GUI/GUI_ObjectList.cpp:1718 +#: src/slic3r/GUI/Tab.cpp:2315 src/libslic3r/GCode/PreviewData.cpp:446 #, possible-c-format msgid "Extruder %d" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1135 src/slic3r/GUI/GUI_ObjectList.cpp:1713 +#: src/slic3r/GUI/DoubleSlider.cpp:1137 src/slic3r/GUI/GUI_ObjectList.cpp:1719 msgid "active" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1144 +#: src/slic3r/GUI/DoubleSlider.cpp:1146 msgid "Switch code to Change extruder" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1144 src/slic3r/GUI/GUI_ObjectList.cpp:1679 +#: src/slic3r/GUI/DoubleSlider.cpp:1146 src/slic3r/GUI/GUI_ObjectList.cpp:1685 msgid "Change extruder" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1145 +#: src/slic3r/GUI/DoubleSlider.cpp:1147 msgid "Change extruder (N/A)" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1147 +#: src/slic3r/GUI/DoubleSlider.cpp:1149 msgid "Use another extruder" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1171 +#: src/slic3r/GUI/DoubleSlider.cpp:1173 msgid "used" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1179 +#: src/slic3r/GUI/DoubleSlider.cpp:1181 msgid "Switch code to Color change (%1%) for:" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1180 +#: src/slic3r/GUI/DoubleSlider.cpp:1182 msgid "Add color change (%1%) for:" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1477 +#: src/slic3r/GUI/DoubleSlider.cpp:1479 msgid "Add color change" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1487 +#: src/slic3r/GUI/DoubleSlider.cpp:1489 msgid "Add pause print" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1490 +#: src/slic3r/GUI/DoubleSlider.cpp:1492 msgid "Add custom G-code" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1508 +#: src/slic3r/GUI/DoubleSlider.cpp:1510 msgid "Edit color" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1509 +#: src/slic3r/GUI/DoubleSlider.cpp:1511 msgid "Edit pause print message" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1510 +#: src/slic3r/GUI/DoubleSlider.cpp:1512 msgid "Edit custom G-code" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1516 +#: src/slic3r/GUI/DoubleSlider.cpp:1518 msgid "Delete color change" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1517 +#: src/slic3r/GUI/DoubleSlider.cpp:1519 msgid "Delete tool change" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1518 +#: src/slic3r/GUI/DoubleSlider.cpp:1520 msgid "Delete pause print" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1519 +#: src/slic3r/GUI/DoubleSlider.cpp:1521 msgid "Delete custom G-code" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1532 +#: src/slic3r/GUI/DoubleSlider.cpp:1534 msgid "Set extruder sequence for the entire print" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1618 +#: src/slic3r/GUI/DoubleSlider.cpp:1620 msgid "Enter custom G-code used on current layer" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1619 +#: src/slic3r/GUI/DoubleSlider.cpp:1621 msgid "Custom G-code on current layer (%1% mm)." msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1634 +#: src/slic3r/GUI/DoubleSlider.cpp:1636 msgid "Enter short message shown on Printer display when a print is paused" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1635 +#: src/slic3r/GUI/DoubleSlider.cpp:1637 msgid "Message for pause print on current layer (%1% mm)." msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1650 +#: src/slic3r/GUI/DoubleSlider.cpp:1652 msgid "Enter the height you want to jump to" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1899 +#: src/slic3r/GUI/DoubleSlider.cpp:1901 msgid "The last color change data was saved for a single extruder printing." msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1900 src/slic3r/GUI/DoubleSlider.cpp:1916 +#: src/slic3r/GUI/DoubleSlider.cpp:1902 src/slic3r/GUI/DoubleSlider.cpp:1918 msgid "The last color change data was saved for a multi extruder printing." msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1902 +#: src/slic3r/GUI/DoubleSlider.cpp:1904 msgid "Your current changes will delete all saved color changes." msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1903 src/slic3r/GUI/DoubleSlider.cpp:1924 +#: src/slic3r/GUI/DoubleSlider.cpp:1905 src/slic3r/GUI/DoubleSlider.cpp:1926 msgid "Are you sure you want to continue?" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1917 +#: src/slic3r/GUI/DoubleSlider.cpp:1919 msgid "" "Select YES if you want to delete all saved tool changes, \n" "NO if you want all tool changes switch to color changes, \n" "or CANCEL to leave it unchanged." msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1920 +#: src/slic3r/GUI/DoubleSlider.cpp:1922 msgid "Do you want to delete all saved tool changes?" msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1922 +#: src/slic3r/GUI/DoubleSlider.cpp:1924 msgid "" "The last color change data was saved for a multi extruder printing with tool " "changes for whole print." msgstr "" -#: src/slic3r/GUI/DoubleSlider.cpp:1923 +#: src/slic3r/GUI/DoubleSlider.cpp:1925 msgid "Your current changes will delete all saved extruder (tool) changes." msgstr "" @@ -1208,11 +1208,11 @@ msgid "%s doesn't support percentage" msgstr "" #: src/slic3r/GUI/Field.cpp:195 src/slic3r/GUI/Field.cpp:226 -#: src/slic3r/GUI/GUI_ObjectLayers.cpp:383 +#: src/slic3r/GUI/Field.cpp:1298 src/slic3r/GUI/GUI_ObjectLayers.cpp:383 msgid "Invalid numeric input." msgstr "" -#: src/slic3r/GUI/Field.cpp:204 +#: src/slic3r/GUI/Field.cpp:204 src/slic3r/GUI/Field.cpp:1310 msgid "Input value is out of range" msgstr "" @@ -1300,8 +1300,8 @@ msgstr "" msgid "Firmware image:" msgstr "" -#: src/slic3r/GUI/FirmwareDialog.cpp:805 src/slic3r/GUI/Tab.cpp:1706 -#: src/slic3r/GUI/Tab.cpp:1768 +#: src/slic3r/GUI/FirmwareDialog.cpp:805 src/slic3r/GUI/Tab.cpp:1716 +#: src/slic3r/GUI/Tab.cpp:1778 msgid "Browse" msgstr "" @@ -1334,7 +1334,7 @@ msgid "Advanced: Output log" msgstr "" #: src/slic3r/GUI/FirmwareDialog.cpp:852 -#: src/slic3r/GUI/Mouse3DController.cpp:355 +#: src/slic3r/GUI/Mouse3DController.cpp:371 #: src/slic3r/GUI/PrintHostDialogs.cpp:161 msgid "Close" msgstr "" @@ -1353,390 +1353,409 @@ msgstr "" msgid "Cancelling..." msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:234 src/slic3r/GUI/GLCanvas3D.cpp:4596 +#: src/slic3r/GUI/GLCanvas3D.cpp:238 src/slic3r/GUI/GLCanvas3D.cpp:4962 msgid "Variable layer height" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:237 +#: src/slic3r/GUI/GLCanvas3D.cpp:241 msgid "Left mouse button:" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:240 +#: src/slic3r/GUI/GLCanvas3D.cpp:244 msgid "Add detail" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:243 +#: src/slic3r/GUI/GLCanvas3D.cpp:247 msgid "Right mouse button:" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:246 +#: src/slic3r/GUI/GLCanvas3D.cpp:250 msgid "Remove detail" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:249 +#: src/slic3r/GUI/GLCanvas3D.cpp:253 msgid "Shift + Left mouse button:" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:252 +#: src/slic3r/GUI/GLCanvas3D.cpp:256 msgid "Reset to base" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:255 +#: src/slic3r/GUI/GLCanvas3D.cpp:259 msgid "Shift + Right mouse button:" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:258 +#: src/slic3r/GUI/GLCanvas3D.cpp:262 msgid "Smoothing" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:261 +#: src/slic3r/GUI/GLCanvas3D.cpp:265 msgid "Mouse wheel:" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:264 +#: src/slic3r/GUI/GLCanvas3D.cpp:268 msgid "Increase/decrease edit area" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:267 +#: src/slic3r/GUI/GLCanvas3D.cpp:271 msgid "Adaptive" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:273 +#: src/slic3r/GUI/GLCanvas3D.cpp:277 msgid "Quality / Speed" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:277 +#: src/slic3r/GUI/GLCanvas3D.cpp:281 msgid "Higher print quality versus higher print speed." msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:288 +#: src/slic3r/GUI/GLCanvas3D.cpp:292 msgid "Smooth" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:294 src/libslic3r/PrintConfig.cpp:511 +#: src/slic3r/GUI/GLCanvas3D.cpp:298 src/libslic3r/PrintConfig.cpp:511 msgid "Radius" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:304 +#: src/slic3r/GUI/GLCanvas3D.cpp:308 msgid "Keep min" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:313 +#: src/slic3r/GUI/GLCanvas3D.cpp:317 msgid "Reset" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:599 +#: src/slic3r/GUI/GLCanvas3D.cpp:607 msgid "Variable layer height - Manual edit" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:685 +#: src/slic3r/GUI/GLCanvas3D.cpp:693 msgid "An object outside the print area was detected" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:686 +#: src/slic3r/GUI/GLCanvas3D.cpp:694 msgid "A toolpath outside the print area was detected" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:687 +#: src/slic3r/GUI/GLCanvas3D.cpp:695 msgid "SLA supports outside the print area were detected" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:688 +#: src/slic3r/GUI/GLCanvas3D.cpp:696 msgid "Some objects are not visible" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:690 +#: src/slic3r/GUI/GLCanvas3D.cpp:698 msgid "" "An object outside the print area was detected\n" "Resolve the current problem to continue slicing" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:904 src/slic3r/GUI/GLCanvas3D.cpp:933 +#: src/slic3r/GUI/GLCanvas3D.cpp:923 src/slic3r/GUI/GLCanvas3D.cpp:952 msgid "Default print color" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:934 src/slic3r/GUI/GLCanvas3D.cpp:943 -#: src/slic3r/GUI/GLCanvas3D.cpp:982 +#: src/slic3r/GUI/GLCanvas3D.cpp:953 src/slic3r/GUI/GLCanvas3D.cpp:962 +#: src/slic3r/GUI/GLCanvas3D.cpp:1001 msgid "Pause print or custom G-code" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:955 +#: src/slic3r/GUI/GLCanvas3D.cpp:974 #, possible-c-format msgid "up to %.2f mm" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:959 +#: src/slic3r/GUI/GLCanvas3D.cpp:978 #, possible-c-format msgid "above %.2f mm" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:963 +#: src/slic3r/GUI/GLCanvas3D.cpp:982 #, possible-c-format msgid "%.2f - %.2f mm" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:995 +#: src/slic3r/GUI/GLCanvas3D.cpp:1014 #, possible-c-format msgid "Color change for Extruder %d at %.2f mm" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:1305 +#: src/slic3r/GUI/GLCanvas3D.cpp:1332 msgid "Seq." msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:1701 +#: src/slic3r/GUI/GLCanvas3D.cpp:1439 +msgid "canvas_tooltip" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1466 +msgid "Slope visualization" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1468 +msgid "Facets' normal angle range (degrees)" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1495 src/slic3r/GUI/GUI_ObjectList.cpp:1718 +#: src/libslic3r/PrintConfig.cpp:335 +msgid "Default" +msgstr "" + +#: src/slic3r/GUI/GLCanvas3D.cpp:1880 msgid "Variable layer height - Reset" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:1709 +#: src/slic3r/GUI/GLCanvas3D.cpp:1888 msgid "Variable layer height - Adaptive" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:1717 +#: src/slic3r/GUI/GLCanvas3D.cpp:1896 msgid "Variable layer height - Smooth all" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:2053 +#: src/slic3r/GUI/GLCanvas3D.cpp:2332 msgid "Mirror Object" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:2921 -#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:570 +#: src/slic3r/GUI/GLCanvas3D.cpp:3224 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:519 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:746 msgid "Gizmo-Move" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3001 -#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:572 +#: src/slic3r/GUI/GLCanvas3D.cpp:3308 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:521 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:748 msgid "Gizmo-Rotate" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:3529 +#: src/slic3r/GUI/GLCanvas3D.cpp:3880 msgid "Move Object" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4070 +#: src/slic3r/GUI/GLCanvas3D.cpp:4425 msgid "Undo History" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4070 +#: src/slic3r/GUI/GLCanvas3D.cpp:4425 msgid "Redo History" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4088 +#: src/slic3r/GUI/GLCanvas3D.cpp:4446 #, possible-c-format msgid "Undo %1$d Action" msgid_plural "Undo %1$d Actions" msgstr[0] "" msgstr[1] "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4088 +#: src/slic3r/GUI/GLCanvas3D.cpp:4446 #, possible-c-format msgid "Redo %1$d Action" msgid_plural "Redo %1$d Actions" msgstr[0] "" msgstr[1] "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4490 +#: src/slic3r/GUI/GLCanvas3D.cpp:4856 msgid "Add..." msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4498 src/slic3r/GUI/GUI_ObjectList.cpp:1726 -#: src/slic3r/GUI/Plater.cpp:3998 src/slic3r/GUI/Plater.cpp:4020 -#: src/slic3r/GUI/Tab.cpp:3145 +#: src/slic3r/GUI/GLCanvas3D.cpp:4864 src/slic3r/GUI/GUI_ObjectList.cpp:1732 +#: src/slic3r/GUI/Plater.cpp:3935 src/slic3r/GUI/Plater.cpp:3957 +#: src/slic3r/GUI/Tab.cpp:3155 msgid "Delete" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4507 src/slic3r/GUI/KBShortcutsDialog.cpp:131 -#: src/slic3r/GUI/Plater.cpp:4740 +#: src/slic3r/GUI/GLCanvas3D.cpp:4873 src/slic3r/GUI/KBShortcutsDialog.cpp:131 +#: src/slic3r/GUI/Plater.cpp:4705 msgid "Delete all" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4516 src/slic3r/GUI/KBShortcutsDialog.cpp:157 -#: src/slic3r/GUI/Plater.cpp:2816 +#: src/slic3r/GUI/GLCanvas3D.cpp:4882 src/slic3r/GUI/KBShortcutsDialog.cpp:160 +#: src/slic3r/GUI/Plater.cpp:2815 msgid "Arrange" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4516 src/slic3r/GUI/KBShortcutsDialog.cpp:158 +#: src/slic3r/GUI/GLCanvas3D.cpp:4882 src/slic3r/GUI/KBShortcutsDialog.cpp:161 msgid "Arrange selection" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4528 +#: src/slic3r/GUI/GLCanvas3D.cpp:4894 msgid "Copy" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4537 +#: src/slic3r/GUI/GLCanvas3D.cpp:4903 msgid "Paste" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4549 src/slic3r/GUI/Plater.cpp:3855 -#: src/slic3r/GUI/Plater.cpp:3867 src/slic3r/GUI/Plater.cpp:4007 +#: src/slic3r/GUI/GLCanvas3D.cpp:4915 src/slic3r/GUI/Plater.cpp:3794 +#: src/slic3r/GUI/Plater.cpp:3806 src/slic3r/GUI/Plater.cpp:3944 msgid "Add instance" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4560 src/slic3r/GUI/Plater.cpp:4009 +#: src/slic3r/GUI/GLCanvas3D.cpp:4926 src/slic3r/GUI/Plater.cpp:3946 msgid "Remove instance" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4573 +#: src/slic3r/GUI/GLCanvas3D.cpp:4939 msgid "Split to objects" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4583 src/slic3r/GUI/GUI_ObjectList.cpp:1495 +#: src/slic3r/GUI/GLCanvas3D.cpp:4949 src/slic3r/GUI/GUI_ObjectList.cpp:1501 msgid "Split to parts" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4647 src/slic3r/GUI/KBShortcutsDialog.cpp:132 -#: src/slic3r/GUI/MainFrame.cpp:662 +#: src/slic3r/GUI/GLCanvas3D.cpp:5013 src/slic3r/GUI/KBShortcutsDialog.cpp:132 +#: src/slic3r/GUI/MainFrame.cpp:717 msgid "Undo" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4647 src/slic3r/GUI/GLCanvas3D.cpp:4680 -msgid "Click right mouse button to open History" +#: src/slic3r/GUI/GLCanvas3D.cpp:5013 src/slic3r/GUI/GLCanvas3D.cpp:5052 +msgid "Click right mouse button to open/close History" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4664 +#: src/slic3r/GUI/GLCanvas3D.cpp:5036 msgid "Next Undo action: %1%" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4680 src/slic3r/GUI/KBShortcutsDialog.cpp:133 -#: src/slic3r/GUI/MainFrame.cpp:665 +#: src/slic3r/GUI/GLCanvas3D.cpp:5052 src/slic3r/GUI/KBShortcutsDialog.cpp:133 +#: src/slic3r/GUI/MainFrame.cpp:720 msgid "Redo" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:4696 +#: src/slic3r/GUI/GLCanvas3D.cpp:5074 msgid "Next Redo action: %1%" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:6609 +#: src/slic3r/GUI/GLCanvas3D.cpp:7064 msgid "Selection-Add from rectangle" msgstr "" -#: src/slic3r/GUI/GLCanvas3D.cpp:6628 +#: src/slic3r/GUI/GLCanvas3D.cpp:7083 msgid "Selection-Remove from rectangle" msgstr "" -#: src/slic3r/GUI/GLCanvas3DManager.cpp:306 +#: src/slic3r/GUI/GLCanvas3DManager.cpp:365 #, possible-c-format msgid "" "PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n" "while OpenGL version %s, render %s, vendor %s was detected." msgstr "" -#: src/slic3r/GUI/GLCanvas3DManager.cpp:309 +#: src/slic3r/GUI/GLCanvas3DManager.cpp:368 msgid "You may need to update your graphics card driver." msgstr "" -#: src/slic3r/GUI/GLCanvas3DManager.cpp:312 +#: src/slic3r/GUI/GLCanvas3DManager.cpp:371 msgid "" "As a workaround, you may run PrusaSlicer with a software rendered 3D " "graphics by running prusa-slicer.exe with the --sw_renderer parameter." msgstr "" -#: src/slic3r/GUI/GLCanvas3DManager.cpp:314 +#: src/slic3r/GUI/GLCanvas3DManager.cpp:373 msgid "Unsupported OpenGL version" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:42 -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:144 src/libslic3r/PrintConfig.cpp:3424 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:47 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:145 src/libslic3r/PrintConfig.cpp:3424 msgid "Cut" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:168 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:169 msgid "Keep upper part" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:169 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:170 msgid "Keep lower part" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:170 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:171 msgid "Rotate lower part upwards" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:175 +#: src/slic3r/GUI/Gizmos/GLGizmoCut.cpp:176 msgid "Perform cut" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp:45 +#: src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp:41 msgid "Place on face" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:40 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:38 msgid "Hollow this object" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:41 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:39 msgid "Preview hollowed and drilled model" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:42 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:40 msgid "Offset" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:43 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:41 msgid "Quality" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:44 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:42 #: src/libslic3r/PrintConfig.cpp:2951 msgid "Closing distance" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:45 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:43 msgid "Hole diameter" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:46 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:44 msgid "Hole depth" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:47 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:45 msgid "Remove selected holes" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:48 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:46 msgid "Remove all holes" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:49 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:57 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:47 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:56 msgid "Clipping of view" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:50 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:58 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:48 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:57 msgid "Reset direction" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:51 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:49 msgid "Show supports" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:423 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:307 msgid "Add drainage hole" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:541 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:428 msgid "Delete drainage hole" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:815 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:628 msgid "Hollowing parameter change" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:887 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:699 msgid "Change drainage hole diameter" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:979 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:791 msgid "Hollow and drill" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:1061 +#: src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp:846 msgid "Move drainage hole" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:48 +#: src/slic3r/GUI/Gizmos/GLGizmoMove.cpp:64 msgid "Move" msgstr "" @@ -1748,7 +1767,7 @@ msgstr "" msgid "Rotate" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoScale.cpp:47 +#: src/slic3r/GUI/Gizmos/GLGizmoScale.cpp:79 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:230 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:500 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:518 @@ -1756,194 +1775,195 @@ msgstr "" msgid "Scale" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:47 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:46 msgid "Head diameter" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:48 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:47 msgid "Lock supports under new islands" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:49 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1361 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:48 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1216 msgid "Remove selected points" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:50 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:49 msgid "Remove all points" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:51 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1364 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:50 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1219 msgid "Apply changes" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:52 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1365 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:51 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1220 msgid "Discard changes" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:53 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:52 msgid "Minimal points distance" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:54 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:53 #: src/libslic3r/PrintConfig.cpp:2781 msgid "Support points density" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:55 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1367 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:54 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1222 msgid "Auto-generate points" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:56 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:55 msgid "Manual editing" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:484 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:373 msgid "Add support point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:621 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:513 msgid "Delete support point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:810 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:693 msgid "Change point head diameter" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:878 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:761 msgid "Support parameter change" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:986 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:868 msgid "SLA Support Points" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1007 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:893 msgid "SLA gizmo turned on" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1035 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:909 msgid "Do you want to save your manually edited support points?" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1036 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:910 msgid "Save changes?" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1048 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:922 msgid "SLA gizmo turned off" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1087 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:953 msgid "Move support point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1186 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1046 msgid "Support points edit" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1258 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1125 msgid "Autogeneration will erase all manually edited points." msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1259 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1126 msgid "Are you sure you want to do it?" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1260 src/slic3r/GUI/GUI.cpp:258 -#: src/slic3r/GUI/Tab.cpp:3075 src/slic3r/GUI/WipeTowerDialog.cpp:45 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1127 src/slic3r/GUI/GUI.cpp:258 +#: src/slic3r/GUI/Tab.cpp:3085 src/slic3r/GUI/WipeTowerDialog.cpp:45 #: src/slic3r/GUI/WipeTowerDialog.cpp:366 msgid "Warning" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1263 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1132 msgid "Autogenerate support points" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1324 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1179 msgid "SLA gizmo keyboard shortcuts" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1335 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1190 msgid "Note: some shortcuts work in (non)editing mode only." msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1353 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1356 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1357 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1208 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1211 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1212 msgid "Left click" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1353 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1208 msgid "Add point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1354 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1209 msgid "Right click" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1354 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1209 msgid "Remove point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1355 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1358 -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1359 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1210 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1213 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1214 msgid "Drag" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1355 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1210 msgid "Move point" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1356 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1211 msgid "Add point to selection" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1357 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1212 msgid "Remove point from selection" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1358 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1213 msgid "Select by rectangle" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1359 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1214 msgid "Deselect by rectangle" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1360 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1215 msgid "Select all points" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1362 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1217 msgid "Mouse wheel" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1362 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1217 msgid "Move clipping plane" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1363 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1218 msgid "Reset clipping plane" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1366 +#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1221 msgid "Switch to editing mode" msgstr "" -#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:497 -msgid "Gizmo-Place on Face" -msgstr "" - -#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:571 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:520 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:747 msgid "Gizmo-Scale" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:184 +#: src/slic3r/GUI/Gizmos/GLGizmosManager.cpp:662 +msgid "Gizmo-Place on Face" +msgstr "" + +#: src/slic3r/GUI/GUI_App.cpp:234 #, possible-c-format msgid "" "%s has encountered an error. It was likely caused by running out of memory. " @@ -1953,184 +1973,177 @@ msgid "" "The application will now terminate." msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:187 +#: src/slic3r/GUI/GUI_App.cpp:237 msgid "Fatal error" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:487 +#: src/slic3r/GUI/GUI_App.cpp:554 msgid "Changing of an application language" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:490 src/slic3r/GUI/GUI_App.cpp:498 +#: src/slic3r/GUI/GUI_App.cpp:557 src/slic3r/GUI/GUI_App.cpp:565 msgid "Recreating" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:503 +#: src/slic3r/GUI/GUI_App.cpp:570 msgid "Loading of current presets" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:508 +#: src/slic3r/GUI/GUI_App.cpp:575 msgid "Loading of a mode view" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:587 +#: src/slic3r/GUI/GUI_App.cpp:654 msgid "Choose one file (3MF/AMF):" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:599 +#: src/slic3r/GUI/GUI_App.cpp:672 msgid "Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:660 +#: src/slic3r/GUI/GUI_App.cpp:733 msgid "Select the language" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:660 +#: src/slic3r/GUI/GUI_App.cpp:733 msgid "Language" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:828 +#: src/slic3r/GUI/GUI_App.cpp:911 #, possible-c-format msgid "Run %s" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:831 +#: src/slic3r/GUI/GUI_App.cpp:914 msgid "&Configuration Snapshots" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:831 +#: src/slic3r/GUI/GUI_App.cpp:914 msgid "Inspect / activate configuration snapshots" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:832 +#: src/slic3r/GUI/GUI_App.cpp:915 msgid "Take Configuration &Snapshot" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:832 +#: src/slic3r/GUI/GUI_App.cpp:915 msgid "Capture a configuration snapshot" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:833 +#: src/slic3r/GUI/GUI_App.cpp:916 msgid "Check for updates" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:833 +#: src/slic3r/GUI/GUI_App.cpp:916 msgid "Check for configuration updates" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:835 +#: src/slic3r/GUI/GUI_App.cpp:918 msgid "&Preferences" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:841 +#: src/slic3r/GUI/GUI_App.cpp:924 msgid "Application preferences" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:844 src/slic3r/GUI/wxExtensions.cpp:756 +#: src/slic3r/GUI/GUI_App.cpp:927 src/slic3r/GUI/wxExtensions.cpp:756 msgid "Simple" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:844 +#: src/slic3r/GUI/GUI_App.cpp:927 msgid "Simple View Mode" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:845 src/slic3r/GUI/GUI_ObjectList.cpp:104 -#: src/slic3r/GUI/GUI_ObjectList.cpp:620 src/slic3r/GUI/Tab.cpp:1147 -#: src/slic3r/GUI/Tab.cpp:1162 src/slic3r/GUI/Tab.cpp:1262 -#: src/slic3r/GUI/Tab.cpp:1265 src/slic3r/GUI/Tab.cpp:1526 -#: src/slic3r/GUI/Tab.cpp:1990 src/slic3r/GUI/Tab.cpp:3720 -#: src/slic3r/GUI/wxExtensions.cpp:757 src/libslic3r/PrintConfig.cpp:88 -#: src/libslic3r/PrintConfig.cpp:119 src/libslic3r/PrintConfig.cpp:223 -#: src/libslic3r/PrintConfig.cpp:1037 src/libslic3r/PrintConfig.cpp:2283 -#: src/libslic3r/PrintConfig.cpp:2455 +#: src/slic3r/GUI/GUI_App.cpp:929 src/slic3r/GUI/wxExtensions.cpp:758 +msgctxt "Mode" msgid "Advanced" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:845 +#: src/slic3r/GUI/GUI_App.cpp:929 msgid "Advanced View Mode" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:846 src/slic3r/GUI/wxExtensions.cpp:758 +#: src/slic3r/GUI/GUI_App.cpp:930 src/slic3r/GUI/wxExtensions.cpp:759 msgid "Expert" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:846 +#: src/slic3r/GUI/GUI_App.cpp:930 msgid "Expert View Mode" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:851 +#: src/slic3r/GUI/GUI_App.cpp:935 msgid "Mode" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:851 +#: src/slic3r/GUI/GUI_App.cpp:935 #, possible-c-format msgid "%s View Mode" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:853 +#: src/slic3r/GUI/GUI_App.cpp:937 msgid "&Language" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:855 +#: src/slic3r/GUI/GUI_App.cpp:939 msgid "Flash printer &firmware" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:855 +#: src/slic3r/GUI/GUI_App.cpp:939 msgid "Upload a firmware image into an Arduino based printer" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:870 +#: src/slic3r/GUI/GUI_App.cpp:954 msgid "Taking configuration snapshot" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:870 +#: src/slic3r/GUI/GUI_App.cpp:954 msgid "Snapshot name" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:917 +#: src/slic3r/GUI/GUI_App.cpp:1001 msgid "" "Switching the language will trigger application restart.\n" "You will lose content of the plater." msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:919 +#: src/slic3r/GUI/GUI_App.cpp:1003 msgid "Do you want to proceed?" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:920 +#: src/slic3r/GUI/GUI_App.cpp:1004 msgid "Language selection" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:944 +#: src/slic3r/GUI/GUI_App.cpp:1028 msgid "&Configuration" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:968 +#: src/slic3r/GUI/GUI_App.cpp:1052 msgid "The presets on the following tabs were modified" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:968 src/slic3r/GUI/Tab.cpp:2935 +#: src/slic3r/GUI/GUI_App.cpp:1052 src/slic3r/GUI/Tab.cpp:2945 msgid "Discard changes and continue anyway?" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:971 +#: src/slic3r/GUI/GUI_App.cpp:1055 msgid "Unsaved Presets" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:1120 src/slic3r/GUI/Tab.cpp:2947 +#: src/slic3r/GUI/GUI_App.cpp:1204 src/slic3r/GUI/Tab.cpp:2957 msgid "It's impossible to print multi-part object(s) with SLA technology." msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:1121 +#: src/slic3r/GUI/GUI_App.cpp:1205 msgid "Please check and fix your object list." msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:1122 src/slic3r/GUI/Plater.cpp:2375 -#: src/slic3r/GUI/Tab.cpp:2949 +#: src/slic3r/GUI/GUI_App.cpp:1206 src/slic3r/GUI/Plater.cpp:2377 +#: src/slic3r/GUI/Tab.cpp:2959 msgid "Attention!" msgstr "" -#: src/slic3r/GUI/GUI_App.cpp:1139 +#: src/slic3r/GUI/GUI_App.cpp:1223 msgid "Select a gcode file:" msgstr "" @@ -2151,7 +2164,7 @@ msgid "Add layer range" msgstr "" #: src/slic3r/GUI/GUI_ObjectList.cpp:34 src/slic3r/GUI/GUI_ObjectList.cpp:95 -#: src/slic3r/GUI/GUI_ObjectList.cpp:611 src/libslic3r/PrintConfig.cpp:72 +#: src/slic3r/GUI/GUI_ObjectList.cpp:617 src/libslic3r/PrintConfig.cpp:72 #: src/libslic3r/PrintConfig.cpp:175 src/libslic3r/PrintConfig.cpp:184 #: src/libslic3r/PrintConfig.cpp:408 src/libslic3r/PrintConfig.cpp:470 #: src/libslic3r/PrintConfig.cpp:478 src/libslic3r/PrintConfig.cpp:890 @@ -2163,8 +2176,8 @@ msgid "Layers and Perimeters" msgstr "" #: src/slic3r/GUI/GUI_ObjectList.cpp:36 src/slic3r/GUI/GUI_ObjectList.cpp:97 -#: src/slic3r/GUI/GUI_ObjectList.cpp:613 src/slic3r/GUI/GUI_Preview.cpp:246 -#: src/slic3r/GUI/Tab.cpp:1181 src/slic3r/GUI/Tab.cpp:1182 +#: src/slic3r/GUI/GUI_ObjectList.cpp:619 src/slic3r/GUI/GUI_Preview.cpp:305 +#: src/slic3r/GUI/Tab.cpp:1188 src/slic3r/GUI/Tab.cpp:1189 #: src/libslic3r/ExtrusionEntity.cpp:319 src/libslic3r/PrintConfig.cpp:370 #: src/libslic3r/PrintConfig.cpp:1502 src/libslic3r/PrintConfig.cpp:1873 #: src/libslic3r/PrintConfig.cpp:1879 src/libslic3r/PrintConfig.cpp:1887 @@ -2180,7 +2193,7 @@ msgid "Support material" msgstr "" #: src/slic3r/GUI/GUI_ObjectList.cpp:39 src/slic3r/GUI/GUI_ObjectList.cpp:101 -#: src/slic3r/GUI/GUI_ObjectList.cpp:617 src/libslic3r/PrintConfig.cpp:2259 +#: src/slic3r/GUI/GUI_ObjectList.cpp:623 src/libslic3r/PrintConfig.cpp:2259 #: src/libslic3r/PrintConfig.cpp:2267 msgid "Wipe options" msgstr "" @@ -2205,8 +2218,8 @@ msgstr "" msgid "Add support blocker" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:98 src/slic3r/GUI/GUI_ObjectList.cpp:614 -#: src/slic3r/GUI/GUI_Preview.cpp:224 src/slic3r/GUI/Tab.cpp:1206 +#: src/slic3r/GUI/GUI_ObjectList.cpp:98 src/slic3r/GUI/GUI_ObjectList.cpp:620 +#: src/slic3r/GUI/GUI_Preview.cpp:283 src/slic3r/GUI/Tab.cpp:1213 #: src/libslic3r/PrintConfig.cpp:235 src/libslic3r/PrintConfig.cpp:458 #: src/libslic3r/PrintConfig.cpp:919 src/libslic3r/PrintConfig.cpp:1048 #: src/libslic3r/PrintConfig.cpp:1431 src/libslic3r/PrintConfig.cpp:1668 @@ -2215,15 +2228,15 @@ msgstr "" msgid "Speed" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:99 src/slic3r/GUI/GUI_ObjectList.cpp:615 -#: src/slic3r/GUI/Tab.cpp:1241 src/slic3r/GUI/Tab.cpp:1861 +#: src/slic3r/GUI/GUI_ObjectList.cpp:99 src/slic3r/GUI/GUI_ObjectList.cpp:621 +#: src/slic3r/GUI/Tab.cpp:1248 src/slic3r/GUI/Tab.cpp:1871 #: src/libslic3r/PrintConfig.cpp:488 src/libslic3r/PrintConfig.cpp:1002 #: src/libslic3r/PrintConfig.cpp:1409 src/libslic3r/PrintConfig.cpp:1744 #: src/libslic3r/PrintConfig.cpp:1945 src/libslic3r/PrintConfig.cpp:1972 msgid "Extruders" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:100 src/slic3r/GUI/GUI_ObjectList.cpp:616 +#: src/slic3r/GUI/GUI_ObjectList.cpp:100 src/slic3r/GUI/GUI_ObjectList.cpp:622 #: src/libslic3r/PrintConfig.cpp:447 src/libslic3r/PrintConfig.cpp:555 #: src/libslic3r/PrintConfig.cpp:877 src/libslic3r/PrintConfig.cpp:1010 #: src/libslic3r/PrintConfig.cpp:1418 src/libslic3r/PrintConfig.cpp:1764 @@ -2231,9 +2244,20 @@ msgstr "" msgid "Extrusion Width" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:106 src/slic3r/GUI/GUI_ObjectList.cpp:622 -#: src/slic3r/GUI/Plater.cpp:500 src/slic3r/GUI/Tab.cpp:3661 -#: src/slic3r/GUI/Tab.cpp:3662 src/libslic3r/PrintConfig.cpp:2621 +#: src/slic3r/GUI/GUI_ObjectList.cpp:104 src/slic3r/GUI/GUI_ObjectList.cpp:626 +#: src/slic3r/GUI/Tab.cpp:1154 src/slic3r/GUI/Tab.cpp:1169 +#: src/slic3r/GUI/Tab.cpp:1269 src/slic3r/GUI/Tab.cpp:1272 +#: src/slic3r/GUI/Tab.cpp:1536 src/slic3r/GUI/Tab.cpp:2000 +#: src/slic3r/GUI/Tab.cpp:3730 src/libslic3r/PrintConfig.cpp:88 +#: src/libslic3r/PrintConfig.cpp:119 src/libslic3r/PrintConfig.cpp:223 +#: src/libslic3r/PrintConfig.cpp:1037 src/libslic3r/PrintConfig.cpp:2283 +#: src/libslic3r/PrintConfig.cpp:2455 +msgid "Advanced" +msgstr "" + +#: src/slic3r/GUI/GUI_ObjectList.cpp:106 src/slic3r/GUI/GUI_ObjectList.cpp:628 +#: src/slic3r/GUI/Plater.cpp:502 src/slic3r/GUI/Tab.cpp:3671 +#: src/slic3r/GUI/Tab.cpp:3672 src/libslic3r/PrintConfig.cpp:2621 #: src/libslic3r/PrintConfig.cpp:2628 src/libslic3r/PrintConfig.cpp:2637 #: src/libslic3r/PrintConfig.cpp:2646 src/libslic3r/PrintConfig.cpp:2656 #: src/libslic3r/PrintConfig.cpp:2692 src/libslic3r/PrintConfig.cpp:2699 @@ -2245,9 +2269,9 @@ msgstr "" msgid "Supports" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:107 src/slic3r/GUI/GUI_ObjectList.cpp:623 -#: src/slic3r/GUI/Plater.cpp:640 src/slic3r/GUI/Tab.cpp:3695 -#: src/slic3r/GUI/Tab.cpp:3696 src/libslic3r/PrintConfig.cpp:2798 +#: src/slic3r/GUI/GUI_ObjectList.cpp:107 src/slic3r/GUI/GUI_ObjectList.cpp:629 +#: src/slic3r/GUI/Plater.cpp:642 src/slic3r/GUI/Tab.cpp:3705 +#: src/slic3r/GUI/Tab.cpp:3706 src/libslic3r/PrintConfig.cpp:2798 #: src/libslic3r/PrintConfig.cpp:2805 src/libslic3r/PrintConfig.cpp:2819 #: src/libslic3r/PrintConfig.cpp:2830 src/libslic3r/PrintConfig.cpp:2840 #: src/libslic3r/PrintConfig.cpp:2862 src/libslic3r/PrintConfig.cpp:2873 @@ -2257,8 +2281,8 @@ msgstr "" msgid "Pad" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:108 src/slic3r/GUI/Tab.cpp:3713 -#: src/slic3r/GUI/Tab.cpp:3714 src/libslic3r/SLA/Hollowing.cpp:46 +#: src/slic3r/GUI/GUI_ObjectList.cpp:108 src/slic3r/GUI/Tab.cpp:3723 +#: src/slic3r/GUI/Tab.cpp:3724 src/libslic3r/SLA/Hollowing.cpp:46 #: src/libslic3r/SLA/Hollowing.cpp:58 src/libslic3r/SLA/Hollowing.cpp:67 #: src/libslic3r/SLA/Hollowing.cpp:76 src/libslic3r/PrintConfig.cpp:2926 #: src/libslic3r/PrintConfig.cpp:2933 src/libslic3r/PrintConfig.cpp:2943 @@ -2266,343 +2290,339 @@ msgstr "" msgid "Hollowing" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:275 +#: src/slic3r/GUI/GUI_ObjectList.cpp:282 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:153 msgid "Name" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:283 src/slic3r/GUI/Tab.cpp:1490 +#: src/slic3r/GUI/GUI_ObjectList.cpp:290 src/slic3r/GUI/Tab.cpp:1500 #: src/slic3r/GUI/wxExtensions.cpp:598 src/libslic3r/PrintConfig.cpp:487 msgid "Extruder" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:287 src/slic3r/GUI/GUI_ObjectList.cpp:400 +#: src/slic3r/GUI/GUI_ObjectList.cpp:294 src/slic3r/GUI/GUI_ObjectList.cpp:407 msgid "Editing" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:345 +#: src/slic3r/GUI/GUI_ObjectList.cpp:352 #, possible-c-format msgid "Auto-repaired (%d errors):" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:352 +#: src/slic3r/GUI/GUI_ObjectList.cpp:359 msgid "degenerate facets" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:353 +#: src/slic3r/GUI/GUI_ObjectList.cpp:360 msgid "edges fixed" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:354 +#: src/slic3r/GUI/GUI_ObjectList.cpp:361 msgid "facets removed" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:355 +#: src/slic3r/GUI/GUI_ObjectList.cpp:362 msgid "facets added" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:356 +#: src/slic3r/GUI/GUI_ObjectList.cpp:363 msgid "facets reversed" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:357 +#: src/slic3r/GUI/GUI_ObjectList.cpp:364 msgid "backwards edges" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:365 +#: src/slic3r/GUI/GUI_ObjectList.cpp:372 msgid "Right button click the icon to fix STL through Netfabb" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:402 +#: src/slic3r/GUI/GUI_ObjectList.cpp:409 msgid "Right button click the icon to change the object settings" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:404 +#: src/slic3r/GUI/GUI_ObjectList.cpp:411 msgid "Click the icon to change the object settings" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:408 +#: src/slic3r/GUI/GUI_ObjectList.cpp:415 msgid "Right button click the icon to change the object printable property" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:410 +#: src/slic3r/GUI/GUI_ObjectList.cpp:417 msgid "Click the icon to change the object printable property" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:455 src/slic3r/GUI/GUI_ObjectList.cpp:467 -#: src/slic3r/GUI/GUI_ObjectList.cpp:925 src/slic3r/GUI/GUI_ObjectList.cpp:4036 -#: src/slic3r/GUI/GUI_ObjectList.cpp:4046 -#: src/slic3r/GUI/GUI_ObjectList.cpp:4081 -#: src/slic3r/GUI/ObjectDataViewModel.cpp:200 -#: src/slic3r/GUI/ObjectDataViewModel.cpp:257 -#: src/slic3r/GUI/ObjectDataViewModel.cpp:282 -#: src/slic3r/GUI/ObjectDataViewModel.cpp:490 -#: src/slic3r/GUI/ObjectDataViewModel.cpp:1753 +#: src/slic3r/GUI/GUI_ObjectList.cpp:462 src/slic3r/GUI/GUI_ObjectList.cpp:474 +#: src/slic3r/GUI/GUI_ObjectList.cpp:931 src/slic3r/GUI/GUI_ObjectList.cpp:4051 +#: src/slic3r/GUI/GUI_ObjectList.cpp:4061 +#: src/slic3r/GUI/GUI_ObjectList.cpp:4096 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:202 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:259 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:284 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:492 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:1755 msgid "default" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:534 +#: src/slic3r/GUI/GUI_ObjectList.cpp:540 msgid "Change Extruder" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:549 +#: src/slic3r/GUI/GUI_ObjectList.cpp:555 msgid "Rename Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:549 +#: src/slic3r/GUI/GUI_ObjectList.cpp:555 msgid "Rename Sub-object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1099 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3850 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1105 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3865 msgid "Instances to Separated Objects" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1114 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1120 msgid "Volumes in Object reordered" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1114 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1120 msgid "Object reordered" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1190 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1538 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1196 #: src/slic3r/GUI/GUI_ObjectList.cpp:1544 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1857 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1550 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1863 #, possible-c-format msgid "Quick Add Settings (%s)" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1273 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1279 msgid "Select showing settings" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1322 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1328 msgid "Add Settings for Layers" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1323 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1329 msgid "Add Settings for Sub-object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1324 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1330 msgid "Add Settings for Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1394 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1400 msgid "Add Settings Bundle for Height range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1395 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1401 msgid "Add Settings Bundle for Sub-object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1396 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1402 msgid "Add Settings Bundle for Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1435 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1441 msgid "Load" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1440 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1472 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1476 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1446 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1478 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1482 msgid "Box" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1440 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1446 msgid "Cylinder" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1440 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1446 msgid "Sphere" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1440 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1446 msgid "Slab" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1508 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1514 msgid "Height range Modifier" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1517 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1523 msgid "Add settings" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1597 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1603 msgid "Change type" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1607 -#: src/slic3r/GUI/GUI_ObjectList.cpp:1619 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1613 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1625 msgid "Set as a Separated Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1619 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1625 msgid "Set as a Separated Objects" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1629 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1635 msgid "Printable" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1644 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1650 msgid "Rename" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1655 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1661 msgid "Fix through the Netfabb" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1665 src/slic3r/GUI/Plater.cpp:4033 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1671 src/slic3r/GUI/Plater.cpp:3970 msgid "Export as STL" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1672 -#: src/slic3r/GUI/GUI_ObjectList.cpp:4025 src/slic3r/GUI/Plater.cpp:4001 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1678 +#: src/slic3r/GUI/GUI_ObjectList.cpp:4040 src/slic3r/GUI/Plater.cpp:3938 msgid "Reload the selected volumes from disk" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1679 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1685 msgid "Set extruder for selected items" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1712 src/libslic3r/PrintConfig.cpp:335 -msgid "Default" -msgstr "" - -#: src/slic3r/GUI/GUI_ObjectList.cpp:1732 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1738 msgid "Scale to print volume" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1732 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1738 msgid "Scale the selected object to fit the print volume" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1801 -#: src/slic3r/GUI/GUI_ObjectList.cpp:2059 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1807 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2065 msgid "Add Shape" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1887 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1893 msgid "Load Part" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:1926 +#: src/slic3r/GUI/GUI_ObjectList.cpp:1932 msgid "Error!" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2001 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2007 msgid "Add Generic Subobject" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2030 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2036 msgid "Generic" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2148 -#: src/slic3r/GUI/GUI_ObjectList.cpp:2250 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2154 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2256 msgid "Last instance of an object cannot be deleted." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2160 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2166 msgid "Delete Settings" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2184 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2190 msgid "Delete All Instances from Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2200 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2206 msgid "Delete Height Range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2231 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2237 msgid "From Object List You can't delete the last solid part from object." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2235 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2241 msgid "Delete Subobject" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2254 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2260 msgid "Delete Instance" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2278 src/slic3r/GUI/Plater.cpp:3045 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2284 src/slic3r/GUI/Plater.cpp:2978 msgid "" "The selected object couldn't be split because it contains only one part." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2282 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2288 msgid "Split to Parts" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2336 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2342 msgid "Add Layers" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2462 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2468 msgid "Group manipulation" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2474 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2480 msgid "Object manipulation" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2487 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2493 msgid "Object Settings to modify" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2491 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2497 msgid "Part Settings to modify" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2496 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2502 msgid "Layer range Settings to modify" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2502 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2508 msgid "Part manipulation" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2508 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2514 msgid "Instance manipulation" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2515 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2521 msgid "Height ranges" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2515 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2521 msgid "Settings for height range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2701 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2707 msgid "Delete Selected Item" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2838 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2844 msgid "Delete Selected" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:2914 -#: src/slic3r/GUI/GUI_ObjectList.cpp:2942 -#: src/slic3r/GUI/GUI_ObjectList.cpp:2962 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2920 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2948 +#: src/slic3r/GUI/GUI_ObjectList.cpp:2968 msgid "Add Height Range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3008 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3014 msgid "" "Cannot insert a new layer range after the current layer range.\n" "The next layer range is too thin to be split to two\n" "without violating the minimum layer height." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3012 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3018 msgid "" "Cannot insert a new layer range between the current and the next layer " "range.\n" @@ -2610,120 +2630,120 @@ msgid "" "is thinner than the minimum layer height allowed." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3017 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3023 msgid "" "Cannot insert a new layer range after the current layer range.\n" "Current layer range overlaps with the next layer range." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3070 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3082 msgid "Edit Height Range" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3360 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3375 msgid "Selection-Remove from list" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3368 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3383 msgid "Selection-Add from list" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3486 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3501 msgid "Object or Instance" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3487 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3620 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3502 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3635 msgid "Part" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3487 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3502 msgid "Layer" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3489 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3504 msgid "Unsupported selection" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3490 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3505 #, possible-c-format msgid "You started your selection with %s Item." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3491 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3506 #, possible-c-format msgid "In this mode you can select only other %s Items%s" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3494 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3509 msgid "of a current Object" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3499 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3574 src/slic3r/GUI/Plater.cpp:141 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3514 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3589 src/slic3r/GUI/Plater.cpp:143 msgid "Info" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3615 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3630 msgid "You can't change a type of the last solid part of the object." msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3620 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3635 msgid "Modifier" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3620 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3635 msgid "Support Enforcer" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3620 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3635 msgid "Support Blocker" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3622 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3637 msgid "Select type of part" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3627 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3642 msgid "Change Part Type" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3872 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3887 msgid "Enter new name" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3872 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3887 msgid "Renaming" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3888 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3995 src/slic3r/GUI/Tab.cpp:3519 -#: src/slic3r/GUI/Tab.cpp:3523 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3903 +#: src/slic3r/GUI/GUI_ObjectList.cpp:4010 src/slic3r/GUI/Tab.cpp:3529 +#: src/slic3r/GUI/Tab.cpp:3533 msgid "The supplied name is not valid;" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:3889 -#: src/slic3r/GUI/GUI_ObjectList.cpp:3996 src/slic3r/GUI/Tab.cpp:3520 +#: src/slic3r/GUI/GUI_ObjectList.cpp:3904 +#: src/slic3r/GUI/GUI_ObjectList.cpp:4011 src/slic3r/GUI/Tab.cpp:3530 msgid "the following characters are not allowed:" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:4040 +#: src/slic3r/GUI/GUI_ObjectList.cpp:4055 msgid "Select extruder number:" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:4041 +#: src/slic3r/GUI/GUI_ObjectList.cpp:4056 msgid "This extruder will be set for selected items" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:4066 +#: src/slic3r/GUI/GUI_ObjectList.cpp:4081 msgid "Change Extruders" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:4163 src/slic3r/GUI/Selection.cpp:1475 +#: src/slic3r/GUI/GUI_ObjectList.cpp:4178 src/slic3r/GUI/Selection.cpp:1481 msgid "Set Printable" msgstr "" -#: src/slic3r/GUI/GUI_ObjectList.cpp:4163 src/slic3r/GUI/Selection.cpp:1475 +#: src/slic3r/GUI/GUI_ObjectList.cpp:4178 src/slic3r/GUI/Selection.cpp:1481 msgid "Set Unprintable" msgstr "" @@ -2741,7 +2761,7 @@ msgstr "" msgid "Select coordinate space, in which the transformation will be performed." msgstr "" -#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:155 src/libslic3r/GCode.cpp:641 +#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:155 src/libslic3r/GCode.cpp:638 msgid "Object name" msgstr "" @@ -2752,8 +2772,8 @@ msgstr "" #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:216 #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:458 -#: src/slic3r/GUI/Mouse3DController.cpp:290 -#: src/slic3r/GUI/Mouse3DController.cpp:313 +#: src/slic3r/GUI/Mouse3DController.cpp:295 +#: src/slic3r/GUI/Mouse3DController.cpp:318 msgid "Rotation" msgstr "" @@ -2842,117 +2862,117 @@ msgstr "" msgid "Change Option %s" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:218 +#: src/slic3r/GUI/GUI_Preview.cpp:277 msgid "View" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:221 src/slic3r/GUI/GUI_Preview.cpp:575 -#: src/libslic3r/GCode/PreviewData.cpp:345 +#: src/slic3r/GUI/GUI_Preview.cpp:280 src/slic3r/GUI/GUI_Preview.cpp:641 +#: src/libslic3r/GCode/PreviewData.cpp:346 msgid "Feature type" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:222 src/libslic3r/PrintConfig.cpp:500 +#: src/slic3r/GUI/GUI_Preview.cpp:281 src/libslic3r/PrintConfig.cpp:500 msgid "Height" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:223 src/libslic3r/PrintConfig.cpp:2245 +#: src/slic3r/GUI/GUI_Preview.cpp:282 src/libslic3r/PrintConfig.cpp:2245 msgid "Width" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:225 src/slic3r/GUI/Tab.cpp:1513 +#: src/slic3r/GUI/GUI_Preview.cpp:284 src/slic3r/GUI/Tab.cpp:1523 msgid "Fan speed" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:226 +#: src/slic3r/GUI/GUI_Preview.cpp:285 msgid "Volumetric flow rate" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:227 src/slic3r/GUI/GUI_Preview.cpp:335 -#: src/slic3r/GUI/GUI_Preview.cpp:519 src/slic3r/GUI/GUI_Preview.cpp:574 -#: src/slic3r/GUI/GUI_Preview.cpp:835 src/libslic3r/GCode/PreviewData.cpp:357 +#: src/slic3r/GUI/GUI_Preview.cpp:286 src/slic3r/GUI/GUI_Preview.cpp:401 +#: src/slic3r/GUI/GUI_Preview.cpp:585 src/slic3r/GUI/GUI_Preview.cpp:640 +#: src/slic3r/GUI/GUI_Preview.cpp:901 src/libslic3r/GCode/PreviewData.cpp:358 msgid "Tool" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:228 src/slic3r/GUI/GUI_Preview.cpp:572 -#: src/libslic3r/GCode/PreviewData.cpp:359 +#: src/slic3r/GUI/GUI_Preview.cpp:287 src/slic3r/GUI/GUI_Preview.cpp:638 +#: src/libslic3r/GCode/PreviewData.cpp:360 msgid "Color Print" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:231 +#: src/slic3r/GUI/GUI_Preview.cpp:290 msgid "Show" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:234 src/slic3r/GUI/GUI_Preview.cpp:235 +#: src/slic3r/GUI/GUI_Preview.cpp:293 src/slic3r/GUI/GUI_Preview.cpp:294 msgid "Feature types" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:237 src/libslic3r/ExtrusionEntity.cpp:310 +#: src/slic3r/GUI/GUI_Preview.cpp:296 src/libslic3r/ExtrusionEntity.cpp:310 msgid "Perimeter" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:238 src/libslic3r/ExtrusionEntity.cpp:311 +#: src/slic3r/GUI/GUI_Preview.cpp:297 src/libslic3r/ExtrusionEntity.cpp:311 msgid "External perimeter" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:239 src/libslic3r/ExtrusionEntity.cpp:312 +#: src/slic3r/GUI/GUI_Preview.cpp:298 src/libslic3r/ExtrusionEntity.cpp:312 msgid "Overhang perimeter" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:240 src/libslic3r/ExtrusionEntity.cpp:313 +#: src/slic3r/GUI/GUI_Preview.cpp:299 src/libslic3r/ExtrusionEntity.cpp:313 msgid "Internal infill" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:241 src/libslic3r/ExtrusionEntity.cpp:314 +#: src/slic3r/GUI/GUI_Preview.cpp:300 src/libslic3r/ExtrusionEntity.cpp:314 #: src/libslic3r/PrintConfig.cpp:1763 src/libslic3r/PrintConfig.cpp:1774 msgid "Solid infill" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:242 src/libslic3r/ExtrusionEntity.cpp:315 +#: src/slic3r/GUI/GUI_Preview.cpp:301 src/libslic3r/ExtrusionEntity.cpp:315 #: src/libslic3r/PrintConfig.cpp:2112 src/libslic3r/PrintConfig.cpp:2124 msgid "Top solid infill" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:243 src/libslic3r/ExtrusionEntity.cpp:316 +#: src/slic3r/GUI/GUI_Preview.cpp:302 src/libslic3r/ExtrusionEntity.cpp:316 msgid "Bridge infill" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:244 src/libslic3r/ExtrusionEntity.cpp:317 +#: src/slic3r/GUI/GUI_Preview.cpp:303 src/libslic3r/ExtrusionEntity.cpp:317 #: src/libslic3r/PrintConfig.cpp:918 msgid "Gap fill" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:245 src/slic3r/GUI/Tab.cpp:1171 +#: src/slic3r/GUI/GUI_Preview.cpp:304 src/slic3r/GUI/Tab.cpp:1178 #: src/libslic3r/ExtrusionEntity.cpp:318 msgid "Skirt" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:247 src/libslic3r/ExtrusionEntity.cpp:320 +#: src/slic3r/GUI/GUI_Preview.cpp:306 src/libslic3r/ExtrusionEntity.cpp:320 #: src/libslic3r/PrintConfig.cpp:1998 msgid "Support material interface" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:248 src/slic3r/GUI/Tab.cpp:1252 +#: src/slic3r/GUI/GUI_Preview.cpp:307 src/slic3r/GUI/Tab.cpp:1259 #: src/libslic3r/ExtrusionEntity.cpp:321 msgid "Wipe tower" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:253 src/libslic3r/PrintConfig.cpp:2159 +#: src/slic3r/GUI/GUI_Preview.cpp:312 src/libslic3r/PrintConfig.cpp:2159 msgid "Travel" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:254 +#: src/slic3r/GUI/GUI_Preview.cpp:313 msgid "Retractions" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:255 +#: src/slic3r/GUI/GUI_Preview.cpp:314 msgid "Unretractions" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:256 +#: src/slic3r/GUI/GUI_Preview.cpp:315 msgid "Shells" msgstr "" -#: src/slic3r/GUI/GUI_Preview.cpp:257 +#: src/slic3r/GUI/GUI_Preview.cpp:316 msgid "Legend" msgstr "" @@ -2960,7 +2980,7 @@ msgstr "" msgid "ERROR: not enough resources to execute a new job." msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:41 src/slic3r/GUI/MainFrame.cpp:789 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:41 src/slic3r/GUI/MainFrame.cpp:855 msgid "Keyboard Shortcuts" msgstr "" @@ -2996,12 +3016,12 @@ msgstr "" msgid "Load Config from ini/amf/3mf/gcode and merge" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:122 src/slic3r/GUI/Plater.cpp:896 -#: src/slic3r/GUI/Plater.cpp:5562 src/libslic3r/PrintConfig.cpp:3375 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:122 src/slic3r/GUI/Plater.cpp:898 +#: src/slic3r/GUI/Plater.cpp:5531 src/libslic3r/PrintConfig.cpp:3375 msgid "Export G-code" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:123 src/slic3r/GUI/Plater.cpp:5563 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:123 src/slic3r/GUI/Plater.cpp:5532 msgid "Send G-code" msgstr "" @@ -3009,7 +3029,7 @@ msgstr "" msgid "Export config" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:125 src/slic3r/GUI/Plater.cpp:885 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:125 src/slic3r/GUI/Plater.cpp:887 msgid "Export to SD card / Flash drive" msgstr "" @@ -3078,876 +3098,896 @@ msgstr "" msgid "Show/Hide object/instance labels" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:149 src/slic3r/GUI/Preferences.cpp:10 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:149 +msgid "Turn On/Off facets' slope rendering" +msgstr "" + +#: src/slic3r/GUI/KBShortcutsDialog.cpp:152 src/slic3r/GUI/Preferences.cpp:10 msgid "Preferences" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:151 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:154 msgid "Show keyboard shortcuts list" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:154 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:157 msgid "Commands" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:159 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:162 msgid "Add Instance of the selected object" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:160 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:163 msgid "Remove Instance of the selected object" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:161 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:164 msgid "" "Press to select multiple objects\n" "or move multiple objects with mouse" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:162 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:165 msgid "Press to activate selection rectangle" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:163 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:166 msgid "Press to activate deselection rectangle" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:164 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:204 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:214 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:167 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:209 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:219 msgid "Arrow Up" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:164 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:167 msgid "Move selection 10 mm in positive Y direction" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:165 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:205 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:215 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:168 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:210 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:220 msgid "Arrow Down" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:165 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:168 msgid "Move selection 10 mm in negative Y direction" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:166 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:216 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:169 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:221 msgid "Arrow Left" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:166 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:169 msgid "Move selection 10 mm in negative X direction" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:167 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:217 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:170 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:222 msgid "Arrow Right" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:167 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:170 msgid "Move selection 10 mm in positive X direction" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:168 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:169 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:171 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:172 msgid "Any arrow" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:168 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:171 msgid "Movement step set to 1 mm" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:169 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:172 msgid "Movement in camera space" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:170 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:173 msgid "Page Up" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:170 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:173 msgid "Rotate selection 45 degrees CCW" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:171 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:174 msgid "Page Down" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:171 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:174 msgid "Rotate selection 45 degrees CW" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:172 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:175 msgid "Gizmo move" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:173 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:176 msgid "Gizmo scale" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:174 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:177 msgid "Gizmo rotate" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:175 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:178 msgid "Gizmo cut" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:176 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:179 msgid "Gizmo Place face on bed" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:177 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:180 msgid "Gizmo SLA hollow" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:178 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:181 msgid "Gizmo SLA support points" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:179 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:182 msgid "Unselect gizmo or clear selection" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:180 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:183 msgid "Change camera type (perspective, orthographic)" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:181 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:184 msgid "Zoom to Bed" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:182 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:185 msgid "" "Zoom to selected object\n" "or all objects in scene, if none selected" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:183 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:186 msgid "Zoom in" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:184 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:187 msgid "Zoom out" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:185 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:189 msgid "Show/Hide 3Dconnexion devices settings dialog" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:192 src/slic3r/GUI/MainFrame.cpp:256 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:197 src/slic3r/GUI/MainFrame.cpp:311 msgid "Plater" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:195 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:200 #, possible-c-format msgid "" "Press to snap by 5% in Gizmo scale\n" "or to snap by 1mm in Gizmo move" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:196 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:201 msgid "" "Scale selection to fit print volume\n" "in Gizmo scale" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:197 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:202 msgid "Press to activate one direction scaling in Gizmo scale" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:198 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:203 msgid "" "Press to scale (in Gizmo scale) or rotate (in Gizmo rotate)\n" "selected objects around their own center" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:201 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:206 msgid "Gizmos" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:204 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:206 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:209 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:211 msgid "Upper Layer" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:205 -#: src/slic3r/GUI/KBShortcutsDialog.cpp:207 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:210 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:212 msgid "Lower Layer" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:208 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:213 msgid "Show/Hide Legend" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:211 src/slic3r/GUI/Plater.cpp:4172 -#: src/slic3r/GUI/Tab.cpp:2375 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:216 src/slic3r/GUI/Plater.cpp:4129 +#: src/slic3r/GUI/Tab.cpp:2385 msgid "Preview" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:214 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:219 msgid "Move current slider thumb Up" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:215 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:220 msgid "Move current slider thumb Down" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:216 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:221 msgid "Set upper thumb to current slider thumb" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:217 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:222 msgid "Set lower thumb to current slider thumb" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:218 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:223 msgid "Add color change marker for current layer" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:219 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:224 msgid "Delete color change marker for current layer" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:222 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:227 msgid "Layers Slider" msgstr "" -#: src/slic3r/GUI/KBShortcutsDialog.cpp:245 +#: src/slic3r/GUI/KBShortcutsDialog.cpp:250 msgid "Keyboard shortcuts" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:71 +#: src/slic3r/GUI/MainFrame.cpp:72 msgid "" " - Remember to check for updates at http://github.com/prusa3d/PrusaSlicer/" "releases" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:226 +#: src/slic3r/GUI/MainFrame.cpp:281 msgid "based on Slic3r" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:478 +#: src/slic3r/GUI/MainFrame.cpp:533 msgid "&New Project" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:478 +#: src/slic3r/GUI/MainFrame.cpp:533 msgid "Start a new project" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:481 +#: src/slic3r/GUI/MainFrame.cpp:536 msgid "&Open Project" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:481 +#: src/slic3r/GUI/MainFrame.cpp:536 msgid "Open a project file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:486 +#: src/slic3r/GUI/MainFrame.cpp:541 msgid "Recent projects" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:495 +#: src/slic3r/GUI/MainFrame.cpp:550 msgid "" "The selected project is no longer available.\n" "Do you want to remove it from the recent projects list?" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:495 src/slic3r/GUI/MainFrame.cpp:866 +#: src/slic3r/GUI/MainFrame.cpp:550 src/slic3r/GUI/MainFrame.cpp:932 #: src/slic3r/GUI/PrintHostDialogs.cpp:231 msgid "Error" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:520 +#: src/slic3r/GUI/MainFrame.cpp:575 msgid "&Save Project" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:520 +#: src/slic3r/GUI/MainFrame.cpp:575 msgid "Save current project file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:524 src/slic3r/GUI/MainFrame.cpp:526 +#: src/slic3r/GUI/MainFrame.cpp:579 src/slic3r/GUI/MainFrame.cpp:581 msgid "Save Project &as" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:524 src/slic3r/GUI/MainFrame.cpp:526 +#: src/slic3r/GUI/MainFrame.cpp:579 src/slic3r/GUI/MainFrame.cpp:581 msgid "Save current project file as" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:534 +#: src/slic3r/GUI/MainFrame.cpp:589 msgid "Import STL/OBJ/AM&F/3MF" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:534 +#: src/slic3r/GUI/MainFrame.cpp:589 msgid "Load a model" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:538 +#: src/slic3r/GUI/MainFrame.cpp:593 msgid "Import &Config" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:538 +#: src/slic3r/GUI/MainFrame.cpp:593 msgid "Load exported configuration file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:541 +#: src/slic3r/GUI/MainFrame.cpp:596 msgid "Import Config from &project" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:541 +#: src/slic3r/GUI/MainFrame.cpp:596 msgid "Load configuration from project file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:545 +#: src/slic3r/GUI/MainFrame.cpp:600 msgid "Import Config &Bundle" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:545 +#: src/slic3r/GUI/MainFrame.cpp:600 msgid "Load presets from a bundle" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:548 +#: src/slic3r/GUI/MainFrame.cpp:603 msgid "&Import" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:551 src/slic3r/GUI/MainFrame.cpp:830 +#: src/slic3r/GUI/MainFrame.cpp:606 src/slic3r/GUI/MainFrame.cpp:896 msgid "Export &G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:551 +#: src/slic3r/GUI/MainFrame.cpp:606 msgid "Export current plate as G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:555 src/slic3r/GUI/MainFrame.cpp:831 +#: src/slic3r/GUI/MainFrame.cpp:610 src/slic3r/GUI/MainFrame.cpp:897 msgid "S&end G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:555 +#: src/slic3r/GUI/MainFrame.cpp:610 msgid "Send to print current plate as G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:559 +#: src/slic3r/GUI/MainFrame.cpp:614 msgid "Export G-code to SD card / Flash drive" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:559 +#: src/slic3r/GUI/MainFrame.cpp:614 msgid "Export current plate as G-code to SD card / Flash drive" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:563 +#: src/slic3r/GUI/MainFrame.cpp:618 msgid "Export plate as &STL" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:563 +#: src/slic3r/GUI/MainFrame.cpp:618 msgid "Export current plate as STL" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:566 +#: src/slic3r/GUI/MainFrame.cpp:621 msgid "Export plate as STL &including supports" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:566 +#: src/slic3r/GUI/MainFrame.cpp:621 msgid "Export current plate as STL including supports" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:569 +#: src/slic3r/GUI/MainFrame.cpp:624 msgid "Export plate as &AMF" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:569 +#: src/slic3r/GUI/MainFrame.cpp:624 msgid "Export current plate as AMF" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:573 +#: src/slic3r/GUI/MainFrame.cpp:628 msgid "Export &toolpaths as OBJ" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:573 +#: src/slic3r/GUI/MainFrame.cpp:628 msgid "Export toolpaths as OBJ" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:577 +#: src/slic3r/GUI/MainFrame.cpp:632 msgid "Export &Config" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:577 +#: src/slic3r/GUI/MainFrame.cpp:632 msgid "Export current configuration to file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:580 +#: src/slic3r/GUI/MainFrame.cpp:635 msgid "Export Config &Bundle" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:580 +#: src/slic3r/GUI/MainFrame.cpp:635 msgid "Export all presets to file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:583 +#: src/slic3r/GUI/MainFrame.cpp:638 msgid "&Export" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:585 +#: src/slic3r/GUI/MainFrame.cpp:640 msgid "Ejec&t SD card / Flash drive" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:585 +#: src/slic3r/GUI/MainFrame.cpp:640 msgid "Eject SD card / Flash drive after the G-code was exported to it." msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:593 +#: src/slic3r/GUI/MainFrame.cpp:648 msgid "Quick Slice" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:593 +#: src/slic3r/GUI/MainFrame.cpp:648 msgid "Slice a file into a G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:599 +#: src/slic3r/GUI/MainFrame.cpp:654 msgid "Quick Slice and Save As" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:599 +#: src/slic3r/GUI/MainFrame.cpp:654 msgid "Slice a file into a G-code, save as" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:605 +#: src/slic3r/GUI/MainFrame.cpp:660 msgid "Repeat Last Quick Slice" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:605 +#: src/slic3r/GUI/MainFrame.cpp:660 msgid "Repeat last quick slice" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:613 +#: src/slic3r/GUI/MainFrame.cpp:668 msgid "(Re)Slice No&w" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:613 +#: src/slic3r/GUI/MainFrame.cpp:668 msgid "Start new slicing process" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:617 +#: src/slic3r/GUI/MainFrame.cpp:672 msgid "&Repair STL file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:617 +#: src/slic3r/GUI/MainFrame.cpp:672 msgid "Automatically repair an STL file" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:621 +#: src/slic3r/GUI/MainFrame.cpp:676 msgid "&Quit" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:621 +#: src/slic3r/GUI/MainFrame.cpp:676 #, possible-c-format msgid "Quit %s" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:646 +#: src/slic3r/GUI/MainFrame.cpp:701 msgid "&Select all" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:647 +#: src/slic3r/GUI/MainFrame.cpp:702 msgid "Selects all objects" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:649 +#: src/slic3r/GUI/MainFrame.cpp:704 msgid "D&eselect all" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:650 +#: src/slic3r/GUI/MainFrame.cpp:705 msgid "Deselects all objects" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:653 +#: src/slic3r/GUI/MainFrame.cpp:708 msgid "&Delete selected" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:654 +#: src/slic3r/GUI/MainFrame.cpp:709 msgid "Deletes the current selection" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:656 +#: src/slic3r/GUI/MainFrame.cpp:711 msgid "Delete &all" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:657 +#: src/slic3r/GUI/MainFrame.cpp:712 msgid "Deletes all objects" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:661 +#: src/slic3r/GUI/MainFrame.cpp:716 msgid "&Undo" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:664 +#: src/slic3r/GUI/MainFrame.cpp:719 msgid "&Redo" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:669 +#: src/slic3r/GUI/MainFrame.cpp:724 msgid "&Copy" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:670 +#: src/slic3r/GUI/MainFrame.cpp:725 msgid "Copy selection to clipboard" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:672 +#: src/slic3r/GUI/MainFrame.cpp:727 msgid "&Paste" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:673 +#: src/slic3r/GUI/MainFrame.cpp:728 msgid "Paste clipboard" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:677 +#: src/slic3r/GUI/MainFrame.cpp:732 msgid "Re&load from disk" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:678 +#: src/slic3r/GUI/MainFrame.cpp:733 msgid "Reload the plater from disk" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:687 +#: src/slic3r/GUI/MainFrame.cpp:742 msgid "&Plater Tab" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:687 +#: src/slic3r/GUI/MainFrame.cpp:742 msgid "Show the plater" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:695 +#: src/slic3r/GUI/MainFrame.cpp:750 msgid "P&rint Settings Tab" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:695 +#: src/slic3r/GUI/MainFrame.cpp:750 msgid "Show the print settings" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:698 src/slic3r/GUI/MainFrame.cpp:833 +#: src/slic3r/GUI/MainFrame.cpp:753 src/slic3r/GUI/MainFrame.cpp:899 msgid "&Filament Settings Tab" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:698 +#: src/slic3r/GUI/MainFrame.cpp:753 msgid "Show the filament settings" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:702 +#: src/slic3r/GUI/MainFrame.cpp:757 msgid "Print&er Settings Tab" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:702 +#: src/slic3r/GUI/MainFrame.cpp:757 msgid "Show the printer settings" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:707 +#: src/slic3r/GUI/MainFrame.cpp:762 msgid "3&D" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:707 +#: src/slic3r/GUI/MainFrame.cpp:762 msgid "Show the 3D editing view" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:710 +#: src/slic3r/GUI/MainFrame.cpp:765 msgid "Pre&view" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:710 +#: src/slic3r/GUI/MainFrame.cpp:765 msgid "Show the 3D slices preview" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:729 +#: src/slic3r/GUI/MainFrame.cpp:784 msgid "Print &Host Upload Queue" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:729 +#: src/slic3r/GUI/MainFrame.cpp:784 msgid "Display the Print Host Upload Queue window" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:739 +#: src/slic3r/GUI/MainFrame.cpp:794 msgid "Iso" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:739 +#: src/slic3r/GUI/MainFrame.cpp:794 msgid "Iso View" msgstr "" #. TRN To be shown in the main menu View->Top #. TRN To be shown in Print Settings "Top solid layers" -#: src/slic3r/GUI/MainFrame.cpp:743 src/libslic3r/PrintConfig.cpp:2139 +#: src/slic3r/GUI/MainFrame.cpp:798 src/libslic3r/PrintConfig.cpp:2139 #: src/libslic3r/PrintConfig.cpp:2148 msgid "Top" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:743 +#: src/slic3r/GUI/MainFrame.cpp:798 msgid "Top View" msgstr "" #. TRN To be shown in the main menu View->Bottom #. TRN To be shown in Print Settings "Bottom solid layers" #. TRN To be shown in Print Settings "Top solid layers" -#: src/slic3r/GUI/MainFrame.cpp:746 src/libslic3r/PrintConfig.cpp:174 +#: src/slic3r/GUI/MainFrame.cpp:801 src/libslic3r/PrintConfig.cpp:174 #: src/libslic3r/PrintConfig.cpp:183 msgid "Bottom" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:746 +#: src/slic3r/GUI/MainFrame.cpp:801 msgid "Bottom View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:748 +#: src/slic3r/GUI/MainFrame.cpp:803 msgid "Front" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:748 +#: src/slic3r/GUI/MainFrame.cpp:803 msgid "Front View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:750 src/libslic3r/PrintConfig.cpp:1632 +#: src/slic3r/GUI/MainFrame.cpp:805 src/libslic3r/PrintConfig.cpp:1632 msgid "Rear" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:750 +#: src/slic3r/GUI/MainFrame.cpp:805 msgid "Rear View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:752 +#: src/slic3r/GUI/MainFrame.cpp:807 msgid "Left" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:752 +#: src/slic3r/GUI/MainFrame.cpp:807 msgid "Left View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:754 +#: src/slic3r/GUI/MainFrame.cpp:809 msgid "Right" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:754 +#: src/slic3r/GUI/MainFrame.cpp:809 msgid "Right View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:757 +#: src/slic3r/GUI/MainFrame.cpp:814 src/slic3r/GUI/MainFrame.cpp:822 msgid "Show &labels" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:757 +#: src/slic3r/GUI/MainFrame.cpp:814 src/slic3r/GUI/MainFrame.cpp:822 msgid "Show object/instance labels in 3D scene" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:765 +#: src/slic3r/GUI/MainFrame.cpp:817 +msgid "Show &slope" +msgstr "" + +#: src/slic3r/GUI/MainFrame.cpp:817 +msgid "Objects coloring using faces' slope" +msgstr "" + +#: src/slic3r/GUI/MainFrame.cpp:820 +msgid "&Options" +msgstr "" + +#: src/slic3r/GUI/MainFrame.cpp:831 msgid "Prusa 3D &Drivers" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:765 +#: src/slic3r/GUI/MainFrame.cpp:831 msgid "Open the Prusa3D drivers download page in your browser" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:767 +#: src/slic3r/GUI/MainFrame.cpp:833 msgid "Software &Releases" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:767 +#: src/slic3r/GUI/MainFrame.cpp:833 msgid "Open the software releases page in your browser" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:773 +#: src/slic3r/GUI/MainFrame.cpp:839 #, possible-c-format msgid "%s &Website" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:774 +#: src/slic3r/GUI/MainFrame.cpp:840 #, possible-c-format msgid "Open the %s website in your browser" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:780 +#: src/slic3r/GUI/MainFrame.cpp:846 msgid "System &Info" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:780 +#: src/slic3r/GUI/MainFrame.cpp:846 msgid "Show system information" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:782 +#: src/slic3r/GUI/MainFrame.cpp:848 msgid "Show &Configuration Folder" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:782 +#: src/slic3r/GUI/MainFrame.cpp:848 msgid "Show user configuration folder (datadir)" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:784 +#: src/slic3r/GUI/MainFrame.cpp:850 msgid "Report an I&ssue" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:784 +#: src/slic3r/GUI/MainFrame.cpp:850 #, possible-c-format msgid "Report an issue on %s" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:786 +#: src/slic3r/GUI/MainFrame.cpp:852 #, possible-c-format msgid "&About %s" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:786 +#: src/slic3r/GUI/MainFrame.cpp:852 msgid "Show about dialog" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:789 +#: src/slic3r/GUI/MainFrame.cpp:855 msgid "Show the list of the keyboard shortcuts" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:802 +#: src/slic3r/GUI/MainFrame.cpp:868 msgid "&File" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:803 +#: src/slic3r/GUI/MainFrame.cpp:869 msgid "&Edit" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:804 +#: src/slic3r/GUI/MainFrame.cpp:870 msgid "&Window" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:805 +#: src/slic3r/GUI/MainFrame.cpp:871 msgid "&View" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:808 +#: src/slic3r/GUI/MainFrame.cpp:874 msgid "&Help" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:830 +#: src/slic3r/GUI/MainFrame.cpp:896 msgid "E&xport" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:831 +#: src/slic3r/GUI/MainFrame.cpp:897 msgid "S&end to print" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:833 +#: src/slic3r/GUI/MainFrame.cpp:899 msgid "Mate&rial Settings Tab" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:854 +#: src/slic3r/GUI/MainFrame.cpp:920 msgid "Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:865 +#: src/slic3r/GUI/MainFrame.cpp:931 msgid "No previously sliced file." msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:871 +#: src/slic3r/GUI/MainFrame.cpp:937 msgid "Previously sliced file (" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:871 +#: src/slic3r/GUI/MainFrame.cpp:937 msgid ") not found." msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:872 +#: src/slic3r/GUI/MainFrame.cpp:938 msgid "File Not Found" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:907 +#: src/slic3r/GUI/MainFrame.cpp:973 #, possible-c-format msgid "Save %s file as:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:907 +#: src/slic3r/GUI/MainFrame.cpp:973 msgid "SVG" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:907 +#: src/slic3r/GUI/MainFrame.cpp:973 msgid "G-code" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:919 +#: src/slic3r/GUI/MainFrame.cpp:985 msgid "Save zip file as:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:928 src/slic3r/GUI/Plater.cpp:3188 -#: src/slic3r/GUI/Plater.cpp:5165 src/slic3r/GUI/Tab.cpp:1282 -#: src/slic3r/GUI/Tab.cpp:3721 +#: src/slic3r/GUI/MainFrame.cpp:994 src/slic3r/GUI/Plater.cpp:3121 +#: src/slic3r/GUI/Plater.cpp:5122 src/slic3r/GUI/Tab.cpp:1289 +#: src/slic3r/GUI/Tab.cpp:3731 msgid "Slicing" msgstr "" #. TRN "Processing input_file_basename" -#: src/slic3r/GUI/MainFrame.cpp:930 +#: src/slic3r/GUI/MainFrame.cpp:996 #, possible-c-format msgid "Processing %s" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:953 +#: src/slic3r/GUI/MainFrame.cpp:1019 msgid " was successfully sliced." msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:955 +#: src/slic3r/GUI/MainFrame.cpp:1021 msgid "Slicing Done!" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:970 +#: src/slic3r/GUI/MainFrame.cpp:1036 msgid "Select the STL file to repair:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:980 +#: src/slic3r/GUI/MainFrame.cpp:1046 msgid "Save OBJ file (less prone to coordinate errors than STL) as:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:992 +#: src/slic3r/GUI/MainFrame.cpp:1058 msgid "Your file was repaired." msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:992 src/libslic3r/PrintConfig.cpp:3469 +#: src/slic3r/GUI/MainFrame.cpp:1058 src/libslic3r/PrintConfig.cpp:3469 msgid "Repair" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:1006 +#: src/slic3r/GUI/MainFrame.cpp:1072 msgid "Save configuration as:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:1025 src/slic3r/GUI/MainFrame.cpp:1087 +#: src/slic3r/GUI/MainFrame.cpp:1091 src/slic3r/GUI/MainFrame.cpp:1153 msgid "Select configuration to load:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:1061 +#: src/slic3r/GUI/MainFrame.cpp:1127 msgid "Save presets bundle as:" msgstr "" -#: src/slic3r/GUI/MainFrame.cpp:1108 +#: src/slic3r/GUI/MainFrame.cpp:1174 #, possible-c-format msgid "%d presets successfully imported." msgstr "" -#: src/slic3r/GUI/Mouse3DController.cpp:258 +#: src/slic3r/GUI/Mouse3DController.cpp:263 msgid "3Dconnexion settings" msgstr "" -#: src/slic3r/GUI/Mouse3DController.cpp:273 +#: src/slic3r/GUI/Mouse3DController.cpp:278 msgid "Device:" msgstr "" -#: src/slic3r/GUI/Mouse3DController.cpp:280 +#: src/slic3r/GUI/Mouse3DController.cpp:285 msgid "Speed:" msgstr "" -#: src/slic3r/GUI/Mouse3DController.cpp:284 -#: src/slic3r/GUI/Mouse3DController.cpp:307 +#: src/slic3r/GUI/Mouse3DController.cpp:289 +#: src/slic3r/GUI/Mouse3DController.cpp:312 msgid "Translation" msgstr "" -#: src/slic3r/GUI/Mouse3DController.cpp:296 -#: src/slic3r/GUI/Mouse3DController.cpp:307 +#: src/slic3r/GUI/Mouse3DController.cpp:301 +#: src/slic3r/GUI/Mouse3DController.cpp:312 msgid "Zoom" msgstr "" -#: src/slic3r/GUI/Mouse3DController.cpp:303 +#: src/slic3r/GUI/Mouse3DController.cpp:308 msgid "Deadzone:" msgstr "" +#: src/slic3r/GUI/Mouse3DController.cpp:325 +msgid "Options:" +msgstr "" + #: src/slic3r/GUI/MsgDialog.cpp:73 #, possible-c-format msgid "%s error" @@ -3958,22 +3998,22 @@ msgstr "" msgid "%s has encountered an error" msgstr "" -#: src/slic3r/GUI/ObjectDataViewModel.cpp:56 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:58 msgid "Instances" msgstr "" -#: src/slic3r/GUI/ObjectDataViewModel.cpp:60 -#: src/slic3r/GUI/ObjectDataViewModel.cpp:216 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:62 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:218 #, possible-c-format msgid "Instance %d" msgstr "" -#: src/slic3r/GUI/ObjectDataViewModel.cpp:67 src/slic3r/GUI/Tab.cpp:3575 -#: src/slic3r/GUI/Tab.cpp:3657 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:69 src/slic3r/GUI/Tab.cpp:3585 +#: src/slic3r/GUI/Tab.cpp:3667 msgid "Layers" msgstr "" -#: src/slic3r/GUI/ObjectDataViewModel.cpp:94 +#: src/slic3r/GUI/ObjectDataViewModel.cpp:96 msgid "Range" msgstr "" @@ -3987,625 +4027,605 @@ msgctxt "Layers" msgid "Bottom" msgstr "" -#: src/slic3r/GUI/Plater.cpp:161 +#: src/slic3r/GUI/Plater.cpp:163 msgid "Volume" msgstr "" -#: src/slic3r/GUI/Plater.cpp:162 +#: src/slic3r/GUI/Plater.cpp:164 msgid "Facets" msgstr "" -#: src/slic3r/GUI/Plater.cpp:163 +#: src/slic3r/GUI/Plater.cpp:165 msgid "Materials" msgstr "" -#: src/slic3r/GUI/Plater.cpp:166 +#: src/slic3r/GUI/Plater.cpp:168 msgid "Manifold" msgstr "" -#: src/slic3r/GUI/Plater.cpp:216 +#: src/slic3r/GUI/Plater.cpp:218 msgid "Sliced Info" msgstr "" -#: src/slic3r/GUI/Plater.cpp:235 src/slic3r/GUI/Plater.cpp:1234 +#: src/slic3r/GUI/Plater.cpp:237 src/slic3r/GUI/Plater.cpp:1236 msgid "Used Filament (m)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:236 +#: src/slic3r/GUI/Plater.cpp:238 msgid "Used Filament (mm³)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:237 +#: src/slic3r/GUI/Plater.cpp:239 msgid "Used Filament (g)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:238 +#: src/slic3r/GUI/Plater.cpp:240 msgid "Used Material (unit)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:239 +#: src/slic3r/GUI/Plater.cpp:241 msgid "Cost (money)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:240 src/slic3r/GUI/Plater.cpp:1221 -#: src/slic3r/GUI/Plater.cpp:1263 +#: src/slic3r/GUI/Plater.cpp:242 src/slic3r/GUI/Plater.cpp:1223 +#: src/slic3r/GUI/Plater.cpp:1265 msgid "Estimated printing time" msgstr "" -#: src/slic3r/GUI/Plater.cpp:241 +#: src/slic3r/GUI/Plater.cpp:243 msgid "Number of tool changes" msgstr "" -#: src/slic3r/GUI/Plater.cpp:348 +#: src/slic3r/GUI/Plater.cpp:350 msgid "Click to edit preset" msgstr "" -#: src/slic3r/GUI/Plater.cpp:503 +#: src/slic3r/GUI/Plater.cpp:505 msgid "Select what kind of support do you need" msgstr "" -#: src/slic3r/GUI/Plater.cpp:505 src/libslic3r/PrintConfig.cpp:1908 +#: src/slic3r/GUI/Plater.cpp:507 src/libslic3r/PrintConfig.cpp:1908 #: src/libslic3r/PrintConfig.cpp:2691 msgid "Support on build plate only" msgstr "" -#: src/slic3r/GUI/Plater.cpp:506 src/slic3r/GUI/Plater.cpp:629 +#: src/slic3r/GUI/Plater.cpp:508 src/slic3r/GUI/Plater.cpp:631 msgid "For support enforcers only" msgstr "" -#: src/slic3r/GUI/Plater.cpp:507 +#: src/slic3r/GUI/Plater.cpp:509 msgid "Everywhere" msgstr "" -#: src/slic3r/GUI/Plater.cpp:539 src/slic3r/GUI/Tab.cpp:1178 +#: src/slic3r/GUI/Plater.cpp:541 src/slic3r/GUI/Tab.cpp:1185 msgid "Brim" msgstr "" -#: src/slic3r/GUI/Plater.cpp:541 +#: src/slic3r/GUI/Plater.cpp:543 msgid "" "This flag enables the brim that will be printed around each object on the " "first layer." msgstr "" -#: src/slic3r/GUI/Plater.cpp:549 +#: src/slic3r/GUI/Plater.cpp:551 msgid "Purging volumes" msgstr "" -#: src/slic3r/GUI/Plater.cpp:643 +#: src/slic3r/GUI/Plater.cpp:645 msgid "Select what kind of pad do you need" msgstr "" -#: src/slic3r/GUI/Plater.cpp:645 +#: src/slic3r/GUI/Plater.cpp:647 msgid "Below object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:646 +#: src/slic3r/GUI/Plater.cpp:648 msgid "Around object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:820 +#: src/slic3r/GUI/Plater.cpp:822 msgid "Print settings" msgstr "" -#: src/slic3r/GUI/Plater.cpp:821 src/slic3r/GUI/Tab.cpp:1481 -#: src/slic3r/GUI/Tab.cpp:1482 +#: src/slic3r/GUI/Plater.cpp:823 src/slic3r/GUI/Tab.cpp:1491 +#: src/slic3r/GUI/Tab.cpp:1492 msgid "Filament" msgstr "" -#: src/slic3r/GUI/Plater.cpp:822 +#: src/slic3r/GUI/Plater.cpp:824 msgid "SLA print settings" msgstr "" -#: src/slic3r/GUI/Plater.cpp:823 src/slic3r/GUI/Preset.cpp:1565 +#: src/slic3r/GUI/Plater.cpp:825 src/slic3r/GUI/Preset.cpp:1566 msgid "SLA material" msgstr "" -#: src/slic3r/GUI/Plater.cpp:824 +#: src/slic3r/GUI/Plater.cpp:826 msgid "Printer" msgstr "" -#: src/slic3r/GUI/Plater.cpp:883 src/slic3r/GUI/Plater.cpp:5563 +#: src/slic3r/GUI/Plater.cpp:885 src/slic3r/GUI/Plater.cpp:5532 msgid "Send to printer" msgstr "" -#: src/slic3r/GUI/Plater.cpp:884 +#: src/slic3r/GUI/Plater.cpp:886 msgid "Remove device" msgstr "" -#: src/slic3r/GUI/Plater.cpp:897 src/slic3r/GUI/Plater.cpp:3188 -#: src/slic3r/GUI/Plater.cpp:5168 +#: src/slic3r/GUI/Plater.cpp:899 src/slic3r/GUI/Plater.cpp:3121 +#: src/slic3r/GUI/Plater.cpp:5125 msgid "Slice now" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1047 +#: src/slic3r/GUI/Plater.cpp:1049 msgid "Hold Shift to Slice & Export G-code" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1157 +#: src/slic3r/GUI/Plater.cpp:1159 #, possible-c-format msgid "%d (%d shells)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1162 +#: src/slic3r/GUI/Plater.cpp:1164 #, possible-c-format msgid "Auto-repaired (%d errors)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1165 +#: src/slic3r/GUI/Plater.cpp:1167 #, possible-c-format msgid "" "%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d " "facets reversed, %d backwards edges" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1175 +#: src/slic3r/GUI/Plater.cpp:1177 msgid "Yes" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1196 +#: src/slic3r/GUI/Plater.cpp:1198 msgid "Used Material (ml)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1199 +#: src/slic3r/GUI/Plater.cpp:1201 msgid "object(s)" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1199 +#: src/slic3r/GUI/Plater.cpp:1201 msgid "supports and pad" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1236 src/slic3r/GUI/Plater.cpp:1250 +#: src/slic3r/GUI/Plater.cpp:1238 src/slic3r/GUI/Plater.cpp:1252 msgid "objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1236 src/slic3r/GUI/Plater.cpp:1250 +#: src/slic3r/GUI/Plater.cpp:1238 src/slic3r/GUI/Plater.cpp:1252 msgid "wipe tower" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1248 src/libslic3r/PrintConfig.cpp:760 +#: src/slic3r/GUI/Plater.cpp:1250 src/libslic3r/PrintConfig.cpp:760 #: src/libslic3r/PrintConfig.cpp:2517 src/libslic3r/PrintConfig.cpp:2518 msgid "Cost" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1265 src/libslic3r/PrintConfig.cpp:582 +#: src/slic3r/GUI/Plater.cpp:1267 src/libslic3r/PrintConfig.cpp:582 msgid "Color" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1266 +#: src/slic3r/GUI/Plater.cpp:1268 msgid "Pause" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1291 +#: src/slic3r/GUI/Plater.cpp:1293 msgid "normal mode" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1296 +#: src/slic3r/GUI/Plater.cpp:1298 msgid "stealth mode" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1400 +#: src/slic3r/GUI/Plater.cpp:1402 msgid "Load File" msgstr "" -#: src/slic3r/GUI/Plater.cpp:1404 +#: src/slic3r/GUI/Plater.cpp:1406 msgid "Load Files" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2205 +#: src/slic3r/GUI/Plater.cpp:2207 #, possible-c-format msgid "" "Unmounting successful. The device %s(%s) can now be safely removed from the " "computer." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2208 +#: src/slic3r/GUI/Plater.cpp:2210 #, possible-c-format msgid "Ejecting of device %s(%s) has failed." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2221 +#: src/slic3r/GUI/Plater.cpp:2223 msgid "New Project" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2335 +#: src/slic3r/GUI/Plater.cpp:2337 msgid "Loading" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2345 +#: src/slic3r/GUI/Plater.cpp:2347 #, possible-c-format msgid "Processing input file %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2373 +#: src/slic3r/GUI/Plater.cpp:2375 msgid "You cannot load SLA project with a multi-part object on the bed" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2374 src/slic3r/GUI/Tab.cpp:2948 +#: src/slic3r/GUI/Plater.cpp:2376 src/slic3r/GUI/Tab.cpp:2958 msgid "Please check your object list before preset changing." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2419 +#: src/slic3r/GUI/Plater.cpp:2421 msgid "" "This file contains several objects positioned at multiple heights.\n" "Instead of considering them as multiple objects, should I consider\n" "this file as a single object having multiple parts?" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2422 src/slic3r/GUI/Plater.cpp:2475 +#: src/slic3r/GUI/Plater.cpp:2424 src/slic3r/GUI/Plater.cpp:2477 msgid "Multi-part object detected" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2429 +#: src/slic3r/GUI/Plater.cpp:2431 msgid "" "This file cannot be loaded in a simple mode. Do you want to switch to an " "advanced mode?" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2430 +#: src/slic3r/GUI/Plater.cpp:2432 msgid "Detected advanced data" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2452 +#: src/slic3r/GUI/Plater.cpp:2454 #, possible-c-format msgid "" "You can't to add the object(s) from %s because of one or some of them " "is(are) multi-part" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2472 +#: src/slic3r/GUI/Plater.cpp:2474 msgid "" "Multiple objects were loaded for a multi-material printer.\n" "Instead of considering them as multiple objects, should I consider\n" "these files to represent a single object having multiple parts?" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2488 +#: src/slic3r/GUI/Plater.cpp:2490 msgid "Loaded" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2590 +#: src/slic3r/GUI/Plater.cpp:2592 msgid "" "Your object appears to be too large, so it was automatically scaled down to " "fit your print bed." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2591 +#: src/slic3r/GUI/Plater.cpp:2593 msgid "Object too large?" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2653 +#: src/slic3r/GUI/Plater.cpp:2655 msgid "Export STL file:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2660 +#: src/slic3r/GUI/Plater.cpp:2662 msgid "Export AMF file:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2666 +#: src/slic3r/GUI/Plater.cpp:2668 msgid "Save file as:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2672 +#: src/slic3r/GUI/Plater.cpp:2674 msgid "Export OBJ file:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2774 +#: src/slic3r/GUI/Plater.cpp:2773 msgid "Delete Object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2785 +#: src/slic3r/GUI/Plater.cpp:2784 msgid "Reset Project" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2822 -msgid "Hollow" -msgstr "" - -#: src/slic3r/GUI/Plater.cpp:2829 +#: src/slic3r/GUI/Plater.cpp:2823 msgid "Optimize Rotation" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2875 +#: src/slic3r/GUI/Plater.cpp:2869 msgid "Arranging" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2902 +#: src/slic3r/GUI/Plater.cpp:2896 msgid "Could not arrange model objects! Some geometries may be invalid." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2908 +#: src/slic3r/GUI/Plater.cpp:2902 msgid "Arranging canceled." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2909 +#: src/slic3r/GUI/Plater.cpp:2903 msgid "Arranging done." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2925 +#: src/slic3r/GUI/Plater.cpp:2919 msgid "Searching for optimal orientation" msgstr "" -#: src/slic3r/GUI/Plater.cpp:2958 +#: src/slic3r/GUI/Plater.cpp:2952 msgid "Orientation search canceled." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2959 +#: src/slic3r/GUI/Plater.cpp:2953 msgid "Orientation found." msgstr "" -#: src/slic3r/GUI/Plater.cpp:2989 -msgid "Indexing hollowed object" -msgstr "" - -#: src/slic3r/GUI/Plater.cpp:2993 -msgid "Hollowing cancelled." -msgstr "" - -#: src/slic3r/GUI/Plater.cpp:2994 -msgid "Hollowing done." -msgstr "" - -#: src/slic3r/GUI/Plater.cpp:2996 -msgid "Hollowing failed." -msgstr "" - -#: src/slic3r/GUI/Plater.cpp:3037 +#: src/slic3r/GUI/Plater.cpp:2970 msgid "" "The selected object can't be split because it contains more than one volume/" "material." msgstr "" -#: src/slic3r/GUI/Plater.cpp:3048 +#: src/slic3r/GUI/Plater.cpp:2981 msgid "Split to Objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3173 +#: src/slic3r/GUI/Plater.cpp:3106 msgid "Invalid data" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3182 +#: src/slic3r/GUI/Plater.cpp:3115 msgid "Ready to slice" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3220 src/slic3r/GUI/PrintHostDialogs.cpp:232 +#: src/slic3r/GUI/Plater.cpp:3153 src/slic3r/GUI/PrintHostDialogs.cpp:232 msgid "Cancelling" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3237 +#: src/slic3r/GUI/Plater.cpp:3170 msgid "Another export job is currently running." msgstr "" -#: src/slic3r/GUI/Plater.cpp:3353 +#: src/slic3r/GUI/Plater.cpp:3286 msgid "Please select the file to reload" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3388 +#: src/slic3r/GUI/Plater.cpp:3321 msgid "It is not allowed to change the file to reload" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3388 +#: src/slic3r/GUI/Plater.cpp:3321 msgid "Do you want to retry" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3406 +#: src/slic3r/GUI/Plater.cpp:3339 msgid "Reload from:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3495 +#: src/slic3r/GUI/Plater.cpp:3428 msgid "Unable to reload:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3500 +#: src/slic3r/GUI/Plater.cpp:3433 msgid "Error during reload" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3519 +#: src/slic3r/GUI/Plater.cpp:3452 msgid "Reload all from disk" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3540 +#: src/slic3r/GUI/Plater.cpp:3473 msgid "Fix Throught NetFabb" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3731 +#: src/slic3r/GUI/Plater.cpp:3672 msgid "Export failed" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3741 src/slic3r/GUI/PrintHostDialogs.cpp:233 +#: src/slic3r/GUI/Plater.cpp:3682 src/slic3r/GUI/PrintHostDialogs.cpp:233 msgid "Cancelled" msgstr "" -#: src/slic3r/GUI/Plater.cpp:3998 src/slic3r/GUI/Plater.cpp:4020 +#: src/slic3r/GUI/Plater.cpp:3935 src/slic3r/GUI/Plater.cpp:3957 msgid "Remove the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4007 +#: src/slic3r/GUI/Plater.cpp:3944 msgid "Add one more instance of the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4009 +#: src/slic3r/GUI/Plater.cpp:3946 msgid "Remove one instance of the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4011 +#: src/slic3r/GUI/Plater.cpp:3948 msgid "Set number of instances" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4011 +#: src/slic3r/GUI/Plater.cpp:3948 msgid "Change the number of instances of the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4030 +#: src/slic3r/GUI/Plater.cpp:3967 msgid "Reload the selected object from disk" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4033 +#: src/slic3r/GUI/Plater.cpp:3970 msgid "Export the selected object as STL file" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4062 +#: src/slic3r/GUI/Plater.cpp:3999 msgid "Along X axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4062 +#: src/slic3r/GUI/Plater.cpp:3999 msgid "Mirror the selected object along the X axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4064 +#: src/slic3r/GUI/Plater.cpp:4001 msgid "Along Y axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4064 +#: src/slic3r/GUI/Plater.cpp:4001 msgid "Mirror the selected object along the Y axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4066 +#: src/slic3r/GUI/Plater.cpp:4003 msgid "Along Z axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4066 +#: src/slic3r/GUI/Plater.cpp:4003 msgid "Mirror the selected object along the Z axis" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4069 +#: src/slic3r/GUI/Plater.cpp:4006 msgid "Mirror" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4069 +#: src/slic3r/GUI/Plater.cpp:4006 msgid "Mirror the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4081 +#: src/slic3r/GUI/Plater.cpp:4018 msgid "To objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4081 src/slic3r/GUI/Plater.cpp:4101 +#: src/slic3r/GUI/Plater.cpp:4018 src/slic3r/GUI/Plater.cpp:4038 msgid "Split the selected object into individual objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4083 +#: src/slic3r/GUI/Plater.cpp:4020 msgid "To parts" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4083 src/slic3r/GUI/Plater.cpp:4115 +#: src/slic3r/GUI/Plater.cpp:4020 src/slic3r/GUI/Plater.cpp:4052 msgid "Split the selected object into individual sub-parts" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4086 src/slic3r/GUI/Plater.cpp:4101 -#: src/slic3r/GUI/Plater.cpp:4115 src/libslic3r/PrintConfig.cpp:3493 +#: src/slic3r/GUI/Plater.cpp:4023 src/slic3r/GUI/Plater.cpp:4038 +#: src/slic3r/GUI/Plater.cpp:4052 src/libslic3r/PrintConfig.cpp:3493 msgid "Split" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4086 +#: src/slic3r/GUI/Plater.cpp:4023 msgid "Split the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4107 +#: src/slic3r/GUI/Plater.cpp:4044 msgid "Optimize orientation" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4107 +#: src/slic3r/GUI/Plater.cpp:4044 msgid "Optimize the rotation of the object for better print results." msgstr "" -#: src/slic3r/GUI/Plater.cpp:4164 +#: src/slic3r/GUI/Plater.cpp:4121 msgid "3D editor view" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4471 +#: src/slic3r/GUI/Plater.cpp:4428 msgid "" "%1% printer was active at the time the target Undo / Redo snapshot was " "taken. Switching to %1% printer requires reloading of %1% presets." msgstr "" -#: src/slic3r/GUI/Plater.cpp:4646 +#: src/slic3r/GUI/Plater.cpp:4604 msgid "Load Project" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4674 +#: src/slic3r/GUI/Plater.cpp:4632 msgid "Import Object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4678 +#: src/slic3r/GUI/Plater.cpp:4636 msgid "Import Objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4740 +#: src/slic3r/GUI/Plater.cpp:4705 msgid "All objects will be removed, continue?" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4748 +#: src/slic3r/GUI/Plater.cpp:4713 msgid "Delete Selected Objects" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4756 +#: src/slic3r/GUI/Plater.cpp:4721 msgid "Increase Instances" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4791 +#: src/slic3r/GUI/Plater.cpp:4756 msgid "Decrease Instances" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4822 +#: src/slic3r/GUI/Plater.cpp:4787 msgid "Enter the number of copies:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4823 +#: src/slic3r/GUI/Plater.cpp:4788 msgid "Copies of the selected object" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4827 +#: src/slic3r/GUI/Plater.cpp:4792 #, possible-c-format msgid "Set numbers of copies to %d" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4857 +#: src/slic3r/GUI/Plater.cpp:4822 msgid "Cut by Plane" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4908 +#: src/slic3r/GUI/Plater.cpp:4873 msgid "Save G-code file as:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:4908 +#: src/slic3r/GUI/Plater.cpp:4873 msgid "Save SL1 file as:" msgstr "" -#: src/slic3r/GUI/Plater.cpp:5043 +#: src/slic3r/GUI/Plater.cpp:5008 #, possible-c-format msgid "STL file exported to %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:5060 +#: src/slic3r/GUI/Plater.cpp:5025 #, possible-c-format msgid "AMF file exported to %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:5063 +#: src/slic3r/GUI/Plater.cpp:5028 #, possible-c-format msgid "Error exporting AMF file %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:5096 +#: src/slic3r/GUI/Plater.cpp:5057 #, possible-c-format msgid "3MF file exported to %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:5101 +#: src/slic3r/GUI/Plater.cpp:5062 #, possible-c-format msgid "Error exporting 3MF file %s" msgstr "" -#: src/slic3r/GUI/Plater.cpp:5562 +#: src/slic3r/GUI/Plater.cpp:5531 msgid "Export" msgstr "" -#: src/slic3r/GUI/Plater.cpp:5648 +#: src/slic3r/GUI/Plater.cpp:5617 msgid "Paste From Clipboard" msgstr "" -#: src/slic3r/GUI/Preferences.cpp:22 src/slic3r/GUI/Tab.cpp:1847 -#: src/slic3r/GUI/Tab.cpp:2059 +#: src/slic3r/GUI/Preferences.cpp:22 src/slic3r/GUI/Tab.cpp:1857 +#: src/slic3r/GUI/Tab.cpp:2069 msgid "General" msgstr "" @@ -4745,30 +4765,30 @@ msgstr "" msgid "modified" msgstr "" -#: src/slic3r/GUI/Preset.cpp:1150 src/slic3r/GUI/Preset.cpp:1205 -#: src/slic3r/GUI/Preset.cpp:1283 src/slic3r/GUI/Preset.cpp:1325 +#: src/slic3r/GUI/Preset.cpp:1151 src/slic3r/GUI/Preset.cpp:1206 +#: src/slic3r/GUI/Preset.cpp:1284 src/slic3r/GUI/Preset.cpp:1326 #: src/slic3r/GUI/PresetBundle.cpp:1599 src/slic3r/GUI/PresetBundle.cpp:1697 msgid "System presets" msgstr "" -#: src/slic3r/GUI/Preset.cpp:1209 src/slic3r/GUI/Preset.cpp:1329 +#: src/slic3r/GUI/Preset.cpp:1210 src/slic3r/GUI/Preset.cpp:1330 #: src/slic3r/GUI/PresetBundle.cpp:1702 msgid "User presets" msgstr "" -#: src/slic3r/GUI/Preset.cpp:1242 +#: src/slic3r/GUI/Preset.cpp:1243 msgid "Add/Remove materials" msgstr "" -#: src/slic3r/GUI/Preset.cpp:1244 +#: src/slic3r/GUI/Preset.cpp:1245 msgid "Add/Remove printers" msgstr "" -#: src/slic3r/GUI/Preset.cpp:1563 +#: src/slic3r/GUI/Preset.cpp:1564 msgid "filament" msgstr "" -#: src/slic3r/GUI/Preset.cpp:1564 +#: src/slic3r/GUI/Preset.cpp:1565 msgid "SLA print" msgstr "" @@ -5049,15 +5069,15 @@ msgstr "" msgid "Selection-Remove All" msgstr "" -#: src/slic3r/GUI/Selection.cpp:940 +#: src/slic3r/GUI/Selection.cpp:946 msgid "Scale To Fit" msgstr "" -#: src/slic3r/GUI/Selection.cpp:1477 +#: src/slic3r/GUI/Selection.cpp:1483 msgid "Set Printable Instance" msgstr "" -#: src/slic3r/GUI/Selection.cpp:1477 +#: src/slic3r/GUI/Selection.cpp:1483 msgid "Set Unprintable Instance" msgstr "" @@ -5065,388 +5085,388 @@ msgstr "" msgid "System Information" msgstr "" -#: src/slic3r/GUI/SysInfoDialog.cpp:154 +#: src/slic3r/GUI/SysInfoDialog.cpp:158 msgid "Copy to Clipboard" msgstr "" -#: src/slic3r/GUI/Tab.cpp:50 src/libslic3r/PrintConfig.cpp:265 +#: src/slic3r/GUI/Tab.cpp:51 src/libslic3r/PrintConfig.cpp:265 msgid "Compatible printers" msgstr "" -#: src/slic3r/GUI/Tab.cpp:51 +#: src/slic3r/GUI/Tab.cpp:52 msgid "Select the printers this profile is compatible with." msgstr "" -#: src/slic3r/GUI/Tab.cpp:56 src/libslic3r/PrintConfig.cpp:280 +#: src/slic3r/GUI/Tab.cpp:57 src/libslic3r/PrintConfig.cpp:280 msgid "Compatible print profiles" msgstr "" -#: src/slic3r/GUI/Tab.cpp:57 +#: src/slic3r/GUI/Tab.cpp:58 msgid "Select the print profiles this profile is compatible with." msgstr "" #. TRN "Save current Settings" -#: src/slic3r/GUI/Tab.cpp:133 +#: src/slic3r/GUI/Tab.cpp:134 #, possible-c-format msgid "Save current %s" msgstr "" -#: src/slic3r/GUI/Tab.cpp:134 +#: src/slic3r/GUI/Tab.cpp:135 msgid "Delete this preset" msgstr "" -#: src/slic3r/GUI/Tab.cpp:139 +#: src/slic3r/GUI/Tab.cpp:140 msgid "" "Hover the cursor over buttons to find more information \n" "or click this button." msgstr "" -#: src/slic3r/GUI/Tab.cpp:241 +#: src/slic3r/GUI/Tab.cpp:248 msgid "Add a new printer" msgstr "" -#: src/slic3r/GUI/Tab.cpp:963 +#: src/slic3r/GUI/Tab.cpp:970 msgid "Detach from system preset" msgstr "" -#: src/slic3r/GUI/Tab.cpp:975 +#: src/slic3r/GUI/Tab.cpp:982 msgid "" "A copy of the current system preset will be created, which will be detached " "from the system preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:976 +#: src/slic3r/GUI/Tab.cpp:983 msgid "" "The current custom preset will be detached from the parent system preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:979 +#: src/slic3r/GUI/Tab.cpp:986 msgid "Modifications to the current profile will be saved." msgstr "" -#: src/slic3r/GUI/Tab.cpp:982 +#: src/slic3r/GUI/Tab.cpp:989 msgid "" "This action is not revertable.\n" "Do you want to proceed?" msgstr "" -#: src/slic3r/GUI/Tab.cpp:984 +#: src/slic3r/GUI/Tab.cpp:991 msgid "Detach preset" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1010 +#: src/slic3r/GUI/Tab.cpp:1017 msgid "This is a default preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1012 +#: src/slic3r/GUI/Tab.cpp:1019 msgid "This is a system preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1014 +#: src/slic3r/GUI/Tab.cpp:1021 msgid "Current preset is inherited from the default preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1016 +#: src/slic3r/GUI/Tab.cpp:1023 msgid "Current preset is inherited from" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1020 +#: src/slic3r/GUI/Tab.cpp:1027 msgid "It can't be deleted or modified." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1021 +#: src/slic3r/GUI/Tab.cpp:1028 msgid "" "Any modifications should be saved as a new preset inherited from this one." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1022 +#: src/slic3r/GUI/Tab.cpp:1029 msgid "To do that please specify a new name for the preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1026 +#: src/slic3r/GUI/Tab.cpp:1033 msgid "Additional information:" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1032 +#: src/slic3r/GUI/Tab.cpp:1039 msgid "printer model" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1040 +#: src/slic3r/GUI/Tab.cpp:1047 msgid "default print profile" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1043 +#: src/slic3r/GUI/Tab.cpp:1050 msgid "default filament profile" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1057 +#: src/slic3r/GUI/Tab.cpp:1064 msgid "default SLA material profile" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1061 +#: src/slic3r/GUI/Tab.cpp:1068 msgid "default SLA print profile" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1069 +#: src/slic3r/GUI/Tab.cpp:1076 msgid "full profile name" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1070 +#: src/slic3r/GUI/Tab.cpp:1077 msgid "symbolic profile name" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1108 src/slic3r/GUI/Tab.cpp:3655 +#: src/slic3r/GUI/Tab.cpp:1115 src/slic3r/GUI/Tab.cpp:3665 msgid "Layers and perimeters" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1113 +#: src/slic3r/GUI/Tab.cpp:1120 msgid "Vertical shells" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1124 +#: src/slic3r/GUI/Tab.cpp:1131 msgid "Horizontal shells" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1125 src/libslic3r/PrintConfig.cpp:1787 +#: src/slic3r/GUI/Tab.cpp:1132 src/libslic3r/PrintConfig.cpp:1787 msgid "Solid layers" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1129 +#: src/slic3r/GUI/Tab.cpp:1136 msgid "Minimum shell thickness" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1140 +#: src/slic3r/GUI/Tab.cpp:1147 msgid "Quality (slower slicing)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1158 +#: src/slic3r/GUI/Tab.cpp:1165 msgid "Reducing printing time" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1170 +#: src/slic3r/GUI/Tab.cpp:1177 msgid "Skirt and brim" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1188 +#: src/slic3r/GUI/Tab.cpp:1195 msgid "Raft" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1192 +#: src/slic3r/GUI/Tab.cpp:1199 msgid "Options for support material and raft" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1207 +#: src/slic3r/GUI/Tab.cpp:1214 msgid "Speed for print moves" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1219 +#: src/slic3r/GUI/Tab.cpp:1226 msgid "Speed for non-print moves" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1222 +#: src/slic3r/GUI/Tab.cpp:1229 msgid "Modifiers" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1225 +#: src/slic3r/GUI/Tab.cpp:1232 msgid "Acceleration control (advanced)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1232 +#: src/slic3r/GUI/Tab.cpp:1239 msgid "Autospeed (advanced)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1240 +#: src/slic3r/GUI/Tab.cpp:1247 msgid "Multiple Extruders" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1248 +#: src/slic3r/GUI/Tab.cpp:1255 msgid "Ooze prevention" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1266 +#: src/slic3r/GUI/Tab.cpp:1273 msgid "Extrusion width" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1276 +#: src/slic3r/GUI/Tab.cpp:1283 msgid "Overlap" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1279 +#: src/slic3r/GUI/Tab.cpp:1286 msgid "Flow" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1288 +#: src/slic3r/GUI/Tab.cpp:1295 msgid "Other" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1291 src/slic3r/GUI/Tab.cpp:3724 +#: src/slic3r/GUI/Tab.cpp:1298 src/slic3r/GUI/Tab.cpp:3734 msgid "Output options" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1292 +#: src/slic3r/GUI/Tab.cpp:1299 msgid "Sequential printing" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1294 +#: src/slic3r/GUI/Tab.cpp:1301 msgid "Extruder clearance (mm)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1299 src/slic3r/GUI/Tab.cpp:3725 +#: src/slic3r/GUI/Tab.cpp:1306 src/slic3r/GUI/Tab.cpp:3735 msgid "Output file" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1306 src/libslic3r/PrintConfig.cpp:1453 +#: src/slic3r/GUI/Tab.cpp:1313 src/libslic3r/PrintConfig.cpp:1453 msgid "Post-processing scripts" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1312 src/slic3r/GUI/Tab.cpp:1313 -#: src/slic3r/GUI/Tab.cpp:1596 src/slic3r/GUI/Tab.cpp:1597 -#: src/slic3r/GUI/Tab.cpp:2035 src/slic3r/GUI/Tab.cpp:2036 -#: src/slic3r/GUI/Tab.cpp:2117 src/slic3r/GUI/Tab.cpp:2118 -#: src/slic3r/GUI/Tab.cpp:3598 src/slic3r/GUI/Tab.cpp:3599 +#: src/slic3r/GUI/Tab.cpp:1319 src/slic3r/GUI/Tab.cpp:1320 +#: src/slic3r/GUI/Tab.cpp:1606 src/slic3r/GUI/Tab.cpp:1607 +#: src/slic3r/GUI/Tab.cpp:2045 src/slic3r/GUI/Tab.cpp:2046 +#: src/slic3r/GUI/Tab.cpp:2127 src/slic3r/GUI/Tab.cpp:2128 +#: src/slic3r/GUI/Tab.cpp:3608 src/slic3r/GUI/Tab.cpp:3609 msgid "Notes" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1319 src/slic3r/GUI/Tab.cpp:1604 -#: src/slic3r/GUI/Tab.cpp:2042 src/slic3r/GUI/Tab.cpp:2124 -#: src/slic3r/GUI/Tab.cpp:3606 src/slic3r/GUI/Tab.cpp:3730 +#: src/slic3r/GUI/Tab.cpp:1326 src/slic3r/GUI/Tab.cpp:1614 +#: src/slic3r/GUI/Tab.cpp:2052 src/slic3r/GUI/Tab.cpp:2134 +#: src/slic3r/GUI/Tab.cpp:3616 src/slic3r/GUI/Tab.cpp:3740 msgid "Dependencies" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1320 src/slic3r/GUI/Tab.cpp:1605 -#: src/slic3r/GUI/Tab.cpp:2043 src/slic3r/GUI/Tab.cpp:2125 -#: src/slic3r/GUI/Tab.cpp:3607 src/slic3r/GUI/Tab.cpp:3731 +#: src/slic3r/GUI/Tab.cpp:1327 src/slic3r/GUI/Tab.cpp:1615 +#: src/slic3r/GUI/Tab.cpp:2053 src/slic3r/GUI/Tab.cpp:2135 +#: src/slic3r/GUI/Tab.cpp:3617 src/slic3r/GUI/Tab.cpp:3741 msgid "Profile dependencies" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1379 src/slic3r/GUI/Tab.cpp:1434 +#: src/slic3r/GUI/Tab.cpp:1389 src/slic3r/GUI/Tab.cpp:1444 msgid "Filament Overrides" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1380 src/slic3r/GUI/Tab.cpp:1439 -#: src/slic3r/GUI/Tab.cpp:2355 +#: src/slic3r/GUI/Tab.cpp:1390 src/slic3r/GUI/Tab.cpp:1449 +#: src/slic3r/GUI/Tab.cpp:2365 msgid "Retraction" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1489 src/libslic3r/PrintConfig.cpp:2074 +#: src/slic3r/GUI/Tab.cpp:1499 src/libslic3r/PrintConfig.cpp:2074 msgid "Temperature" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1495 +#: src/slic3r/GUI/Tab.cpp:1505 msgid "Bed" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1500 +#: src/slic3r/GUI/Tab.cpp:1510 msgid "Cooling" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1501 src/libslic3r/PrintConfig.cpp:1355 +#: src/slic3r/GUI/Tab.cpp:1511 src/libslic3r/PrintConfig.cpp:1355 #: src/libslic3r/PrintConfig.cpp:2207 msgid "Enable" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1512 +#: src/slic3r/GUI/Tab.cpp:1522 msgid "Fan settings" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1521 +#: src/slic3r/GUI/Tab.cpp:1531 msgid "Cooling thresholds" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1527 +#: src/slic3r/GUI/Tab.cpp:1537 msgid "Filament properties" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1534 +#: src/slic3r/GUI/Tab.cpp:1544 msgid "Print speed override" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1544 +#: src/slic3r/GUI/Tab.cpp:1554 msgid "Wipe tower parameters" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1547 +#: src/slic3r/GUI/Tab.cpp:1557 msgid "Toolchange parameters with single extruder MM printers" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1561 +#: src/slic3r/GUI/Tab.cpp:1571 msgid "Ramming settings" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1583 src/slic3r/GUI/Tab.cpp:1998 +#: src/slic3r/GUI/Tab.cpp:1593 src/slic3r/GUI/Tab.cpp:2008 msgid "Custom G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1584 src/slic3r/GUI/Tab.cpp:1999 +#: src/slic3r/GUI/Tab.cpp:1594 src/slic3r/GUI/Tab.cpp:2009 #: src/libslic3r/PrintConfig.cpp:1820 src/libslic3r/PrintConfig.cpp:1835 msgid "Start G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1590 src/slic3r/GUI/Tab.cpp:2005 +#: src/slic3r/GUI/Tab.cpp:1600 src/slic3r/GUI/Tab.cpp:2015 #: src/libslic3r/PrintConfig.cpp:385 src/libslic3r/PrintConfig.cpp:395 msgid "End G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1639 +#: src/slic3r/GUI/Tab.cpp:1649 msgid "Volumetric flow hints not available" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1725 src/slic3r/GUI/Tab.cpp:1938 +#: src/slic3r/GUI/Tab.cpp:1735 src/slic3r/GUI/Tab.cpp:1948 msgid "Test" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1734 +#: src/slic3r/GUI/Tab.cpp:1744 msgid "Could not get a valid Printer Host reference" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1740 src/slic3r/GUI/Tab.cpp:1951 +#: src/slic3r/GUI/Tab.cpp:1750 src/slic3r/GUI/Tab.cpp:1961 msgid "Success!" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1760 +#: src/slic3r/GUI/Tab.cpp:1770 msgid "" "HTTPS CA file is optional. It is only needed if you use HTTPS with a self-" "signed certificate." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1775 +#: src/slic3r/GUI/Tab.cpp:1785 msgid "Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1776 +#: src/slic3r/GUI/Tab.cpp:1786 msgid "Open CA certificate file" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1803 src/libslic3r/PrintConfig.cpp:111 +#: src/slic3r/GUI/Tab.cpp:1813 src/libslic3r/PrintConfig.cpp:111 msgid "HTTPS CA File" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1804 +#: src/slic3r/GUI/Tab.cpp:1814 #, possible-c-format msgid "" "On this system, %s uses HTTPS certificates from the system Certificate Store " "or Keychain." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1805 +#: src/slic3r/GUI/Tab.cpp:1815 msgid "" "To use a custom CA file, please import your CA file into Certificate Store / " "Keychain." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1848 src/slic3r/GUI/Tab.cpp:2060 +#: src/slic3r/GUI/Tab.cpp:1858 src/slic3r/GUI/Tab.cpp:2070 msgid "Size and coordinates" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1857 +#: src/slic3r/GUI/Tab.cpp:1867 msgid "Capabilities" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1862 +#: src/slic3r/GUI/Tab.cpp:1872 msgid "Number of extruders of the printer." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1890 +#: src/slic3r/GUI/Tab.cpp:1900 msgid "" "Single Extruder Multi Material is selected, \n" "and all extruders must have the same diameter.\n" @@ -5454,72 +5474,72 @@ msgid "" "nozzle diameter value?" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1893 src/slic3r/GUI/Tab.cpp:2325 +#: src/slic3r/GUI/Tab.cpp:1903 src/slic3r/GUI/Tab.cpp:2335 #: src/libslic3r/PrintConfig.cpp:1326 msgid "Nozzle diameter" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1923 +#: src/slic3r/GUI/Tab.cpp:1933 msgid "USB/Serial connection" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1924 src/libslic3r/PrintConfig.cpp:1661 +#: src/slic3r/GUI/Tab.cpp:1934 src/libslic3r/PrintConfig.cpp:1661 msgid "Serial port" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1929 +#: src/slic3r/GUI/Tab.cpp:1939 msgid "Rescan serial ports" msgstr "" -#: src/slic3r/GUI/Tab.cpp:1951 +#: src/slic3r/GUI/Tab.cpp:1961 msgid "Connection to printer works correctly." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1954 +#: src/slic3r/GUI/Tab.cpp:1964 msgid "Connection failed." msgstr "" -#: src/slic3r/GUI/Tab.cpp:1967 src/slic3r/GUI/Tab.cpp:2112 +#: src/slic3r/GUI/Tab.cpp:1977 src/slic3r/GUI/Tab.cpp:2122 msgid "Print Host upload" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2011 src/libslic3r/PrintConfig.cpp:153 +#: src/slic3r/GUI/Tab.cpp:2021 src/libslic3r/PrintConfig.cpp:153 msgid "Before layer change G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2017 src/libslic3r/PrintConfig.cpp:1080 +#: src/slic3r/GUI/Tab.cpp:2027 src/libslic3r/PrintConfig.cpp:1080 msgid "After layer change G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2023 src/libslic3r/PrintConfig.cpp:2100 +#: src/slic3r/GUI/Tab.cpp:2033 src/libslic3r/PrintConfig.cpp:2100 msgid "Tool change G-code" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2029 +#: src/slic3r/GUI/Tab.cpp:2039 msgid "Between objects G-code (for sequential printing)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2067 +#: src/slic3r/GUI/Tab.cpp:2077 msgid "Display" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2082 +#: src/slic3r/GUI/Tab.cpp:2092 msgid "Tilt" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2083 +#: src/slic3r/GUI/Tab.cpp:2093 msgid "Tilt time" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2089 src/slic3r/GUI/Tab.cpp:3582 +#: src/slic3r/GUI/Tab.cpp:2099 src/slic3r/GUI/Tab.cpp:3592 msgid "Corrections" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2106 src/slic3r/GUI/Tab.cpp:3578 +#: src/slic3r/GUI/Tab.cpp:2116 src/slic3r/GUI/Tab.cpp:3588 msgid "Exposure" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2173 src/slic3r/GUI/Tab.cpp:2258 +#: src/slic3r/GUI/Tab.cpp:2183 src/slic3r/GUI/Tab.cpp:2268 #: src/libslic3r/PrintConfig.cpp:1129 src/libslic3r/PrintConfig.cpp:1146 #: src/libslic3r/PrintConfig.cpp:1163 src/libslic3r/PrintConfig.cpp:1179 #: src/libslic3r/PrintConfig.cpp:1189 src/libslic3r/PrintConfig.cpp:1199 @@ -5527,183 +5547,183 @@ msgstr "" msgid "Machine limits" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2187 +#: src/slic3r/GUI/Tab.cpp:2197 msgid "Values in this column are for Normal mode" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2188 +#: src/slic3r/GUI/Tab.cpp:2198 msgid "Normal" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2193 +#: src/slic3r/GUI/Tab.cpp:2203 msgid "Values in this column are for Stealth mode" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2194 +#: src/slic3r/GUI/Tab.cpp:2204 msgid "Stealth" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2202 +#: src/slic3r/GUI/Tab.cpp:2212 msgid "Maximum feedrates" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2207 +#: src/slic3r/GUI/Tab.cpp:2217 msgid "Maximum accelerations" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2214 +#: src/slic3r/GUI/Tab.cpp:2224 msgid "Jerk limits" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2219 +#: src/slic3r/GUI/Tab.cpp:2229 msgid "Minimum feedrates" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2283 src/slic3r/GUI/Tab.cpp:2291 +#: src/slic3r/GUI/Tab.cpp:2293 src/slic3r/GUI/Tab.cpp:2301 msgid "Single extruder MM setup" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2292 +#: src/slic3r/GUI/Tab.cpp:2302 msgid "Single extruder multimaterial parameters" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2323 +#: src/slic3r/GUI/Tab.cpp:2333 msgid "" "This is a single extruder multimaterial printer, diameters of all extruders " "will be set to the new value. Do you want to proceed?" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2347 +#: src/slic3r/GUI/Tab.cpp:2357 msgid "Layer height limits" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2352 +#: src/slic3r/GUI/Tab.cpp:2362 msgid "Position (for multi-extruder printers)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2358 +#: src/slic3r/GUI/Tab.cpp:2368 msgid "Only lift Z" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2371 +#: src/slic3r/GUI/Tab.cpp:2381 msgid "" "Retraction when tool is disabled (advanced settings for multi-extruder " "setups)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2379 +#: src/slic3r/GUI/Tab.cpp:2389 msgid "Reset to Filament Color" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2560 +#: src/slic3r/GUI/Tab.cpp:2570 msgid "" "The Wipe option is not available when using the Firmware Retraction mode.\n" "\n" "Shall I disable it in order to enable Firmware Retraction?" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2562 +#: src/slic3r/GUI/Tab.cpp:2572 msgid "Firmware Retraction" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2908 +#: src/slic3r/GUI/Tab.cpp:2918 #, possible-c-format msgid "Default preset (%s)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2909 +#: src/slic3r/GUI/Tab.cpp:2919 #, possible-c-format msgid "Preset (%s)" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2926 +#: src/slic3r/GUI/Tab.cpp:2936 msgid "has the following unsaved changes:" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2929 +#: src/slic3r/GUI/Tab.cpp:2939 msgid "is not compatible with printer" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2930 +#: src/slic3r/GUI/Tab.cpp:2940 msgid "is not compatible with print profile" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2932 +#: src/slic3r/GUI/Tab.cpp:2942 msgid "and it has the following unsaved changes:" msgstr "" -#: src/slic3r/GUI/Tab.cpp:2936 +#: src/slic3r/GUI/Tab.cpp:2946 msgid "Unsaved Changes" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3030 +#: src/slic3r/GUI/Tab.cpp:3040 msgid "Detached" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3030 +#: src/slic3r/GUI/Tab.cpp:3040 msgctxt "PresetName" msgid "Copy" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3059 +#: src/slic3r/GUI/Tab.cpp:3069 msgid "The supplied name is empty. It can't be saved." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3064 +#: src/slic3r/GUI/Tab.cpp:3074 msgid "Cannot overwrite a system profile." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3068 +#: src/slic3r/GUI/Tab.cpp:3078 msgid "Cannot overwrite an external profile." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3073 +#: src/slic3r/GUI/Tab.cpp:3083 msgid "Preset with name \"%1%\" already exists." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3074 +#: src/slic3r/GUI/Tab.cpp:3084 msgid "Replace?" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3142 +#: src/slic3r/GUI/Tab.cpp:3152 msgid "remove" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3142 +#: src/slic3r/GUI/Tab.cpp:3152 msgid "delete" msgstr "" #. TRN remove/delete -#: src/slic3r/GUI/Tab.cpp:3144 +#: src/slic3r/GUI/Tab.cpp:3154 msgid "Are you sure you want to %1% the selected preset?" msgstr "" #. TRN Remove/Delete -#: src/slic3r/GUI/Tab.cpp:3147 +#: src/slic3r/GUI/Tab.cpp:3157 msgid "%1% Preset" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3206 src/slic3r/GUI/Tab.cpp:3276 +#: src/slic3r/GUI/Tab.cpp:3216 src/slic3r/GUI/Tab.cpp:3286 msgid "Set" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3315 +#: src/slic3r/GUI/Tab.cpp:3325 msgid "LOCKED LOCK" msgstr "" #. TRN Description for "LOCKED LOCK" -#: src/slic3r/GUI/Tab.cpp:3317 +#: src/slic3r/GUI/Tab.cpp:3327 msgid "" "indicates that the settings are the same as the system (or default) values " "for the current option group" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3319 +#: src/slic3r/GUI/Tab.cpp:3329 msgid "UNLOCKED LOCK" msgstr "" #. TRN Description for "UNLOCKED LOCK" -#: src/slic3r/GUI/Tab.cpp:3321 +#: src/slic3r/GUI/Tab.cpp:3331 msgid "" "indicates that some settings were changed and are not equal to the system " "(or default) values for the current option group.\n" @@ -5711,23 +5731,23 @@ msgid "" "to the system (or default) values." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3326 +#: src/slic3r/GUI/Tab.cpp:3336 msgid "WHITE BULLET" msgstr "" #. TRN Description for "WHITE BULLET" -#: src/slic3r/GUI/Tab.cpp:3328 +#: src/slic3r/GUI/Tab.cpp:3338 msgid "" "for the left button: indicates a non-system (or non-default) preset,\n" "for the right button: indicates that the settings hasn't been modified." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3331 +#: src/slic3r/GUI/Tab.cpp:3341 msgid "BACK ARROW" msgstr "" #. TRN Description for "BACK ARROW" -#: src/slic3r/GUI/Tab.cpp:3333 +#: src/slic3r/GUI/Tab.cpp:3343 msgid "" "indicates that the settings were changed and are not equal to the last saved " "preset for the current option group.\n" @@ -5735,13 +5755,13 @@ msgid "" "to the last saved preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3343 +#: src/slic3r/GUI/Tab.cpp:3353 msgid "" "LOCKED LOCK icon indicates that the settings are the same as the system (or " "default) values for the current option group" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3345 +#: src/slic3r/GUI/Tab.cpp:3355 msgid "" "UNLOCKED LOCK icon indicates that some settings were changed and are not " "equal to the system (or default) values for the current option group.\n" @@ -5749,17 +5769,17 @@ msgid "" "default) values." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3348 +#: src/slic3r/GUI/Tab.cpp:3358 msgid "WHITE BULLET icon indicates a non system (or non default) preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3351 +#: src/slic3r/GUI/Tab.cpp:3361 msgid "" "WHITE BULLET icon indicates that the settings are the same as in the last " "saved preset for the current option group." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3353 +#: src/slic3r/GUI/Tab.cpp:3363 msgid "" "BACK ARROW icon indicates that the settings were changed and are not equal " "to the last saved preset for the current option group.\n" @@ -5767,26 +5787,26 @@ msgid "" "preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3359 +#: src/slic3r/GUI/Tab.cpp:3369 msgid "" "LOCKED LOCK icon indicates that the value is the same as the system (or " "default) value." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3360 +#: src/slic3r/GUI/Tab.cpp:3370 msgid "" "UNLOCKED LOCK icon indicates that the value was changed and is not equal to " "the system (or default) value.\n" "Click to reset current value to the system (or default) value." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3366 +#: src/slic3r/GUI/Tab.cpp:3376 msgid "" "WHITE BULLET icon indicates that the value is the same as in the last saved " "preset." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3367 +#: src/slic3r/GUI/Tab.cpp:3377 msgid "" "BACK ARROW icon indicates that the value was changed and is not equal to the " "last saved preset.\n" @@ -5794,36 +5814,36 @@ msgid "" msgstr "" #. TRN Preset -#: src/slic3r/GUI/Tab.cpp:3480 +#: src/slic3r/GUI/Tab.cpp:3490 #, possible-c-format msgid "Save %s as:" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3524 +#: src/slic3r/GUI/Tab.cpp:3534 msgid "the following suffix is not allowed:" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3528 +#: src/slic3r/GUI/Tab.cpp:3538 msgid "The supplied name is not available." msgstr "" -#: src/slic3r/GUI/Tab.cpp:3541 src/slic3r/GUI/Tab.cpp:3543 +#: src/slic3r/GUI/Tab.cpp:3551 src/slic3r/GUI/Tab.cpp:3553 msgid "Material" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3665 +#: src/slic3r/GUI/Tab.cpp:3675 msgid "Support head" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3670 +#: src/slic3r/GUI/Tab.cpp:3680 msgid "Support pillar" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3686 +#: src/slic3r/GUI/Tab.cpp:3696 msgid "Connection of the support sticks and junctions" msgstr "" -#: src/slic3r/GUI/Tab.cpp:3691 +#: src/slic3r/GUI/Tab.cpp:3701 msgid "Automatic generation" msgstr "" @@ -6249,7 +6269,7 @@ msgstr "" msgid "Could not connect to Prusa SLA" msgstr "" -#: src/slic3r/Utils/PresetUpdater.cpp:705 +#: src/slic3r/Utils/PresetUpdater.cpp:706 #, possible-c-format msgid "requires min. %s and max. %s" msgstr "" @@ -6396,15 +6416,15 @@ msgstr "" msgid "Error with zip archive" msgstr "" -#: src/libslic3r/GCode.cpp:640 +#: src/libslic3r/GCode.cpp:637 msgid "Empty layers detected, the output would not be printable." msgstr "" -#: src/libslic3r/GCode.cpp:641 +#: src/libslic3r/GCode.cpp:638 msgid "Print z" msgstr "" -#: src/libslic3r/GCode.cpp:642 +#: src/libslic3r/GCode.cpp:639 msgid "" "This is usually caused by negligibly small extrusions or by a faulty model. " "Try to repair the model or change its orientation on the bed." @@ -6419,7 +6439,7 @@ msgid "" "Cannot calculate extrusion width for %1%: Variable \"%2%\" not accessible." msgstr "" -#: src/libslic3r/Format/3mf.cpp:1630 +#: src/libslic3r/Format/3mf.cpp:1626 msgid "" "The selected 3mf file has been saved with a newer version of %1% and is not " "compatible." @@ -6576,11 +6596,11 @@ msgstr "" msgid "Generating brim" msgstr "" -#: src/libslic3r/Print.cpp:1637 +#: src/libslic3r/Print.cpp:1633 msgid "Exporting G-code" msgstr "" -#: src/libslic3r/Print.cpp:1641 +#: src/libslic3r/Print.cpp:1637 msgid "Generating G-code" msgstr "" @@ -9809,22 +9829,22 @@ msgstr "" msgid "Generating support material" msgstr "" -#: src/libslic3r/GCode/PreviewData.cpp:347 +#: src/libslic3r/GCode/PreviewData.cpp:348 msgid "Height (mm)" msgstr "" -#: src/libslic3r/GCode/PreviewData.cpp:349 +#: src/libslic3r/GCode/PreviewData.cpp:350 msgid "Width (mm)" msgstr "" -#: src/libslic3r/GCode/PreviewData.cpp:351 +#: src/libslic3r/GCode/PreviewData.cpp:352 msgid "Speed (mm/s)" msgstr "" -#: src/libslic3r/GCode/PreviewData.cpp:353 +#: src/libslic3r/GCode/PreviewData.cpp:354 msgid "Fan Speed (%)" msgstr "" -#: src/libslic3r/GCode/PreviewData.cpp:355 +#: src/libslic3r/GCode/PreviewData.cpp:356 msgid "Volumetric flow rate (mm³/s)" msgstr "" diff --git a/resources/localization/fr/PrusaSlicer.mo b/resources/localization/fr/PrusaSlicer.mo index b01197c788..5a4620fca1 100644 Binary files a/resources/localization/fr/PrusaSlicer.mo and b/resources/localization/fr/PrusaSlicer.mo differ diff --git a/resources/localization/fr/PrusaSlicer_fr.po b/resources/localization/fr/PrusaSlicer_fr.po index 4bed900177..e6b8515f39 100644 --- a/resources/localization/fr/PrusaSlicer_fr.po +++ b/resources/localization/fr/PrusaSlicer_fr.po @@ -6360,7 +6360,7 @@ msgstr "Redimensionner" #: src/slic3r/GUI/GUI_ObjectManipulation.cpp:459 msgid "Scale factors" -msgstr "Facteurs de redimensionnement" +msgstr "Échelle" #: src/slic3r/GUI/KBShortcutsDialog.cpp:196 msgid "" diff --git a/resources/localization/ko/PrusaSlicer.mo b/resources/localization/ko/PrusaSlicer.mo index a1e06ababe..fa7a62e6ed 100644 Binary files a/resources/localization/ko/PrusaSlicer.mo and b/resources/localization/ko/PrusaSlicer.mo differ diff --git a/resources/localization/ko/PrusaSlicer_ko_KR.po b/resources/localization/ko/PrusaSlicer_ko_KR.po index db96beba52..804e211418 100644 --- a/resources/localization/ko/PrusaSlicer_ko_KR.po +++ b/resources/localization/ko/PrusaSlicer_ko_KR.po @@ -429,7 +429,7 @@ msgstr "서포트 생성" #: src/slic3r/GUI/ConfigManipulation.cpp:200 msgid "The %1% infill pattern is not supposed to work at 100%% density." -msgstr "%1% 채우기 패턴은 100% 밀도로 작동하도록 되어 있지 않습니다." +msgstr "%1% 채우기 패턴은 100%% 밀도로 작동하도록 되어 있지 않습니다." #: src/slic3r/GUI/ConfigManipulation.cpp:202 msgid "Shall I switch to rectilinear fill pattern?" diff --git a/resources/profiles/BIBO.ini b/resources/profiles/BIBO.ini index 8247a34a79..4a10de3cbc 100644 --- a/resources/profiles/BIBO.ini +++ b/resources/profiles/BIBO.ini @@ -31,17 +31,18 @@ avoid_crossing_perimeters = 0 bottom_fill_pattern = rectilinear bridge_angle = 0 bridge_flow_ratio = 0.95 -bridge_speed = 25 +bridge_speed = 20 brim_width = 0 clip_multipart_objects = 1 -compatible_printers = +compatible_printers = complete_objects = 0 dont_support_bridges = 1 elefant_foot_compensation = 0 ensure_vertical_shell_thickness = 1 external_fill_pattern = rectilinear external_perimeters_first = 0 -external_perimeter_extrusion_width = 0.45 +external_perimeter_extrusion_width = 0.40 +external_perimeter_speed = 25 extra_perimeters = 0 extruder_clearance_height = 12 extruder_clearance_radius = 45 @@ -52,14 +53,14 @@ fill_pattern = grid first_layer_extrusion_width = 0.42 first_layer_height = 0.2 first_layer_speed = 20 -gap_fill_speed = 30 +gap_fill_speed = 15 gcode_comments = 0 infill_every_layers = 1 infill_extruder = 1 infill_extrusion_width = 0.45 infill_first = 0 infill_only_where_needed = 0 -infill_overlap = 25% +infill_overlap = 20% interface_shells = 0 max_print_speed = 60 max_volumetric_extrusion_rate_slope_negative = 0 @@ -83,7 +84,7 @@ single_extruder_multi_material_priming = 0 skirts = 3 skirt_distance = 2 skirt_height = 1 -small_perimeter_speed = 25 +small_perimeter_speed = 15 solid_infill_below_area = 0 solid_infill_every_layers = 0 solid_infill_extruder = 1 @@ -92,7 +93,7 @@ spiral_vase = 0 standby_temperature_delta = -5 support_material = 0 support_material_extruder = 0 -support_material_extrusion_width = 0.4 +support_material_extrusion_width = 0.40 support_material_interface_extruder = 0 support_material_angle = 0 support_material_buildplate_only = 0 @@ -110,8 +111,8 @@ support_material_threshold = 45 support_material_with_sheath = 0 support_material_xy_spacing = 60% thin_walls = 0 -top_infill_extrusion_width = 0.45 -top_solid_infill_speed = 40 +top_infill_extrusion_width = 0.40 +top_solid_infill_speed = 20 travel_speed = 130 wipe_tower = 0 wipe_tower_bridging = 10 @@ -194,7 +195,7 @@ top_solid_layers = 7 [print:*0.15mm*] inherits = *common* -external_perimeter_speed = 40 +external_perimeter_speed = 25 infill_acceleration = 1100 infill_speed = 50 layer_height = 0.15 @@ -242,7 +243,7 @@ top_solid_layers = 4 inherits = *common* bottom_solid_layers = 4 bridge_flow_ratio = 0.95 -external_perimeter_speed = 40 +external_perimeter_speed = 25 infill_acceleration = 1100 infill_speed = 60 layer_height = 0.3 @@ -334,7 +335,7 @@ inherits = *0.30mm* # alias = 0.30mm ULTRADRAFT bottom_solid_layers = 3 bridge_speed = 30 -external_perimeter_speed = 35 +external_perimeter_speed = 30 infill_acceleration = 1100 infill_speed = 55 max_print_speed = 60 diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 93006cc567..c8d8d9bfbe 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -22,6 +22,12 @@ bed_model = ender3_bed.stl bed_texture = ender3.svg default_materials = Generic PLA @ENDER3; Generic PETG @ENDER3; Generic ABS @ENDER3; Prusament PLA @ENDER3; Prusament PETG @ENDER3 +[printer_model:ENDER2] +name = Creality Ender-2 +variants = 0.4 +technology = FFF +default_materials = Generic PLA @ENDER3; Generic PETG @ENDER3; Generic ABS @ENDER3; Prusament PLA @ENDER3; Prusament PETG @ENDER3 + # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. @@ -423,3 +429,9 @@ default_print_profile = 0.20mm NORMAL default_filament_profile = Creality PLA start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 ; home all\nG1 Z2 F240\nG1 X2 Y10 F3000\nG1 Z0.28 F240\nG92 E0.0\nG1 Y190 E15.0 F1500.0 ; intro line\nG1 X2.3 F5000\nG1 Y10 E30 F1200.0 ; intro line\nG92 E0.0 end_gcode = M104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+10, max_print_height)} F600{endif} ; Move print head up\nG1 X0 Y200 F3000 ; present print\nM84 X Y E ; disable motors + +[printer:Creality ENDER-2] +inherits = Creality ENDER-3 +bed_shape = 0x0,150x0,150x150,0x150 +printer_model = ENDER2 +max_print_height = 200 diff --git a/resources/profiles/Creality/ENDER2_thumbnail.png b/resources/profiles/Creality/ENDER2_thumbnail.png new file mode 100644 index 0000000000..45fb9bfe77 Binary files /dev/null and b/resources/profiles/Creality/ENDER2_thumbnail.png differ diff --git a/resources/shaders/gouraud.vs b/resources/shaders/gouraud.vs index a9f3f6118e..2644d48e4e 100644 --- a/resources/shaders/gouraud.vs +++ b/resources/shaders/gouraud.vs @@ -62,10 +62,8 @@ void main() float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; - intensity.y = 0.0; - - if (NdotL > 0.0) - intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); // Perform the same lighting calculation for the 2nd light source (no specular applied). NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); diff --git a/resources/shaders/variable_layer_height.vs b/resources/shaders/variable_layer_height.vs index 4f98dfa56e..0e966b0814 100644 --- a/resources/shaders/variable_layer_height.vs +++ b/resources/shaders/variable_layer_height.vs @@ -32,10 +32,8 @@ void main() float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0); intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; - intensity.y = 0.0; - - if (NdotL > 0.0) - intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); + vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS); // Perform the same lighting calculation for the 2nd light source (no specular) NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b0eab9bcc1..cdc8e0a13f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -59,6 +59,10 @@ if (SLIC3R_GUI) include(${wxWidgets_USE_FILE}) + list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX png) + list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX expat) + list(APPEND wxWidgets_LIBRARIES ${PNG_LIBRARIES} ${EXPAT_LIBRARIES}) + # list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc) message(STATUS "wx libs: ${wxWidgets_LIBRARIES}") diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 9270d3887b..9efecb50c2 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -34,6 +34,7 @@ #include "libslic3r/Config.hpp" #include "libslic3r/Geometry.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/ModelArrange.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/SLAPrint.hpp" #include "libslic3r/TriangleMesh.hpp" @@ -41,6 +42,7 @@ #include "libslic3r/Format/3mf.hpp" #include "libslic3r/Format/STL.hpp" #include "libslic3r/Format/OBJ.hpp" +#include "libslic3r/Format/SL1.hpp" #include "libslic3r/Utils.hpp" #include "PrusaSlicer.hpp" @@ -53,14 +55,16 @@ using namespace Slic3r; -PrinterTechnology get_printer_technology(const DynamicConfig &config) -{ - const ConfigOptionEnum *opt = config.option>("printer_technology"); - return (opt == nullptr) ? ptUnknown : opt->value; -} - int CLI::run(int argc, char **argv) { + +#ifdef __WXGTK__ + // On Linux, wxGTK has no support for Wayland, and the app crashes on + // startup if gtk3 is used. This env var has to be set explicitly to + // instruct the window manager to fall back to X server mode. + ::setenv("GDK_BACKEND", "x11", /* replace */ true); +#endif + // Switch boost::filesystem to utf8. try { boost::nowide::nowide_filesystem(); @@ -86,13 +90,15 @@ int CLI::run(int argc, char **argv) m_extra_config.apply(m_config, true); m_extra_config.normalize(); + + PrinterTechnology printer_technology = Slic3r::printer_technology(m_config); bool start_gui = m_actions.empty() && // cutting transformations are setting an "export" action. std::find(m_transforms.begin(), m_transforms.end(), "cut") == m_transforms.end() && std::find(m_transforms.begin(), m_transforms.end(), "cut_x") == m_transforms.end() && std::find(m_transforms.begin(), m_transforms.end(), "cut_y") == m_transforms.end(); - PrinterTechnology printer_technology = get_printer_technology(m_extra_config); + const std::vector &load_configs = m_config.option("load", true)->values; // load config files supplied via --load @@ -113,7 +119,7 @@ int CLI::run(int argc, char **argv) return 1; } config.normalize(); - PrinterTechnology other_printer_technology = get_printer_technology(config); + PrinterTechnology other_printer_technology = Slic3r::printer_technology(config); if (printer_technology == ptUnknown) { printer_technology = other_printer_technology; } else if (printer_technology != other_printer_technology && other_printer_technology != ptUnknown) { @@ -134,7 +140,7 @@ int CLI::run(int argc, char **argv) // When loading an AMF or 3MF, config is imported as well, including the printer technology. DynamicPrintConfig config; model = Model::read_from_file(file, &config, true); - PrinterTechnology other_printer_technology = get_printer_technology(config); + PrinterTechnology other_printer_technology = Slic3r::printer_technology(config); if (printer_technology == ptUnknown) { printer_technology = other_printer_technology; } else if (printer_technology != other_printer_technology && other_printer_technology != ptUnknown) { @@ -161,9 +167,6 @@ int CLI::run(int argc, char **argv) // Normalizing after importing the 3MFs / AMFs m_print_config.normalize(); - if (printer_technology == ptUnknown) - printer_technology = std::find(m_actions.begin(), m_actions.end(), "export_sla") == m_actions.end() ? ptFFF : ptSLA; - // Initialize full print configs for both the FFF and SLA technologies. FullPrintConfig fff_print_config; SLAFullPrintConfig sla_print_config; @@ -174,6 +177,7 @@ int CLI::run(int argc, char **argv) m_print_config.apply(fff_print_config, true); } else if (printer_technology == ptSLA) { // The default value has to be different from the one in fff mode. + sla_print_config.printer_technology.value = ptSLA; sla_print_config.output_filename_format.value = "[input_filename_base].sl1"; // The default bed shape should reflect the default display parameters @@ -186,8 +190,18 @@ int CLI::run(int argc, char **argv) m_print_config.apply(sla_print_config, true); } + std::string validity = m_print_config.validate(); + if (!validity.empty()) { + boost::nowide::cerr << "error: " << validity << std::endl; + return 1; + } + // Loop through transform options. bool user_center_specified = false; + Points bed = get_bed_shape(m_print_config); + ArrangeParams arrange_cfg; + arrange_cfg.min_obj_distance = scaled(min_object_distance(m_print_config)); + for (auto const &opt_key : m_transforms) { if (opt_key == "merge") { Model m; @@ -197,29 +211,33 @@ int CLI::run(int argc, char **argv) // Rearrange instances unless --dont-arrange is supplied if (! m_config.opt_bool("dont_arrange")) { m.add_default_instances(); - const BoundingBoxf &bb = fff_print_config.bed_shape.values; - m.arrange_objects( - fff_print_config.min_object_distance(), - // If we are going to use the merged model for printing, honor - // the configured print bed for arranging, otherwise do it freely. - this->has_print_action() ? &bb : nullptr - ); + if (this->has_print_action()) + arrange_objects(m, bed, arrange_cfg); + else + arrange_objects(m, InfiniteBed{}, arrange_cfg); } m_models.clear(); m_models.emplace_back(std::move(m)); } else if (opt_key == "duplicate") { - const BoundingBoxf &bb = fff_print_config.bed_shape.values; for (auto &model : m_models) { const bool all_objects_have_instances = std::none_of( model.objects.begin(), model.objects.end(), [](ModelObject* o){ return o->instances.empty(); } ); - if (all_objects_have_instances) { - // if all input objects have defined position(s) apply duplication to the whole model - model.duplicate(m_config.opt_int("duplicate"), fff_print_config.min_object_distance(), &bb); - } else { - model.add_default_instances(); - model.duplicate_objects(m_config.opt_int("duplicate"), fff_print_config.min_object_distance(), &bb); + + int dups = m_config.opt_int("duplicate"); + if (!all_objects_have_instances) model.add_default_instances(); + + try { + if (dups > 1) { + // if all input objects have defined position(s) apply duplication to the whole model + duplicate(model, size_t(dups), bed, arrange_cfg); + } else { + arrange_objects(model, bed, arrange_cfg); + } + } catch (std::exception &ex) { + boost::nowide::cerr << "error: " << ex.what() << std::endl; + return 1; } } } else if (opt_key == "duplicate_grid") { @@ -413,7 +431,8 @@ int CLI::run(int argc, char **argv) std::string outfile = m_config.opt_string("output"); Print fff_print; SLAPrint sla_print; - + SL1Archive sla_archive(sla_print.printer_config()); + sla_print.set_printer(&sla_archive); sla_print.set_status_callback( [](const PrintBase::SlicingStatus& s) { @@ -423,11 +442,11 @@ int CLI::run(int argc, char **argv) PrintBase *print = (printer_technology == ptFFF) ? static_cast(&fff_print) : static_cast(&sla_print); if (! m_config.opt_bool("dont_arrange")) { - //FIXME make the min_object_distance configurable. - model.arrange_objects(fff_print.config().min_object_distance()); - model.center_instances_around_point((! user_center_specified && m_print_config.has("bed_shape")) ? - BoundingBoxf(m_print_config.opt("bed_shape")->values).center() : - m_config.option("center")->value); + if (user_center_specified) { + Vec2d c = m_config.option("center")->value; + arrange_objects(model, InfiniteBed{scaled(c)}, arrange_cfg); + } else + arrange_objects(model, bed, arrange_cfg); } if (printer_technology == ptFFF) { for (auto* mo : model.objects) @@ -453,7 +472,7 @@ int CLI::run(int argc, char **argv) outfile = sla_print.output_filepath(outfile); // We need to finalize the filename beforehand because the export function sets the filename inside the zip metadata outfile_final = sla_print.print_statistics().finalize_output_path(outfile); - sla_print.export_raster(outfile_final); + sla_archive.export_print(outfile_final, sla_print); } if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final)) { boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl; @@ -609,6 +628,8 @@ bool CLI::setup(int argc, char **argv) if (opt_loglevel != 0) set_logging_level(opt_loglevel->value); } + + std::string validity = m_config.validate(); // Initialize with defaults. for (const t_optiondef_map *options : { &cli_actions_config_def.options, &cli_transform_config_def.options, &cli_misc_config_def.options }) @@ -616,6 +637,11 @@ bool CLI::setup(int argc, char **argv) m_config.option(optdef.first, true); set_data_dir(m_config.opt_string("datadir")); + + if (!validity.empty()) { + boost::nowide::cerr << "error: " << validity << std::endl; + return false; + } return true; } diff --git a/src/libnest2d/include/libnest2d/geometry_traits.hpp b/src/libnest2d/include/libnest2d/geometry_traits.hpp index 72e239a707..c447cfcd93 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits.hpp @@ -982,6 +982,9 @@ template inline double area(const S& poly, const PolygonTag& ) }); } +template +inline double area(const RawShapes& shapes, const MultiPolygonTag&); + template // Dispatching function inline double area(const S& sh) { diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index b6d7fcdcf9..5eef28c407 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -27,6 +27,7 @@ using Coord = TCoord; using Box = _Box; using Segment = _Segment; using Circle = _Circle; +using MultiPolygon = TMultiShape; using Item = _Item; using Rectangle = _Rectangle; diff --git a/src/libnest2d/tools/svgtools.hpp b/src/libnest2d/tools/svgtools.hpp index e1ed1ad05a..2a05b551d1 100644 --- a/src/libnest2d/tools/svgtools.hpp +++ b/src/libnest2d/tools/svgtools.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include namespace libnest2d { namespace svg { @@ -48,23 +48,28 @@ public: conf_.width = static_cast(box.width()) / conf_.mm_in_coord_units; } - - void writeItem(const Item& item) { + + void writeShape(RawShape tsh) { if(svg_layers_.empty()) addLayer(); - auto tsh = item.transformedShape(); if(conf_.origo_location == BOTTOMLEFT) { auto d = static_cast( - std::round(conf_.height*conf_.mm_in_coord_units) ); - + std::round(conf_.height*conf_.mm_in_coord_units) ); + auto& contour = shapelike::contour(tsh); for(auto& v : contour) setY(v, -getY(v) + d); - + auto& holes = shapelike::holes(tsh); for(auto& h : holes) for(auto& v : h) setY(v, -getY(v) + d); - + } - currentLayer() += shapelike::serialize(tsh, - 1.0/conf_.mm_in_coord_units) + "\n"; + currentLayer() += + shapelike::serialize(tsh, + 1.0 / conf_.mm_in_coord_units) + + "\n"; + } + + void writeItem(const Item& item) { + writeShape(item.transformedShape()); } void writePackGroup(const PackGroup& result) { diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index b8ef0bcdca..5b048b0ffb 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -1,7 +1,6 @@ #include "Arrange.hpp" -#include "Geometry.hpp" +//#include "Geometry.hpp" #include "SVG.hpp" -#include "MTUtils.hpp" #include #include @@ -83,7 +82,7 @@ const double BIG_ITEM_TRESHOLD = 0.02; // Fill in the placer algorithm configuration with values carefully chosen for // Slic3r. template -void fillConfig(PConf& pcfg) { +void fill_config(PConf& pcfg) { // Align the arranged pile into the center of the bin pcfg.alignment = PConf::Alignment::CENTER; @@ -105,7 +104,7 @@ void fillConfig(PConf& pcfg) { // Apply penalty to object function result. This is used only when alignment // after arrange is explicitly disabled (PConfig::Alignment::DONT_ALIGN) -double fixed_overfit(const std::tuple& result, const Box &binbb) +static double fixed_overfit(const std::tuple& result, const Box &binbb) { double score = std::get<0>(result); Box pilebb = std::get<1>(result); @@ -312,7 +311,7 @@ public: , m_bin_area(sl::area(bin)) , m_norm(std::sqrt(m_bin_area)) { - fillConfig(m_pconf); + fill_config(m_pconf); // Set up a callback that is called just before arranging starts // This functionality is provided by the Nester class (m_pack). @@ -363,6 +362,9 @@ public: m_item_count = 0; } + PConfig& config() { return m_pconf; } + const PConfig& config() const { return m_pconf; } + inline void preload(std::vector& fixeditems) { m_pconf.alignment = PConfig::Alignment::DONT_ALIGN; auto bb = sl::boundingBox(m_bin); @@ -438,127 +440,6 @@ std::function AutoArranger::get_objfn() }; } -inline Circle to_lnCircle(const CircleBed& circ) { - return Circle({circ.center()(0), circ.center()(1)}, circ.radius()); -} - -// Get the type of bed geometry from a simple vector of points. -void BedShapeHint::reset(BedShapes type) -{ - if (m_type != type) { - if (m_type == bsIrregular) - m_bed.polygon.Slic3r::Polyline::~Polyline(); - else if (type == bsIrregular) - ::new (&m_bed.polygon) Polyline(); - } - - m_type = type; -} - -BedShapeHint::BedShapeHint(const Polyline &bed) { - auto x = [](const Point& p) { return p(X); }; - auto y = [](const Point& p) { return p(Y); }; - - auto width = [x](const BoundingBox& box) { - return x(box.max) - x(box.min); - }; - - auto height = [y](const BoundingBox& box) { - return y(box.max) - y(box.min); - }; - - auto area = [&width, &height](const BoundingBox& box) { - double w = width(box); - double h = height(box); - return w * h; - }; - - auto poly_area = [](Polyline p) { - Polygon pp; pp.points.reserve(p.points.size() + 1); - pp.points = std::move(p.points); - pp.points.emplace_back(pp.points.front()); - return std::abs(pp.area()); - }; - - auto distance_to = [x, y](const Point& p1, const Point& p2) { - double dx = x(p2) - x(p1); - double dy = y(p2) - y(p1); - return std::sqrt(dx*dx + dy*dy); - }; - - auto bb = bed.bounding_box(); - - auto isCircle = [bb, distance_to](const Polyline& polygon) { - auto center = bb.center(); - std::vector vertex_distances; - double avg_dist = 0; - for (auto pt: polygon.points) - { - double distance = distance_to(center, pt); - vertex_distances.push_back(distance); - avg_dist += distance; - } - - avg_dist /= vertex_distances.size(); - - CircleBed ret(center, avg_dist); - for(auto el : vertex_distances) - { - if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) { - ret = CircleBed(); - break; - } - } - - return ret; - }; - - auto parea = poly_area(bed); - - if( (1.0 - parea/area(bb)) < 1e-3 ) { - m_type = BedShapes::bsBox; - m_bed.box = bb; - } - else if(auto c = isCircle(bed)) { - m_type = BedShapes::bsCircle; - m_bed.circ = c; - } else { - assert(m_type != BedShapes::bsIrregular); - m_type = BedShapes::bsIrregular; - ::new (&m_bed.polygon) Polyline(bed); - } -} - -BedShapeHint &BedShapeHint::operator=(BedShapeHint &&cpy) -{ - reset(cpy.m_type); - - switch(m_type) { - case bsBox: m_bed.box = std::move(cpy.m_bed.box); break; - case bsCircle: m_bed.circ = std::move(cpy.m_bed.circ); break; - case bsIrregular: m_bed.polygon = std::move(cpy.m_bed.polygon); break; - case bsInfinite: m_bed.infbed = std::move(cpy.m_bed.infbed); break; - case bsUnknown: break; - } - - return *this; -} - -BedShapeHint &BedShapeHint::operator=(const BedShapeHint &cpy) -{ - reset(cpy.m_type); - - switch(m_type) { - case bsBox: m_bed.box = cpy.m_bed.box; break; - case bsCircle: m_bed.circ = cpy.m_bed.circ; break; - case bsIrregular: m_bed.polygon = cpy.m_bed.polygon; break; - case bsInfinite: m_bed.infbed = cpy.m_bed.infbed; break; - case bsUnknown: break; - } - - return *this; -} - template void remove_large_items(std::vector &items, Bin &&bin) { auto it = items.begin(); @@ -572,12 +453,12 @@ void _arrange( std::vector & shapes, std::vector & excludes, const BinT & bin, - coord_t minobjd, + const ArrangeParams & params, std::function progressfn, std::function stopfn) { // Integer ceiling the min distance from the bed perimeters - coord_t md = minobjd; + coord_t md = params.min_obj_distance; md = (md % 2) ? md / 2 + 1 : md / 2; auto corrected_bin = bin; @@ -585,7 +466,10 @@ void _arrange( AutoArranger arranger{corrected_bin, progressfn, stopfn}; - auto infl = coord_t(std::ceil(minobjd / 2.0)); + arranger.config().accuracy = params.accuracy; + arranger.config().parallel = params.parallel; + + auto infl = coord_t(std::ceil(params.min_obj_distance / 2.0)); for (Item& itm : shapes) itm.inflate(infl); for (Item& itm : excludes) itm.inflate(infl); @@ -603,44 +487,106 @@ void _arrange( for (Item &itm : inp) itm.inflate(-infl); } -// The final client function for arrangement. A progress indicator and -// a stop predicate can be also be passed to control the process. -void arrange(ArrangePolygons & arrangables, - const ArrangePolygons & excludes, - coord_t min_obj_dist, - const BedShapeHint & bedhint, - std::function progressind, - std::function stopcondition) +inline Box to_nestbin(const BoundingBox &bb) { return Box{{bb.min(X), bb.min(Y)}, {bb.max(X), bb.max(Y)}};} +inline Circle to_nestbin(const CircleBed &c) { return Circle({c.center()(0), c.center()(1)}, c.radius()); } +inline clppr::Polygon to_nestbin(const Polygon &p) { return sl::create(Slic3rMultiPoint_to_ClipperPath(p)); } +inline Box to_nestbin(const InfiniteBed &bed) { return Box::infinite({bed.center.x(), bed.center.y()}); } + +inline coord_t width(const BoundingBox& box) { return box.max.x() - box.min.x(); } +inline coord_t height(const BoundingBox& box) { return box.max.y() - box.min.y(); } +inline double area(const BoundingBox& box) { return double(width(box)) * height(box); } +inline double poly_area(const Points &pts) { return std::abs(Polygon::area(pts)); } +inline double distance_to(const Point& p1, const Point& p2) +{ + double dx = p2.x() - p1.x(); + double dy = p2.y() - p1.y(); + return std::sqrt(dx*dx + dy*dy); +} + +static CircleBed to_circle(const Point ¢er, const Points& points) { + std::vector vertex_distances; + double avg_dist = 0; + + for (auto pt : points) + { + double distance = distance_to(center, pt); + vertex_distances.push_back(distance); + avg_dist += distance; + } + + avg_dist /= vertex_distances.size(); + + CircleBed ret(center, avg_dist); + for(auto el : vertex_distances) + { + if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) { + ret = {}; + break; + } + } + + return ret; +} + +// Create Item from Arrangeable +static void process_arrangeable(const ArrangePolygon &arrpoly, + std::vector & outp) +{ + Polygon p = arrpoly.poly.contour; + const Vec2crd &offs = arrpoly.translation; + double rotation = arrpoly.rotation; + + if (p.is_counter_clockwise()) p.reverse(); + + clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p)); + + if (!clpath.Contour.empty()) { + auto firstp = clpath.Contour.front(); + clpath.Contour.emplace_back(firstp); + } + + outp.emplace_back(std::move(clpath)); + outp.back().rotation(rotation); + outp.back().translation({offs.x(), offs.y()}); + outp.back().binId(arrpoly.bed_idx); + outp.back().priority(arrpoly.priority); +} + +template<> +void arrange(ArrangePolygons & items, + const ArrangePolygons &excludes, + const Points & bed, + const ArrangeParams & params) +{ + if (bed.empty()) + arrange(items, excludes, InfiniteBed{}, params); + else if (bed.size() == 1) + arrange(items, excludes, InfiniteBed{bed.front()}, params); + else { + auto bb = BoundingBox(bed); + CircleBed circ = to_circle(bb.center(), bed); + auto parea = poly_area(bed); + + if ((1.0 - parea / area(bb)) < 1e-3) + arrange(items, excludes, bb, params); + else if (!std::isnan(circ.radius())) + arrange(items, excludes, circ, params); + else + arrange(items, excludes, Polygon(bed), params); + } +} + +template +void arrange(ArrangePolygons & arrangables, + const ArrangePolygons &excludes, + const BedT & bed, + const ArrangeParams & params) { namespace clppr = ClipperLib; std::vector items, fixeditems; items.reserve(arrangables.size()); - // Create Item from Arrangeable - auto process_arrangeable = [](const ArrangePolygon &arrpoly, - std::vector & outp) - { - Polygon p = arrpoly.poly.contour; - const Vec2crd &offs = arrpoly.translation; - double rotation = arrpoly.rotation; - - if (p.is_counter_clockwise()) p.reverse(); - - clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p)); - - if (!clpath.Contour.empty()) { - auto firstp = clpath.Contour.front(); - clpath.Contour.emplace_back(firstp); - } - - outp.emplace_back(std::move(clpath)); - outp.back().rotation(rotation); - outp.back().translation({offs.x(), offs.y()}); - outp.back().binId(arrpoly.bed_idx); - outp.back().priority(arrpoly.priority); - }; - for (ArrangePolygon &arrangeable : arrangables) process_arrangeable(arrangeable, items); @@ -649,45 +595,10 @@ void arrange(ArrangePolygons & arrangables, for (Item &itm : fixeditems) itm.inflate(scaled(-2. * EPSILON)); - auto &cfn = stopcondition; - auto &pri = progressind; + auto &cfn = params.stopcondition; + auto &pri = params.progressind; - switch (bedhint.get_type()) { - case bsBox: { - // Create the arranger for the box shaped bed - BoundingBox bbb = bedhint.get_box(); - Box binbb{{bbb.min(X), bbb.min(Y)}, {bbb.max(X), bbb.max(Y)}}; - - _arrange(items, fixeditems, binbb, min_obj_dist, pri, cfn); - break; - } - case bsCircle: { - auto cc = to_lnCircle(bedhint.get_circle()); - - _arrange(items, fixeditems, cc, min_obj_dist, pri, cfn); - break; - } - case bsIrregular: { - auto ctour = Slic3rMultiPoint_to_ClipperPath(bedhint.get_irregular()); - auto irrbed = sl::create(std::move(ctour)); - BoundingBox polybb(bedhint.get_irregular()); - - _arrange(items, fixeditems, irrbed, min_obj_dist, pri, cfn); - break; - } - case bsInfinite: { - const InfiniteBed& nobin = bedhint.get_infinite(); - auto infbb = Box::infinite({nobin.center.x(), nobin.center.y()}); - - _arrange(items, fixeditems, infbb, min_obj_dist, pri, cfn); - break; - } - case bsUnknown: { - // We know nothing about the bed, let it be infinite and zero centered - _arrange(items, fixeditems, Box::infinite(), min_obj_dist, pri, cfn); - break; - } - } + _arrange(items, fixeditems, to_nestbin(bed), params, pri, cfn); for(size_t i = 0; i < items.size(); ++i) { clppr::IntPoint tr = items[i].translation(); @@ -697,15 +608,10 @@ void arrange(ArrangePolygons & arrangables, } } -// Arrange, without the fixed items (excludes) -void arrange(ArrangePolygons & inp, - coord_t min_d, - const BedShapeHint & bedhint, - std::function prfn, - std::function stopfn) -{ - arrange(inp, {}, min_d, bedhint, prfn, stopfn); -} +template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const BoundingBox &bed, const ArrangeParams ¶ms); +template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const CircleBed &bed, const ArrangeParams ¶ms); +template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Polygon &bed, const ArrangeParams ¶ms); +template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const InfiniteBed &bed, const ArrangeParams ¶ms); } // namespace arr } // namespace Slic3r diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index 1cfe1c907d..352e9e1cfb 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -1,12 +1,10 @@ -#ifndef MODELARRANGE_HPP -#define MODELARRANGE_HPP +#ifndef ARRANGE_HPP +#define ARRANGE_HPP #include "ExPolygon.hpp" #include "BoundingBox.hpp" -namespace Slic3r { - -namespace arrangement { +namespace Slic3r { namespace arrangement { /// A geometry abstraction for a circular print bed. Similarly to BoundingBox. class CircleBed { @@ -15,96 +13,16 @@ class CircleBed { public: inline CircleBed(): center_(0, 0), radius_(std::nan("")) {} - inline CircleBed(const Point& c, double r): center_(c), radius_(r) {} + explicit inline CircleBed(const Point& c, double r): center_(c), radius_(r) {} inline double radius() const { return radius_; } inline const Point& center() const { return center_; } - inline operator bool() { return !std::isnan(radius_); } }; /// Representing an unbounded bed. -struct InfiniteBed { Point center; }; - -/// Types of print bed shapes. -enum BedShapes { - bsBox, - bsCircle, - bsIrregular, - bsInfinite, - bsUnknown -}; - -/// Info about the print bed for the arrange() function. This is a variant -/// holding one of the four shapes a bed can be. -class BedShapeHint { - BedShapes m_type = BedShapes::bsInfinite; - - // The union neither calls constructors nor destructors of its members. - // The only member with non-trivial constructor / destructor is the polygon, - // a placement new / delete needs to be called over it. - union BedShape_u { // TODO: use variant from cpp17? - CircleBed circ; - BoundingBox box; - Polyline polygon; - InfiniteBed infbed{}; - ~BedShape_u() {} - BedShape_u() {} - } m_bed; - - // Reset the type, allocate m_bed properly - void reset(BedShapes type); - -public: - - BedShapeHint(){} - - /// Get a bed shape hint for arrange() from a naked Polyline. - explicit BedShapeHint(const Polyline &polyl); - explicit BedShapeHint(const BoundingBox &bb) - { - m_type = bsBox; m_bed.box = bb; - } - - explicit BedShapeHint(const CircleBed &c) - { - m_type = bsCircle; m_bed.circ = c; - } - - explicit BedShapeHint(const InfiniteBed &ibed) - { - m_type = bsInfinite; m_bed.infbed = ibed; - } - - ~BedShapeHint() - { - if (m_type == BedShapes::bsIrregular) - m_bed.polygon.Slic3r::Polyline::~Polyline(); - } - - BedShapeHint(const BedShapeHint &cpy) { *this = cpy; } - BedShapeHint(BedShapeHint &&cpy) { *this = std::move(cpy); } - - BedShapeHint &operator=(const BedShapeHint &cpy); - BedShapeHint& operator=(BedShapeHint &&cpy); - - BedShapes get_type() const { return m_type; } - - const BoundingBox &get_box() const - { - assert(m_type == bsBox); return m_bed.box; - } - const CircleBed &get_circle() const - { - assert(m_type == bsCircle); return m_bed.circ; - } - const Polyline &get_irregular() const - { - assert(m_type == bsIrregular); return m_bed.polygon; - } - const InfiniteBed &get_infinite() const - { - assert(m_type == bsInfinite); return m_bed.infbed; - } +struct InfiniteBed { + Point center; + explicit InfiniteBed(const Point &p = {0, 0}): center{p} {} }; /// A logical bed representing an object not being arranged. Either the arrange @@ -125,9 +43,14 @@ struct ArrangePolygon { ExPolygon poly; /// The 2D silhouette to be arranged Vec2crd translation{0, 0}; /// The translation of the poly double rotation{0.0}; /// The rotation of the poly in radians + coord_t inflation = 0; /// Arrange with inflated polygon int bed_idx{UNARRANGED}; /// To which logical bed does poly belong... int priority{0}; + // If empty, any rotation is allowed (currently unsupported) + // If only a zero is there, no rotation is allowed + std::vector allowed_rotations = {0.}; + /// Optional setter function which can store arbitrary data in its closure std::function setter = nullptr; @@ -140,6 +63,30 @@ struct ArrangePolygon { using ArrangePolygons = std::vector; +struct ArrangeParams { + + /// The minimum distance which is allowed for any + /// pair of items on the print bed in any direction. + coord_t min_obj_distance = 0.; + + /// The accuracy of optimization. + /// Goes from 0.0 to 1.0 and scales performance as well + float accuracy = 0.65f; + + /// Allow parallel execution. + bool parallel = true; + + /// Progress indicator callback called when an object gets packed. + /// The unsigned argument is the number of items remaining to pack. + std::function progressind; + + /// A predicate returning true if abort is needed. + std::function stopcondition; + + ArrangeParams() = default; + explicit ArrangeParams(coord_t md) : min_obj_distance(md) {} +}; + /** * \brief Arranges the input polygons. * @@ -150,33 +97,23 @@ using ArrangePolygons = std::vector; * \param items Input vector of ArrangePolygons. The transformation, rotation * and bin_idx fields will be changed after the call finished and can be used * to apply the result on the input polygon. - * - * \param min_obj_distance The minimum distance which is allowed for any - * pair of items on the print bed in any direction. - * - * \param bedhint Info about the shape and type of the bed. - * - * \param progressind Progress indicator callback called when - * an object gets packed. The unsigned argument is the number of items - * remaining to pack. - * - * \param stopcondition A predicate returning true if abort is needed. */ -void arrange(ArrangePolygons & items, - coord_t min_obj_distance, - const BedShapeHint & bedhint, - std::function progressind = nullptr, - std::function stopcondition = nullptr); +template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const TBed &bed, const ArrangeParams ¶ms = {}); -/// Same as the previous, only that it takes unmovable items as an -/// additional argument. Those will be considered as already arranged objects. -void arrange(ArrangePolygons & items, - const ArrangePolygons & excludes, - coord_t min_obj_distance, - const BedShapeHint & bedhint, - std::function progressind = nullptr, - std::function stopcondition = nullptr); +// A dispatch function that determines the bed shape from a set of points. +template<> void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Points &bed, const ArrangeParams ¶ms); + +extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const BoundingBox &bed, const ArrangeParams ¶ms); +extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const CircleBed &bed, const ArrangeParams ¶ms); +extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Polygon &bed, const ArrangeParams ¶ms); +extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const InfiniteBed &bed, const ArrangeParams ¶ms); + +inline void arrange(ArrangePolygons &items, const Points &bed, const ArrangeParams ¶ms = {}) { arrange(items, {}, bed, params); } +inline void arrange(ArrangePolygons &items, const BoundingBox &bed, const ArrangeParams ¶ms = {}) { arrange(items, {}, bed, params); } +inline void arrange(ArrangePolygons &items, const CircleBed &bed, const ArrangeParams ¶ms = {}) { arrange(items, {}, bed, params); } +inline void arrange(ArrangePolygons &items, const Polygon &bed, const ArrangeParams ¶ms = {}) { arrange(items, {}, bed, params); } +inline void arrange(ArrangePolygons &items, const InfiniteBed &bed, const ArrangeParams ¶ms = {}) { arrange(items, {}, bed, params); } + +}} // namespace Slic3r::arrangement -} // arr -} // Slic3r #endif // MODELARRANGE_HPP diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index 4fbe72163c..2216d78882 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -186,6 +186,11 @@ inline bool empty(const BoundingBox3Base &bb) return ! bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1) || bb.min(2) >= bb.max(2); } +inline BoundingBox scaled(const BoundingBoxf &bb) { return {scaled(bb.min), scaled(bb.max)}; } +inline BoundingBox3 scaled(const BoundingBoxf3 &bb) { return {scaled(bb.min), scaled(bb.max)}; } +inline BoundingBoxf unscaled(const BoundingBox &bb) { return {unscaled(bb.min), unscaled(bb.max)}; } +inline BoundingBoxf3 unscaled(const BoundingBox3 &bb) { return {unscaled(bb.min), unscaled(bb.max)}; } + } // namespace Slic3r // Serialization through the Cereal library diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index b37e09ad09..30614100f6 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -77,6 +77,8 @@ add_library(libslic3r STATIC Format/PRUS.hpp Format/STL.cpp Format/STL.hpp + Format/SL1.hpp + Format/SL1.cpp GCode/Analyzer.cpp GCode/Analyzer.hpp GCode/ThumbnailData.cpp @@ -120,6 +122,8 @@ add_library(libslic3r STATIC Line.hpp Model.cpp Model.hpp + ModelArrange.hpp + ModelArrange.cpp CustomGCode.cpp CustomGCode.hpp Arrange.hpp @@ -160,6 +164,8 @@ add_library(libslic3r STATIC SLAPrint.hpp Slicing.cpp Slicing.hpp + SlicesToTriangleMesh.hpp + SlicesToTriangleMesh.cpp SlicingAdaptive.cpp SlicingAdaptive.hpp SupportMaterial.cpp @@ -175,6 +181,8 @@ add_library(libslic3r STATIC Tesselate.hpp TriangleMesh.cpp TriangleMesh.hpp + TriangulateWall.hpp + TriangulateWall.cpp utils.cpp Utils.hpp Time.cpp @@ -189,6 +197,7 @@ add_library(libslic3r STATIC SimplifyMesh.hpp SimplifyMeshImpl.hpp SimplifyMesh.cpp + MarchingSquares.hpp ${OpenVDBUtils_SOURCES} SLA/Common.hpp SLA/Common.cpp @@ -206,10 +215,11 @@ add_library(libslic3r STATIC SLA/Rotfinder.cpp SLA/BoostAdapter.hpp SLA/SpatIndex.hpp - SLA/Raster.hpp - SLA/Raster.cpp - SLA/RasterWriter.hpp - SLA/RasterWriter.cpp + SLA/RasterBase.hpp + SLA/RasterBase.cpp + SLA/AGGRaster.hpp + SLA/RasterToPolygons.hpp + SLA/RasterToPolygons.cpp SLA/ConcaveHull.hpp SLA/ConcaveHull.cpp SLA/Hollowing.hpp @@ -262,7 +272,8 @@ endif () encoding_check(libslic3r) target_compile_definitions(libslic3r PUBLIC -DUSE_TBB -DTBB_USE_CAPTURED_EXCEPTION=0) -target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNEST2D_INCLUDES} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) +target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) +target_include_directories(libslic3r PUBLIC ${EXPAT_INCLUDE_DIRS}) target_link_libraries(libslic3r libnest2d admesh diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp new file mode 100644 index 0000000000..ba5e89330b --- /dev/null +++ b/src/libslic3r/Format/SL1.cpp @@ -0,0 +1,171 @@ +#include "SL1.hpp" +#include "GCode/ThumbnailData.hpp" +#include "libslic3r/Time.hpp" + +#include +#include + +#include "libslic3r/Zipper.hpp" +#include "libslic3r/SLAPrint.hpp" + +namespace Slic3r { + +using ConfMap = std::map; + +namespace { + +std::string to_ini(const ConfMap &m) +{ + std::string ret; + for (auto ¶m : m) ret += param.first + " = " + param.second + "\n"; + + return ret; +} + +std::string get_cfg_value(const DynamicPrintConfig &cfg, const std::string &key) +{ + std::string ret; + + if (cfg.has(key)) { + auto opt = cfg.option(key); + if (opt) ret = opt->serialize(); + } + + return ret; +} + +void fill_iniconf(ConfMap &m, const SLAPrint &print) +{ + auto &cfg = print.full_print_config(); + m["layerHeight"] = get_cfg_value(cfg, "layer_height"); + m["expTime"] = get_cfg_value(cfg, "exposure_time"); + m["expTimeFirst"] = get_cfg_value(cfg, "initial_exposure_time"); + m["materialName"] = get_cfg_value(cfg, "sla_material_settings_id"); + m["printerModel"] = get_cfg_value(cfg, "printer_model"); + m["printerVariant"] = get_cfg_value(cfg, "printer_variant"); + m["printerProfile"] = get_cfg_value(cfg, "printer_settings_id"); + m["printProfile"] = get_cfg_value(cfg, "sla_print_settings_id"); + m["fileCreationTimestamp"] = Utils::utc_timestamp(); + m["prusaSlicerVersion"] = SLIC3R_BUILD_ID; + + SLAPrintStatistics stats = print.print_statistics(); + // Set statistics values to the printer + + double used_material = (stats.objects_used_material + + stats.support_used_material) / 1000; + + int num_fade = print.default_object_config().faded_layers.getInt(); + num_fade = num_fade >= 0 ? num_fade : 0; + + m["usedMaterial"] = std::to_string(used_material); + m["numFade"] = std::to_string(num_fade); + m["numSlow"] = std::to_string(stats.slow_layers_count); + m["numFast"] = std::to_string(stats.fast_layers_count); + m["printTime"] = std::to_string(stats.estimated_print_time); + + m["action"] = "print"; +} + +void fill_slicerconf(ConfMap &m, const SLAPrint &print) +{ + using namespace std::literals::string_view_literals; + + // Sorted list of config keys, which shall not be stored into the ini. + static constexpr auto banned_keys = { + "compatible_printers"sv, + "compatible_prints"sv, + "print_host"sv, + "printhost_apikey"sv, + "printhost_cafile"sv + }; + + assert(std::is_sorted(banned_keys.begin(), banned_keys.end())); + auto is_banned = [](const std::string &key) { + return std::binary_search(banned_keys.begin(), banned_keys.end(), key); + }; + + auto &cfg = print.full_print_config(); + for (const std::string &key : cfg.keys()) + if (! is_banned(key) && ! cfg.option(key)->is_nil()) + m[key] = cfg.opt_serialize(key); + +} + +} // namespace + +uqptr SL1Archive::create_raster() const +{ + sla::RasterBase::Resolution res; + sla::RasterBase::PixelDim pxdim; + std::array mirror; + + double w = m_cfg.display_width.getFloat(); + double h = m_cfg.display_height.getFloat(); + auto pw = size_t(m_cfg.display_pixels_x.getInt()); + auto ph = size_t(m_cfg.display_pixels_y.getInt()); + + mirror[X] = m_cfg.display_mirror_x.getBool(); + mirror[Y] = m_cfg.display_mirror_y.getBool(); + + auto ro = m_cfg.display_orientation.getInt(); + sla::RasterBase::Orientation orientation = + ro == sla::RasterBase::roPortrait ? sla::RasterBase::roPortrait : + sla::RasterBase::roLandscape; + + if (orientation == sla::RasterBase::roPortrait) { + std::swap(w, h); + std::swap(pw, ph); + } + + res = sla::RasterBase::Resolution{pw, ph}; + pxdim = sla::RasterBase::PixelDim{w / pw, h / ph}; + sla::RasterBase::Trafo tr{orientation, mirror}; + + double gamma = m_cfg.gamma_correction.getFloat(); + + return sla::create_raster_grayscale_aa(res, pxdim, gamma, tr); +} + +sla::EncodedRaster SL1Archive::encode_raster(const sla::RasterBase &rst) const +{ + return rst.encode(sla::PNGRasterEncoder()); +} + +void SL1Archive::export_print(Zipper& zipper, + const SLAPrint &print, + const std::string &prjname) +{ + std::string project = + prjname.empty() ? + boost::filesystem::path(zipper.get_filename()).stem().string() : + prjname; + + ConfMap iniconf, slicerconf; + fill_iniconf(iniconf, print); + + iniconf["jobDir"] = project; + + fill_slicerconf(slicerconf, print); + + try { + zipper.add_entry("config.ini"); + zipper << to_ini(iniconf); + zipper.add_entry("prusaslicer.ini"); + zipper << to_ini(slicerconf); + + size_t i = 0; + for (const sla::EncodedRaster &rst : m_layers) { + + std::string imgname = project + string_printf("%.5d", i++) + "." + + rst.extension(); + + zipper.add_entry(imgname.c_str(), rst.data(), rst.size()); + } + } catch(std::exception& e) { + BOOST_LOG_TRIVIAL(error) << e.what(); + // Rethrow the exception + throw; + } +} + +} // namespace Slic3r diff --git a/src/libslic3r/Format/SL1.hpp b/src/libslic3r/Format/SL1.hpp new file mode 100644 index 0000000000..1b9e95392b --- /dev/null +++ b/src/libslic3r/Format/SL1.hpp @@ -0,0 +1,44 @@ +#ifndef ARCHIVETRAITS_HPP +#define ARCHIVETRAITS_HPP + +#include + +#include "libslic3r/Zipper.hpp" +#include "libslic3r/SLAPrint.hpp" + +namespace Slic3r { + +class SL1Archive: public SLAPrinter { + SLAPrinterConfig m_cfg; + +protected: + uqptr create_raster() const override; + sla::EncodedRaster encode_raster(const sla::RasterBase &rst) const override; + +public: + + SL1Archive() = default; + explicit SL1Archive(const SLAPrinterConfig &cfg): m_cfg(cfg) {} + explicit SL1Archive(SLAPrinterConfig &&cfg): m_cfg(std::move(cfg)) {} + + void export_print(Zipper &zipper, const SLAPrint &print, const std::string &projectname = ""); + void export_print(const std::string &fname, const SLAPrint &print, const std::string &projectname = "") + { + Zipper zipper(fname); + export_print(zipper, print, projectname); + } + + void apply(const SLAPrinterConfig &cfg) override + { + auto diff = m_cfg.diff(cfg); + if (!diff.empty()) { + m_cfg.apply_only(cfg, diff); + m_layers = {}; + } + } +}; + + +} // namespace Slic3r::sla + +#endif // ARCHIVETRAITS_HPP diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp index 3402d2c856..294f9ae6f5 100644 --- a/src/libslic3r/MTUtils.hpp +++ b/src/libslic3r/MTUtils.hpp @@ -11,6 +11,7 @@ #include "libslic3r.h" #include "Point.hpp" +#include "BoundingBox.hpp" namespace Slic3r { @@ -75,143 +76,6 @@ public: } }; -/// An std compatible random access iterator which uses indices to the -/// source vector thus resistant to invalidation caused by relocations. It -/// also "knows" its container. No comparison is neccesary to the container -/// "end()" iterator. The template can be instantiated with a different -/// value type than that of the container's but the types must be -/// compatible. E.g. a base class of the contained objects is compatible. -/// -/// For a constant iterator, one can instantiate this template with a value -/// type preceded with 'const'. -template -class IndexBasedIterator -{ - static const size_t NONE = size_t(-1); - - std::reference_wrapper m_index_ref; - size_t m_idx = NONE; - -public: - using value_type = Value; - using pointer = Value *; - using reference = Value &; - using difference_type = long; - using iterator_category = std::random_access_iterator_tag; - - inline explicit IndexBasedIterator(Vector &index, size_t idx) - : m_index_ref(index), m_idx(idx) - {} - - // Post increment - inline IndexBasedIterator operator++(int) - { - IndexBasedIterator cpy(*this); - ++m_idx; - return cpy; - } - - inline IndexBasedIterator operator--(int) - { - IndexBasedIterator cpy(*this); - --m_idx; - return cpy; - } - - inline IndexBasedIterator &operator++() - { - ++m_idx; - return *this; - } - - inline IndexBasedIterator &operator--() - { - --m_idx; - return *this; - } - - inline IndexBasedIterator &operator+=(difference_type l) - { - m_idx += size_t(l); - return *this; - } - - inline IndexBasedIterator operator+(difference_type l) - { - auto cpy = *this; - cpy += l; - return cpy; - } - - inline IndexBasedIterator &operator-=(difference_type l) - { - m_idx -= size_t(l); - return *this; - } - - inline IndexBasedIterator operator-(difference_type l) - { - auto cpy = *this; - cpy -= l; - return cpy; - } - - operator difference_type() { return difference_type(m_idx); } - - /// Tesing the end of the container... this is not possible with std - /// iterators. - inline bool is_end() const - { - return m_idx >= m_index_ref.get().size(); - } - - inline Value &operator*() const - { - assert(m_idx < m_index_ref.get().size()); - return m_index_ref.get().operator[](m_idx); - } - - inline Value *operator->() const - { - assert(m_idx < m_index_ref.get().size()); - return &m_index_ref.get().operator[](m_idx); - } - - /// If both iterators point past the container, they are equal... - inline bool operator==(const IndexBasedIterator &other) - { - size_t e = m_index_ref.get().size(); - return m_idx == other.m_idx || (m_idx >= e && other.m_idx >= e); - } - - inline bool operator!=(const IndexBasedIterator &other) - { - return !(*this == other); - } - - inline bool operator<=(const IndexBasedIterator &other) - { - return (m_idx < other.m_idx) || (*this == other); - } - - inline bool operator<(const IndexBasedIterator &other) - { - return m_idx < other.m_idx && (*this != other); - } - - inline bool operator>=(const IndexBasedIterator &other) - { - return m_idx > other.m_idx || *this == other; - } - - inline bool operator>(const IndexBasedIterator &other) - { - return m_idx > other.m_idx && *this != other; - } -}; - /// A very simple range concept implementation with iterator-like objects. template class Range { @@ -252,97 +116,6 @@ template struct remove_cvref template using remove_cvref_t = typename remove_cvref::type; -// A shorter C++14 style form of the enable_if metafunction -template -using enable_if_t = typename std::enable_if::type; - -// ///////////////////////////////////////////////////////////////////////////// -// Type safe conversions to and from scaled and unscaled coordinates -// ///////////////////////////////////////////////////////////////////////////// - -// A meta-predicate which is true for integers wider than or equal to coord_t -template struct is_scaled_coord -{ - static const SLIC3R_CONSTEXPR bool value = - std::is_integral::value && - std::numeric_limits::digits >= - std::numeric_limits::digits; -}; - -// Meta predicates for floating, 'scaled coord' and generic arithmetic types -template -using FloatingOnly = enable_if_t::value, O>; - -template -using ScaledCoordOnly = enable_if_t::value, O>; - -template -using IntegerOnly = enable_if_t::value, O>; - -template -using ArithmeticOnly = enable_if_t::value, O>; - -// Semantics are the following: -// Upscaling (scaled()): only from floating point types (or Vec) to either -// floating point or integer 'scaled coord' coordinates. -// Downscaling (unscaled()): from arithmetic (or Vec) to floating point only - -// Conversion definition from unscaled to floating point scaled -template> -inline constexpr FloatingOnly scaled(const Tin &v) noexcept -{ - return Tout(v / Tin(SCALING_FACTOR)); -} - -// Conversion definition from unscaled to integer 'scaled coord'. -// TODO: is the rounding necessary? Here it is commented out to show that -// it can be different for integers but it does not have to be. Using -// std::round means loosing noexcept and constexpr modifiers -template> -inline constexpr ScaledCoordOnly scaled(const Tin &v) noexcept -{ - //return static_cast(std::round(v / SCALING_FACTOR)); - return Tout(v / Tin(SCALING_FACTOR)); -} - -// Conversion for Eigen vectors (N dimensional points) -template, - int...EigenArgs> -inline Eigen::Matrix, N, EigenArgs...> -scaled(const Eigen::Matrix &v) -{ - return (v / SCALING_FACTOR).template cast(); -} - -// Conversion from arithmetic scaled type to floating point unscaled -template, - class = FloatingOnly> -inline constexpr Tout unscaled(const Tin &v) noexcept -{ - return Tout(v * Tout(SCALING_FACTOR)); -} - -// Unscaling for Eigen vectors. Input base type can be arithmetic, output base -// type can only be floating point. -template, - class = FloatingOnly, - int...EigenArgs> -inline constexpr Eigen::Matrix -unscaled(const Eigen::Matrix &v) noexcept -{ - return v.template cast() * SCALING_FACTOR; -} - template // Arbitrary allocator can be used inline IntegerOnly> reserve_vector(I capacity) { @@ -353,10 +126,10 @@ inline IntegerOnly> reserve_vector(I capacity) } /// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html -template +template> inline std::vector linspace_vector(const ArithmeticOnly &start, const T &stop, - const IntegerOnly &n) + const I &n) { std::vector vals(n, T()); diff --git a/src/libslic3r/MarchingSquares.hpp b/src/libslic3r/MarchingSquares.hpp new file mode 100644 index 0000000000..d5f07fbde6 --- /dev/null +++ b/src/libslic3r/MarchingSquares.hpp @@ -0,0 +1,448 @@ +#ifndef MARCHINGSQUARES_HPP +#define MARCHINGSQUARES_HPP + +#include +#include +#include +#include +#include + +namespace marchsq { + +// Marks a square in the grid +struct Coord { + long r = 0, c = 0; + + Coord() = default; + explicit Coord(long s) : r(s), c(s) {} + Coord(long _r, long _c): r(_r), c(_c) {} + + size_t seq(const Coord &res) const { return r * res.c + c; } + Coord& operator+=(const Coord& b) { r += b.r; c += b.c; return *this; } + Coord operator+(const Coord& b) const { Coord a = *this; a += b; return a; } +}; + +// Closed ring of cell coordinates +using Ring = std::vector; + +// Specialize this struct to register a raster type for the Marching squares alg +template struct _RasterTraits { + + // The type of pixel cell in the raster + using ValueType = typename T::ValueType; + + // Value at a given position + static ValueType get(const T &raster, size_t row, size_t col); + + // Number of rows and cols of the raster + static size_t rows(const T &raster); + static size_t cols(const T &raster); +}; + +// Specialize this to use parellel loops within the algorithm +template struct _Loop { + template static void for_each(It from, It to, Fn &&fn) + { + for (auto it = from; it < to; ++it) fn(*it, size_t(it - from)); + } +}; + +namespace __impl { + +template using RasterTraits = _RasterTraits>; +template using TRasterValue = typename RasterTraits::ValueType; + +template size_t rows(const T &raster) +{ + return RasterTraits::rows(raster); +} + +template size_t cols(const T &raster) +{ + return RasterTraits::cols(raster); +} + +template TRasterValue isoval(const T &rst, const Coord &crd) +{ + return RasterTraits::get(rst, crd.r, crd.c); +} + +template +void for_each(ExecutionPolicy&& policy, It from, It to, Fn &&fn) +{ + _Loop::for_each(from, to, fn); +} + +// Type of squares (tiles) depending on which vertices are inside an ROI +// The vertices would be marked a, b, c, d in counter clockwise order from the +// bottom left vertex of a square. +// d --- c +// | | +// | | +// a --- b +enum class SquareTag : uint8_t { +// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + none, a, b, ab, c, ac, bc, abc, d, ad, bd, abd, cd, acd, bcd, full +}; + +template constexpr std::underlying_type_t _t(E e) noexcept +{ + return static_cast>(e); +} + +enum class Dir: uint8_t { left, down, right, up, none}; + +static const constexpr Dir NEXT_CCW[] = { + /* 00 */ Dir::none, // SquareTag::none (empty square, nowhere to go) + /* 01 */ Dir::left, // SquareTag::a + /* 02 */ Dir::down, // SquareTag::b + /* 03 */ Dir::left, // SquareTag::ab + /* 04 */ Dir::right, // SquareTag::c + /* 05 */ Dir::none, // SquareTag::ac (ambiguous case) + /* 06 */ Dir::down, // SquareTag::bc + /* 07 */ Dir::left, // SquareTag::abc + /* 08 */ Dir::up, // SquareTag::d + /* 09 */ Dir::up, // SquareTag::ad + /* 10 */ Dir::none, // SquareTag::bd (ambiguous case) + /* 11 */ Dir::up, // SquareTag::abd + /* 12 */ Dir::right, // SquareTag::cd + /* 13 */ Dir::right, // SquareTag::acd + /* 14 */ Dir::down, // SquareTag::bcd + /* 15 */ Dir::none // SquareTag::full (full covered, nowhere to go) +}; + +static const constexpr uint8_t PREV_CCW[] = { + /* 00 */ 1 << _t(Dir::none), + /* 01 */ 1 << _t(Dir::up), + /* 02 */ 1 << _t(Dir::left), + /* 03 */ 1 << _t(Dir::left), + /* 04 */ 1 << _t(Dir::down), + /* 05 */ 1 << _t(Dir::up) | 1 << _t(Dir::down), + /* 06 */ 1 << _t(Dir::down), + /* 07 */ 1 << _t(Dir::down), + /* 08 */ 1 << _t(Dir::right), + /* 09 */ 1 << _t(Dir::up), + /* 10 */ 1 << _t(Dir::left) | 1 << _t(Dir::right), + /* 11 */ 1 << _t(Dir::left), + /* 12 */ 1 << _t(Dir::right), + /* 13 */ 1 << _t(Dir::up), + /* 14 */ 1 << _t(Dir::right), + /* 15 */ 1 << _t(Dir::none) +}; + +const constexpr uint8_t DIRMASKS[] = { + /*left: */ 0x01, /*down*/ 0x12, /*right */0x21, /*up*/ 0x10, /*none*/ 0x00 +}; + +inline Coord step(const Coord &crd, Dir d) +{ + uint8_t dd = DIRMASKS[uint8_t(d)]; + return {crd.r - 1 + (dd & 0x0f), crd.c - 1 + (dd >> 4)}; +} + +template class Grid { + const Rst * m_rst = nullptr; + Coord m_cellsize, m_res_1, m_window, m_gridsize, m_grid_1; + std::vector m_tags; // Assign tags to each square + + Coord rastercoord(const Coord &crd) const + { + return {(crd.r - 1) * m_window.r, (crd.c - 1) * m_window.c}; + } + + Coord bl(const Coord &crd) const { return tl(crd) + Coord{m_res_1.r, 0}; } + Coord br(const Coord &crd) const { return tl(crd) + Coord{m_res_1.r, m_res_1.c}; } + Coord tr(const Coord &crd) const { return tl(crd) + Coord{0, m_res_1.c}; } + Coord tl(const Coord &crd) const { return rastercoord(crd); } + + bool is_within(const Coord &crd) + { + long R = rows(*m_rst), C = cols(*m_rst); + return crd.r >= 0 && crd.r < R && crd.c >= 0 && crd.c < C; + }; + + // Calculate the tag for a cell (or square). The cell coordinates mark the + // top left vertex of a square in the raster. v is the isovalue + uint8_t get_tag_for_cell(const Coord &cell, TRasterValue v) + { + Coord sqr[] = {bl(cell), br(cell), tr(cell), tl(cell)}; + + uint8_t t = ((is_within(sqr[0]) && isoval(*m_rst, sqr[0]) >= v)) + + ((is_within(sqr[1]) && isoval(*m_rst, sqr[1]) >= v) << 1) + + ((is_within(sqr[2]) && isoval(*m_rst, sqr[2]) >= v) << 2) + + ((is_within(sqr[3]) && isoval(*m_rst, sqr[3]) >= v) << 3); + + assert(t < 16); + return t; + } + + // Get a cell coordinate from a sequential index + Coord coord(size_t i) const + { + return {long(i) / m_gridsize.c, long(i) % m_gridsize.c}; + } + + size_t seq(const Coord &crd) const { return crd.seq(m_gridsize); } + + bool is_visited(size_t idx, Dir d = Dir::none) const + { + SquareTag t = get_tag(idx); + uint8_t ref = d == Dir::none ? PREV_CCW[_t(t)] : uint8_t(1 << _t(d)); + return t == SquareTag::full || t == SquareTag::none || + ((m_tags[idx] & 0xf0) >> 4) == ref; + } + + void set_visited(size_t idx, Dir d = Dir::none) + { + m_tags[idx] |= (1 << (_t(d)) << 4); + } + + bool is_ambiguous(size_t idx) const + { + SquareTag t = get_tag(idx); + return t == SquareTag::ac || t == SquareTag::bd; + } + + // Search for a new starting square + size_t search_start_cell(size_t i = 0) const + { + // Skip ambiguous tags as starting tags due to unknown previous + // direction. + while ((i < m_tags.size()) && (is_visited(i) || is_ambiguous(i))) ++i; + + return i; + } + + SquareTag get_tag(size_t idx) const { return SquareTag(m_tags[idx] & 0x0f); } + + Dir next_dir(Dir prev, SquareTag tag) const + { + // Treat ambiguous cases as two separate regions in one square. + switch (tag) { + case SquareTag::ac: + switch (prev) { + case Dir::down: return Dir::right; + case Dir::up: return Dir::left; + default: assert(false); return Dir::none; + } + case SquareTag::bd: + switch (prev) { + case Dir::right: return Dir::up; + case Dir::left: return Dir::down; + default: assert(false); return Dir::none; + } + default: + return NEXT_CCW[uint8_t(tag)]; + } + + return Dir::none; + } + + struct CellIt { + Coord crd; Dir dir= Dir::none; const Rst *grid = nullptr; + + TRasterValue operator*() const { return isoval(*grid, crd); } + CellIt& operator++() { crd = step(crd, dir); return *this; } + CellIt operator++(int) { CellIt it = *this; ++(*this); return it; } + bool operator!=(const CellIt &it) { return crd.r != it.crd.r || crd.c != it.crd.c; } + + using value_type = TRasterValue; + using pointer = TRasterValue *; + using reference = TRasterValue &; + using difference_type = long; + using iterator_category = std::forward_iterator_tag; + }; + + // Two cell iterators representing an edge of a square. This is then + // used for binary search for the first active pixel on the edge. + struct Edge { CellIt from, to; }; + + Edge _edge(const Coord &ringvertex) const + { + size_t idx = ringvertex.r; + Coord cell = coord(idx); + uint8_t tg = m_tags[ringvertex.r]; + SquareTag t = SquareTag(tg & 0x0f); + + switch (t) { + case SquareTag::a: + case SquareTag::ab: + case SquareTag::abc: + return {{tl(cell), Dir::down, m_rst}, {bl(cell)}}; + case SquareTag::b: + case SquareTag::bc: + case SquareTag::bcd: + return {{bl(cell), Dir::right, m_rst}, {br(cell)}}; + case SquareTag::c: + return {{br(cell), Dir::up, m_rst}, {tr(cell)}}; + case SquareTag::ac: + switch (Dir(ringvertex.c)) { + case Dir::left: return {{tl(cell), Dir::down, m_rst}, {bl(cell)}}; + case Dir::right: return {{br(cell), Dir::up, m_rst}, {tr(cell)}}; + default: assert(false); + } + case SquareTag::d: + case SquareTag::ad: + case SquareTag::abd: + return {{tr(cell), Dir::left, m_rst}, {tl(cell)}}; + case SquareTag::bd: + switch (Dir(ringvertex.c)) { + case Dir::down: return {{bl(cell), Dir::right, m_rst}, {br(cell)}}; + case Dir::up: return {{tr(cell), Dir::left, m_rst}, {tl(cell)}}; + default: assert(false); + } + case SquareTag::cd: + case SquareTag::acd: + return {{br(cell), Dir::up, m_rst}, {tr(cell)}}; + case SquareTag::full: + case SquareTag::none: { + Coord crd{tl(cell) + Coord{m_cellsize.r / 2, m_cellsize.c / 2}}; + return {{crd, Dir::none, m_rst}, crd}; + } + } + + return {}; + } + + Edge edge(const Coord &ringvertex) const + { + const long R = rows(*m_rst), C = cols(*m_rst); + const long R_1 = R - 1, C_1 = C - 1; + + Edge e = _edge(ringvertex); + e.to.dir = e.from.dir; + ++e.to; + + e.from.crd.r = std::min(e.from.crd.r, R_1); + e.from.crd.r = std::max(e.from.crd.r, 0l); + e.from.crd.c = std::min(e.from.crd.c, C_1); + e.from.crd.c = std::max(e.from.crd.c, 0l); + + e.to.crd.r = std::min(e.to.crd.r, R); + e.to.crd.r = std::max(e.to.crd.r, 0l); + e.to.crd.c = std::min(e.to.crd.c, C); + e.to.crd.c = std::max(e.to.crd.c, 0l); + + return e; + } + +public: + explicit Grid(const Rst &rst, const Coord &cellsz, const Coord &overlap) + : m_rst{&rst} + , m_cellsize{cellsz} + , m_res_1{m_cellsize.r - 1, m_cellsize.c - 1} + , m_window{overlap.r < cellsz.r ? cellsz.r - overlap.r : cellsz.r, + overlap.c < cellsz.c ? cellsz.c - overlap.c : cellsz.c} + , m_gridsize{2 + (long(rows(rst)) - overlap.r) / m_window.r, + 2 + (long(cols(rst)) - overlap.c) / m_window.c} + , m_tags(m_gridsize.r * m_gridsize.c, 0) + {} + + // Go through the cells and mark them with the appropriate tag. + template + void tag_grid(ExecutionPolicy &&policy, TRasterValue isoval) + { + // parallel for r + for_each (std::forward(policy), + m_tags.begin(), m_tags.end(), + [this, isoval](uint8_t& tag, size_t idx) { + tag = get_tag_for_cell(coord(idx), isoval); + }); + } + + // Scan for the rings on the tagged grid. Each ring vertex stores the + // sequential index of the cell and the next direction (Dir). + // This info can be used later to calculate the exact raster coordinate. + std::vector scan_rings() + { + std::vector rings; + size_t startidx = 0; + while ((startidx = search_start_cell(startidx)) < m_tags.size()) { + Ring ring; + + size_t idx = startidx; + Dir prev = Dir::none, next = next_dir(prev, get_tag(idx)); + + while (next != Dir::none && !is_visited(idx, prev)) { + Coord ringvertex{long(idx), long(next)}; + ring.emplace_back(ringvertex); + set_visited(idx, prev); + + idx = seq(step(coord(idx), next)); + prev = next; + next = next_dir(next, get_tag(idx)); + } + + // To prevent infinite loops in case of degenerate input + if (next == Dir::none) m_tags[startidx] = _t(SquareTag::none); + + if (ring.size() > 1) { + ring.pop_back(); + rings.emplace_back(ring); + } + } + + return rings; + } + + // Calculate the exact raster position from the cells which store the + // sequantial index of the square and the next direction + template + void interpolate_rings(ExecutionPolicy && policy, + std::vector &rings, + TRasterValue isov) + { + for_each(std::forward(policy), + rings.begin(), rings.end(), [this, isov] (Ring &ring, size_t) + { + for (Coord &ringvertex : ring) { + Edge e = edge(ringvertex); + + CellIt found = std::lower_bound(e.from, e.to, isov); + ringvertex = found.crd; + } + }); + } +}; + +template +std::vector execute_with_policy(ExecutionPolicy && policy, + const Raster & raster, + TRasterValue isoval, + Coord windowsize = {}) +{ + if (!rows(raster) || !cols(raster)) return {}; + + size_t ratio = cols(raster) / rows(raster); + + if (!windowsize.r) windowsize.r = 2; + if (!windowsize.c) + windowsize.c = std::max(2l, long(windowsize.r * ratio)); + + Coord overlap{1}; + + Grid grid{raster, windowsize, overlap}; + + grid.tag_grid(std::forward(policy), isoval); + std::vector rings = grid.scan_rings(); + grid.interpolate_rings(std::forward(policy), rings, isoval); + + return rings; +} + +template +std::vector execute(const Raster &raster, + TRasterValue isoval, + Coord windowsize = {}) +{ + return execute_with_policy(nullptr, raster, isoval, windowsize); +} + +} // namespace __impl + +using __impl::execute_with_policy; +using __impl::execute; + +} // namespace marchsq + +#endif // MARCHINGSQUARES_HPP diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 59df8eb611..98f595d91e 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1,4 +1,5 @@ #include "Model.hpp" +#include "ModelArrange.hpp" #include "Geometry.hpp" #include "MTUtils.hpp" @@ -355,116 +356,6 @@ TriangleMesh Model::mesh() const return mesh; } -static bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Pointfs &out) -{ - if (sizes.empty()) - // return if the list is empty or the following call to BoundingBoxf constructor will lead to a crash - return true; - - // we supply unscaled data to arrange() - bool result = Slic3r::Geometry::arrange( - sizes.size(), // number of parts - BoundingBoxf(sizes).max, // width and height of a single cell - dist, // distance between cells - bb, // bounding box of the area to fill - out // output positions - ); - - if (!result && bb != nullptr) { - // Try to arrange again ignoring bb - result = Slic3r::Geometry::arrange( - sizes.size(), // number of parts - BoundingBoxf(sizes).max, // width and height of a single cell - dist, // distance between cells - nullptr, // bounding box of the area to fill - out // output positions - ); - } - - return result; -} - -/* arrange objects preserving their instance count - but altering their instance positions */ -bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) -{ - size_t count = 0; - for (auto obj : objects) count += obj->instances.size(); - - arrangement::ArrangePolygons input; - ModelInstancePtrs instances; - input.reserve(count); - instances.reserve(count); - for (ModelObject *mo : objects) - for (ModelInstance *minst : mo->instances) { - input.emplace_back(minst->get_arrange_polygon()); - instances.emplace_back(minst); - } - - arrangement::BedShapeHint bedhint; - coord_t bedwidth = 0; - - if (bb) { - bedwidth = scaled(bb->size().x()); - bedhint = arrangement::BedShapeHint( - BoundingBox(scaled(bb->min), scaled(bb->max))); - } - - arrangement::arrange(input, scaled(dist), bedhint); - - bool ret = true; - coord_t stride = bedwidth + bedwidth / 5; - - for(size_t i = 0; i < input.size(); ++i) { - if (input[i].bed_idx != 0) ret = false; - if (input[i].bed_idx >= 0) { - input[i].translation += Vec2crd{input[i].bed_idx * stride, 0}; - instances[i]->apply_arrange_result(input[i].translation.cast(), - input[i].rotation); - } - } - - return ret; -} - -// Duplicate the entire model preserving instance relative positions. -void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb) -{ - Pointfs model_sizes(copies_num-1, to_2d(this->bounding_box().size())); - Pointfs positions; - if (! _arrange(model_sizes, dist, bb, positions)) - throw std::invalid_argument("Cannot duplicate part as the resulting objects would not fit on the print bed.\n"); - - // note that this will leave the object count unaltered - - for (ModelObject *o : this->objects) { - // make a copy of the pointers in order to avoid recursion when appending their copies - ModelInstancePtrs instances = o->instances; - for (const ModelInstance *i : instances) { - for (const Vec2d &pos : positions) { - ModelInstance *instance = o->add_instance(*i); - instance->set_offset(instance->get_offset() + Vec3d(pos(0), pos(1), 0.0)); - } - } - o->invalidate_bounding_box(); - } -} - -/* this will append more instances to each object - and then automatically rearrange everything */ -void Model::duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb) -{ - for (ModelObject *o : this->objects) { - // make a copy of the pointers in order to avoid recursion when appending their copies - ModelInstancePtrs instances = o->instances; - for (const ModelInstance *i : instances) - for (size_t k = 2; k <= copies_num; ++ k) - o->add_instance(*i); - } - - this->arrange_objects(dist, bb); -} - void Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist) { if (this->objects.size() > 1) throw "Grid duplication is not supported with multiple objects"; @@ -1149,6 +1040,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b for (ModelVolume *volume : volumes) { const auto volume_matrix = volume->get_matrix(); + volume->m_supported_facets.clear(); + if (! volume->is_model_part()) { // Modifiers are not cut, but we still need to add the instance transformation // to the modifier volume transformation to preserve their shape properly. @@ -1848,6 +1741,41 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const return ret; } + +std::vector FacetsAnnotation::get_facets(FacetSupportType type) const +{ + std::vector out; + for (auto& [facet_idx, this_type] : m_data) + if (this_type == type) + out.push_back(facet_idx); + return out; +} + + + +void FacetsAnnotation::set_facet(int idx, FacetSupportType type) +{ + bool changed = true; + + if (type == FacetSupportType::NONE) + changed = m_data.erase(idx) != 0; + else + m_data[idx] = type; + + if (changed) + update_timestamp(); +} + + + +void FacetsAnnotation::clear() +{ + m_data.clear(); + update_timestamp(); +} + + + // Test whether the two models contain the same number of ModelObjects with the same set of IDs // ordered in the same order. In that case it is not necessary to kill the background processing. bool model_object_list_equal(const Model &model_old, const Model &model_new) @@ -1911,6 +1839,16 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO return false; } +bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject& mo_new) { + assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART)); + assert(mo.volumes.size() == mo_new.volumes.size()); + for (size_t i=0; im_supported_facets.is_same_as(mo.volumes[i]->m_supported_facets)) + return true; + } + return false; +}; + extern bool model_has_multi_part_objects(const Model &model) { for (const ModelObject *model_object : model.objects) @@ -1991,6 +1929,7 @@ void check_model_ids_equal(const Model &model1, const Model &model2) } } } + #endif /* NDEBUG */ } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 2ddad9e59e..cd2c4957da 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace cereal { class BinaryInputArchive; @@ -214,8 +215,8 @@ public: when user expects that. */ Vec3d origin_translation; - Model* get_model() { return m_model; }; - const Model* get_model() const { return m_model; }; + Model* get_model() { return m_model; } + const Model* get_model() const { return m_model; } ModelVolume* add_volume(const TriangleMesh &mesh); ModelVolume* add_volume(TriangleMesh &&mesh); @@ -391,6 +392,34 @@ enum class ModelVolumeType : int { SUPPORT_BLOCKER, }; +enum class FacetSupportType : int8_t { + NONE = 0, + ENFORCER = 1, + BLOCKER = 2 +}; + +class FacetsAnnotation { +public: + using ClockType = std::chrono::steady_clock; + + std::vector get_facets(FacetSupportType type) const; + void set_facet(int idx, FacetSupportType type); + void clear(); + + ClockType::time_point get_timestamp() const { return timestamp; } + bool is_same_as(const FacetsAnnotation& other) const { + return timestamp == other.get_timestamp(); + } + +private: + std::map m_data; + + ClockType::time_point timestamp; + void update_timestamp() { + timestamp = ClockType::now(); + } +}; + // An object STL, or a modifier volume, over which a different set of parameters shall be applied. // ModelVolume instances are owned by a ModelObject. class ModelVolume final : public ObjectBase @@ -421,8 +450,11 @@ public: // overriding the global Slic3r settings and the ModelObject settings. ModelConfig config; + // List of mesh facets to be supported/unsupported. + FacetsAnnotation m_supported_facets; + // A parent object owning this modifier volume. - ModelObject* get_object() const { return this->object; }; + ModelObject* get_object() const { return this->object; } ModelVolumeType type() const { return m_type; } void set_type(const ModelVolumeType t) { m_type = t; } bool is_model_part() const { return m_type == ModelVolumeType::MODEL_PART; } @@ -548,7 +580,9 @@ private: // Copying an existing volume, therefore this volume will get a copy of the ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other) : ObjectBase(other), - name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) + name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), + config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation), + m_supported_facets(other.m_supported_facets) { assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); assert(this->id() == other.id() && this->config.id() == other.config.id()); @@ -565,6 +599,8 @@ private: if (mesh.stl.stats.number_of_facets > 1) calculate_convex_hull(); assert(this->config.id().valid()); assert(this->config.id() != other.config.id()); assert(this->id() != this->config.id()); + + m_supported_facets.clear(); } ModelVolume& operator=(ModelVolume &rhs) = delete; @@ -802,11 +838,9 @@ public: bool center_instances_around_point(const Vec2d &point); void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); } TriangleMesh mesh() const; - bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL); + // Croaks if the duplicated objects do not fit the print bed. - void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); - void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); - void duplicate_objects_grid(size_t x, size_t y, coordf_t dist); + void duplicate_objects_grid(size_t x, size_t y, coordf_t dist); bool looks_like_multipart_object() const; void convert_multipart_object(unsigned int max_extruders); @@ -822,7 +856,7 @@ public: std::string propose_export_file_name_and_path(const std::string &new_extension) const; private: - explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); }; + explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); } void assign_new_unique_ids_recursive(); void update_links_bottom_up_recursive(); @@ -831,7 +865,7 @@ private: template void serialize(Archive &ar) { Internal::StaticSerializationWrapper wipe_tower_wrapper(wipe_tower); ar(materials, objects, wipe_tower_wrapper); - } + } }; #undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE @@ -849,6 +883,10 @@ extern bool model_object_list_extended(const Model &model_old, const Model &mode // than the old ModelObject. extern bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolumeType type); +// Test whether the now ModelObject has newer custom supports data than the old one. +// The function assumes that volumes list is synchronized. +extern bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject& mo_new); + // If the model has multi-part objects, then it is currently not supported by the SLA mode. // Either the model cannot be loaded, or a SLA printer has to be activated. extern bool model_has_multi_part_objects(const Model &model); diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp new file mode 100644 index 0000000000..85aa25a5fb --- /dev/null +++ b/src/libslic3r/ModelArrange.cpp @@ -0,0 +1,83 @@ +#include "ModelArrange.hpp" +#include "MTUtils.hpp" + +namespace Slic3r { + +arrangement::ArrangePolygons get_arrange_polys(const Model &model, ModelInstancePtrs &instances) +{ + size_t count = 0; + for (auto obj : model.objects) count += obj->instances.size(); + + ArrangePolygons input; + input.reserve(count); + instances.clear(); instances.reserve(count); + for (ModelObject *mo : model.objects) + for (ModelInstance *minst : mo->instances) { + input.emplace_back(minst->get_arrange_polygon()); + instances.emplace_back(minst); + } + + return input; +} + +bool apply_arrange_polys(ArrangePolygons &input, ModelInstancePtrs &instances, VirtualBedFn vfn) +{ + bool ret = true; + + for(size_t i = 0; i < input.size(); ++i) { + if (input[i].bed_idx != 0) { ret = false; if (vfn) vfn(input[i]); } + if (input[i].bed_idx >= 0) + instances[i]->apply_arrange_result(input[i].translation.cast(), + input[i].rotation); + } + + return ret; +} + +Slic3r::arrangement::ArrangePolygon get_arrange_poly(const Model &model) +{ + ArrangePolygon ap; + Points &apts = ap.poly.contour.points; + for (const ModelObject *mo : model.objects) + for (const ModelInstance *minst : mo->instances) { + ArrangePolygon obj_ap = minst->get_arrange_polygon(); + ap.poly.contour.rotate(obj_ap.rotation); + ap.poly.contour.translate(obj_ap.translation.x(), obj_ap.translation.y()); + const Points &pts = obj_ap.poly.contour.points; + std::copy(pts.begin(), pts.end(), std::back_inserter(apts)); + } + + apts = Geometry::convex_hull(apts); + return ap; +} + +void duplicate(Model &model, Slic3r::arrangement::ArrangePolygons &copies, VirtualBedFn vfn) +{ + for (ModelObject *o : model.objects) { + // make a copy of the pointers in order to avoid recursion when appending their copies + ModelInstancePtrs instances = o->instances; + o->instances.clear(); + for (const ModelInstance *i : instances) { + for (arrangement::ArrangePolygon &ap : copies) { + if (ap.bed_idx != 0) vfn(ap); + ModelInstance *instance = o->add_instance(*i); + Vec2d pos = unscale(ap.translation); + instance->set_offset(instance->get_offset() + to_3d(pos, 0.)); + } + } + o->invalidate_bounding_box(); + } +} + +void duplicate_objects(Model &model, size_t copies_num) +{ + for (ModelObject *o : model.objects) { + // make a copy of the pointers in order to avoid recursion when appending their copies + ModelInstancePtrs instances = o->instances; + for (const ModelInstance *i : instances) + for (size_t k = 2; k <= copies_num; ++ k) + o->add_instance(*i); + } +} + +} // namespace Slic3r diff --git a/src/libslic3r/ModelArrange.hpp b/src/libslic3r/ModelArrange.hpp new file mode 100644 index 0000000000..d65b0fd6de --- /dev/null +++ b/src/libslic3r/ModelArrange.hpp @@ -0,0 +1,68 @@ +#ifndef MODELARRANGE_HPP +#define MODELARRANGE_HPP + +#include +#include + +namespace Slic3r { + +using arrangement::ArrangePolygon; +using arrangement::ArrangePolygons; +using arrangement::ArrangeParams; +using arrangement::InfiniteBed; +using arrangement::CircleBed; + +// Do something with ArrangePolygons in virtual beds +using VirtualBedFn = std::function; + +[[noreturn]] inline void throw_if_out_of_bed(arrangement::ArrangePolygon&) +{ + throw std::runtime_error("Objects could not fit on the bed"); +} + +ArrangePolygons get_arrange_polys(const Model &model, ModelInstancePtrs &instances); +ArrangePolygon get_arrange_poly(const Model &model); +bool apply_arrange_polys(ArrangePolygons &polys, ModelInstancePtrs &instances, VirtualBedFn); + +void duplicate(Model &model, ArrangePolygons &copies, VirtualBedFn); +void duplicate_objects(Model &model, size_t copies_num); + +template +bool arrange_objects(Model & model, + const TBed & bed, + const ArrangeParams ¶ms, + VirtualBedFn vfn = throw_if_out_of_bed) +{ + ModelInstancePtrs instances; + auto&& input = get_arrange_polys(model, instances); + arrangement::arrange(input, bed, params); + + return apply_arrange_polys(input, instances, vfn); +} + +template +void duplicate(Model & model, + size_t copies_num, + const TBed & bed, + const ArrangeParams ¶ms, + VirtualBedFn vfn = throw_if_out_of_bed) +{ + ArrangePolygons copies(copies_num, get_arrange_poly(model)); + arrangement::arrange(copies, bed, params); + duplicate(model, copies, vfn); +} + +template +void duplicate_objects(Model & model, + size_t copies_num, + const TBed & bed, + const ArrangeParams ¶ms, + VirtualBedFn vfn = throw_if_out_of_bed) +{ + duplicate_objects(model, copies_num); + arrange_objects(model, bed, params, vfn); +} + +} + +#endif // MODELARRANGE_HPP diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index e095f1c757..e511a63167 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -114,6 +114,7 @@ public: Point& operator+=(const Point& rhs) { (*this)(0) += rhs(0); (*this)(1) += rhs(1); return *this; } Point& operator-=(const Point& rhs) { (*this)(0) -= rhs(0); (*this)(1) -= rhs(1); return *this; } Point& operator*=(const double &rhs) { (*this)(0) = coord_t((*this)(0) * rhs); (*this)(1) = coord_t((*this)(1) * rhs); return *this; } + Point operator*(const double &rhs) { return Point((*this)(0) * rhs, (*this)(1) * rhs); } void rotate(double angle); void rotate(double angle, const Point ¢er); @@ -288,6 +289,72 @@ private: std::ostream& operator<<(std::ostream &stm, const Vec2d &pointf); + +// ///////////////////////////////////////////////////////////////////////////// +// Type safe conversions to and from scaled and unscaled coordinates +// ///////////////////////////////////////////////////////////////////////////// + +// Semantics are the following: +// Upscaling (scaled()): only from floating point types (or Vec) to either +// floating point or integer 'scaled coord' coordinates. +// Downscaling (unscaled()): from arithmetic (or Vec) to floating point only + +// Conversion definition from unscaled to floating point scaled +template> +inline constexpr FloatingOnly scaled(const Tin &v) noexcept +{ + return Tout(v / Tin(SCALING_FACTOR)); +} + +// Conversion definition from unscaled to integer 'scaled coord'. +// TODO: is the rounding necessary? Here it is commented out to show that +// it can be different for integers but it does not have to be. Using +// std::round means loosing noexcept and constexpr modifiers +template> +inline constexpr ScaledCoordOnly scaled(const Tin &v) noexcept +{ + //return static_cast(std::round(v / SCALING_FACTOR)); + return Tout(v / Tin(SCALING_FACTOR)); +} + +// Conversion for Eigen vectors (N dimensional points) +template, + int...EigenArgs> +inline Eigen::Matrix, N, EigenArgs...> +scaled(const Eigen::Matrix &v) +{ + return (v / SCALING_FACTOR).template cast(); +} + +// Conversion from arithmetic scaled type to floating point unscaled +template, + class = FloatingOnly> +inline constexpr Tout unscaled(const Tin &v) noexcept +{ + return Tout(v * Tout(SCALING_FACTOR)); +} + +// Unscaling for Eigen vectors. Input base type can be arithmetic, output base +// type can only be floating point. +template, + class = FloatingOnly, + int...EigenArgs> +inline constexpr Eigen::Matrix +unscaled(const Eigen::Matrix &v) noexcept +{ + return v.template cast() * SCALING_FACTOR; +} + } // namespace Slic3r // start Boost diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index e1e2991444..48e63dab31 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -48,12 +48,12 @@ int64_t Polygon::area2x() const } */ -double Polygon::area() const +double Polygon::area(const Points &points) { size_t n = points.size(); if (n < 3) return 0.; - + double a = 0.; for (size_t i = 0, j = n - 1; i < n; ++i) { a += ((double)points[j](0) + (double)points[i](0)) * ((double)points[i](1) - (double)points[j](1)); @@ -62,6 +62,11 @@ double Polygon::area() const return 0.5 * a; } +double Polygon::area() const +{ + return Polygon::area(points); +} + bool Polygon::is_counter_clockwise() const { return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this)); diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 0f8457ebd6..c6678e2d83 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -22,6 +22,7 @@ public: const Point& operator[](Points::size_type idx) const { return this->points[idx]; } Polygon() {} + virtual ~Polygon() = default; explicit Polygon(const Points &points) : MultiPoint(points) {} Polygon(std::initializer_list points) : MultiPoint(points) {} Polygon(const Polygon &other) : MultiPoint(other.points) {} @@ -46,7 +47,8 @@ public: // Split a closed polygon into an open polyline, with the split point duplicated at both ends. Polyline split_at_first_point() const { return this->split_at_index(0); } Points equally_spaced_points(double distance) const { return this->split_at_first_point().equally_spaced_points(distance); } - + + static double area(const Points &pts); double area() const; bool is_counter_clockwise() const; bool is_clockwise() const; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 986b7fa09a..dad41a642e 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -404,6 +404,7 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, // Copy the ModelVolume data. mv_dst.name = mv_src.name; static_cast(mv_dst.config) = static_cast(mv_src.config); + mv_dst.m_supported_facets = mv_src.m_supported_facets; //FIXME what to do with the materials? // mv_dst.m_material_id = mv_src.m_material_id; ++ i_src; @@ -854,7 +855,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ } // Copy content of the ModelObject including its ID, do not change the parent. model_object.assign_copy(model_object_new); - } else if (support_blockers_differ || support_enforcers_differ) { + } else if (support_blockers_differ || support_enforcers_differ || model_custom_supports_data_changed(model_object, model_object_new)) { // First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list. this->call_cancel_callback(); update_apply_status(false); @@ -862,8 +863,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); for (auto it = range.first; it != range.second; ++ it) update_apply_status(it->print_object->invalidate_step(posSupportMaterial)); - // Copy just the support volumes. - model_volume_list_update_supports(model_object, model_object_new); + if (support_enforcers_differ || support_blockers_differ) { + // Copy just the support volumes. + model_volume_list_update_supports(model_object, model_object_new); + } } if (! model_parts_differ && ! modifiers_differ) { // Synchronize Object's config. @@ -881,7 +884,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ } } } - // Synchronize (just copy) the remaining data of ModelVolumes (name, config). + // Synchronize (just copy) the remaining data of ModelVolumes (name, config, custom supports data). //FIXME What to do with m_material_id? model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART); model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index d4d3a1ddc5..54ebceeb66 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -192,6 +192,11 @@ public: std::vector slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); } std::vector slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); } + // Helpers to project custom supports on slices + void project_and_append_custom_supports(FacetSupportType type, std::vector& expolys) const; + void project_and_append_custom_enforcers(std::vector& enforcers) const { project_and_append_custom_supports(FacetSupportType::ENFORCER, enforcers); } + void project_and_append_custom_blockers(std::vector& blockers) const { project_and_append_custom_supports(FacetSupportType::BLOCKER, blockers); } + private: // to be called from Print only. friend class Print; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index a9175ab0cb..a155dff017 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3109,6 +3109,42 @@ DynamicPrintConfig* DynamicPrintConfig::new_from_defaults_keys(const std::vector return out; } +double min_object_distance(const ConfigBase &cfg) +{ + double ret = 0.; + + if (printer_technology(cfg) == ptSLA) ret = 6.; + else { + auto ecr_opt = cfg.option("extruder_clearance_radius"); + auto dd_opt = cfg.option("duplicate_distance"); + auto co_opt = cfg.option("complete_objects"); + + if (!ecr_opt || !dd_opt || !co_opt) ret = 0.; + else { + // min object distance is max(duplicate_distance, clearance_radius) + ret = (co_opt->value && ecr_opt->value > dd_opt->value) ? + ecr_opt->value : dd_opt->value; + } + } + + return ret; +} + +PrinterTechnology printer_technology(const ConfigBase &cfg) +{ + const ConfigOptionEnum *opt = cfg.option>("printer_technology"); + + if (opt) return opt->value; + + const ConfigOptionBool *export_opt = cfg.option("export_sla"); + if (export_opt && export_opt->getBool()) return ptSLA; + + export_opt = cfg.option("export_gcode"); + if (export_opt && export_opt->getBool()) return ptFFF; + + return ptUnknown; +} + void DynamicPrintConfig::normalize() { if (this->has("extruder")) { @@ -3179,22 +3215,6 @@ std::string DynamicPrintConfig::validate() } } -double PrintConfig::min_object_distance() const -{ - return PrintConfig::min_object_distance(static_cast(this)); -} - -double PrintConfig::min_object_distance(const ConfigBase *config) -{ - double extruder_clearance_radius = config->option("extruder_clearance_radius")->getFloat(); - double duplicate_distance = config->option("duplicate_distance")->getFloat(); - - // min object distance is max(duplicate_distance, clearance_radius) - return (config->option("complete_objects")->getBool() && extruder_clearance_radius > duplicate_distance) - ? extruder_clearance_radius - : duplicate_distance; -} - //FIXME localize this function. std::string FullPrintConfig::validate() { @@ -3604,8 +3624,39 @@ void DynamicPrintAndCLIConfig::handle_legacy(t_config_option_key &opt_key, std:: } } +static Points to_points(const std::vector &dpts) +{ + Points pts; pts.reserve(dpts.size()); + for (auto &v : dpts) + pts.emplace_back( coord_t(scale_(v.x())), coord_t(scale_(v.y())) ); + return pts; } +Points get_bed_shape(const DynamicPrintConfig &config) +{ + const auto *bed_shape_opt = config.opt("bed_shape"); + if (!bed_shape_opt) { + + // Here, it is certain that the bed shape is missing, so an infinite one + // has to be used, but still, the center of bed can be queried + if (auto center_opt = config.opt("center")) + return { scaled(center_opt->value) }; + + return {}; + } + + return to_points(bed_shape_opt->values); +} + +Points get_bed_shape(const PrintConfig &cfg) +{ + return to_points(cfg.bed_shape.values); +} + +Points get_bed_shape(const SLAPrinterConfig &cfg) { return to_points(cfg.bed_shape.values); } + +} // namespace Slic3r + #include CEREAL_REGISTER_TYPE(Slic3r::DynamicPrintConfig) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::DynamicConfig, Slic3r::DynamicPrintConfig) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index b3dc3f1544..10ff8086cc 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -212,6 +212,9 @@ extern const PrintConfigDef print_config_def; class StaticPrintConfig; +PrinterTechnology printer_technology(const ConfigBase &cfg); +double min_object_distance(const ConfigBase &cfg); + // Slic3r dynamic configuration, used to override the configuration // per object, per modification volume or per printing material. // The dynamic configuration is also used to store user modifications of the print global parameters, @@ -778,8 +781,6 @@ class PrintConfig : public MachineEnvelopeConfig, public GCodeConfig STATIC_PRINT_CONFIG_CACHE_DERIVED(PrintConfig) PrintConfig() : MachineEnvelopeConfig(0), GCodeConfig(0) { initialize_cache(); *this = s_cache_PrintConfig.defaults(); } public: - double min_object_distance() const; - static double min_object_distance(const ConfigBase *config); ConfigOptionBool avoid_crossing_perimeters; ConfigOptionPoints bed_shape; @@ -1334,6 +1335,10 @@ private: static PrintAndCLIConfigDef s_def; }; +Points get_bed_shape(const DynamicPrintConfig &cfg); +Points get_bed_shape(const PrintConfig &cfg); +Points get_bed_shape(const SLAPrinterConfig &cfg); + } // namespace Slic3r // Serialization through the Cereal library diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5d8d6a7565..5174fd6e97 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2665,4 +2665,168 @@ void PrintObject::_generate_support_material() support_material.generate(*this); } + +void PrintObject::project_and_append_custom_supports( + FacetSupportType type, std::vector& expolys) const +{ + for (const ModelVolume* mv : this->model_object()->volumes) { + const std::vector custom_facets = mv->m_supported_facets.get_facets(type); + if (custom_facets.empty()) + continue; + + const TriangleMesh& mesh = mv->mesh(); + const Transform3f& tr1 = mv->get_matrix().cast(); + const Transform3f& tr2 = this->trafo().cast(); + const Transform3f tr = tr2 * tr1; + + + // The projection will be at most a pentagon. Let's minimize heap + // reallocations by saving in in the following struct. + // Points are used so that scaling can be done in parallel + // and they can be moved from to create an ExPolygon later. + struct LightPolygon { + LightPolygon() { pts.reserve(5); } + Points pts; + + void add(const Vec2f& pt) { + pts.emplace_back(scale_(pt.x()), scale_(pt.y())); + assert(pts.size() <= 5); + } + }; + + // Structure to collect projected polygons. One element for each triangle. + // Saves vector of polygons and layer_id of the first one. + struct TriangleProjections { + size_t first_layer_id; + std::vector polygons; + }; + + // Vector to collect resulting projections from each triangle. + std::vector projections_of_triangles(custom_facets.size()); + + // Iterate over all triangles. + tbb::parallel_for( + tbb::blocked_range(0, custom_facets.size()), + [&](const tbb::blocked_range& range) { + for (size_t idx = range.begin(); idx < range.end(); ++ idx) { + + std::array facet; + + // Transform the triangle into worlds coords. + for (int i=0; i<3; ++i) + facet[i] = tr * mesh.its.vertices[mesh.its.indices[custom_facets[idx]](i)]; + + // Ignore triangles with upward-pointing normal. + if ((facet[1]-facet[0]).cross(facet[2]-facet[0]).z() > 0.) + continue; + + // Sort the three vertices according to z-coordinate. + std::sort(facet.begin(), facet.end(), + [](const Vec3f& pt1, const Vec3f&pt2) { + return pt1.z() < pt2.z(); + }); + + std::array trianglef; + for (int i=0; i<3; ++i) { + trianglef[i] = Vec2f(facet[i].x(), facet[i].y()); + trianglef[i] += Vec2f(unscale(this->center_offset().x()), + unscale(this->center_offset().y())); + } + + // Find lowest slice not below the triangle. + auto it = std::lower_bound(layers().begin(), layers().end(), facet[0].z()+EPSILON, + [](const Layer* l1, float z) { + return l1->slice_z < z; + }); + + // Count how many projections will be generated for this triangle + // and allocate respective amount in projections_of_triangles. + projections_of_triangles[idx].first_layer_id = it-layers().begin(); + size_t last_layer_id = projections_of_triangles[idx].first_layer_id; + // The cast in the condition below is important. The comparison must + // be an exact opposite of the one lower in the code where + // the polygons are appended. And that one is on floats. + while (last_layer_id + 1 < layers().size() + && float(layers()[last_layer_id]->slice_z) <= facet[2].z()) + ++last_layer_id; + projections_of_triangles[idx].polygons.resize( + last_layer_id - projections_of_triangles[idx].first_layer_id + 1); + + // Calculate how to move points on triangle sides per unit z increment. + Vec2f ta(trianglef[1] - trianglef[0]); + Vec2f tb(trianglef[2] - trianglef[0]); + ta *= 1./(facet[1].z() - facet[0].z()); + tb *= 1./(facet[2].z() - facet[0].z()); + + // Projection on current slice will be build directly in place. + LightPolygon* proj = &projections_of_triangles[idx].polygons[0]; + proj->add(trianglef[0]); + + bool passed_first = false; + bool stop = false; + + // Project a sub-polygon on all slices intersecting the triangle. + while (it != layers().end()) { + const float z = (*it)->slice_z; + + // Projections of triangle sides intersections with slices. + // a moves along one side, b tracks the other. + Vec2f a; + Vec2f b; + + // If the middle vertex was already passed, append the vertex + // and use ta for tracking the remaining side. + if (z > facet[1].z() && ! passed_first) { + proj->add(trianglef[1]); + ta = trianglef[2]-trianglef[1]; + ta *= 1./(facet[2].z() - facet[1].z()); + passed_first = true; + } + + // This slice is above the triangle already. + if (z > facet[2].z() || it+1 == layers().end()) { + proj->add(trianglef[2]); + stop = true; + } + else { + // Move a, b along the side it currently tracks to get + // projected intersection with current slice. + a = passed_first ? (trianglef[1]+ta*(z-facet[1].z())) + : (trianglef[0]+ta*(z-facet[0].z())); + b = trianglef[0]+tb*(z-facet[0].z()); + proj->add(a); + proj->add(b); + } + + if (stop) + break; + + // Advance to the next layer. + ++it; + ++proj; + assert(proj <= &projections_of_triangles[idx].polygons.back() ); + + // a, b are first two points of the polygon for the next layer. + proj->add(b); + proj->add(a); + } + } + }); // end of parallel_for + + // Make sure that the output vector can be used. + expolys.resize(layers().size()); + + // Now append the collected polygons to respective layers. + for (auto& trg : projections_of_triangles) { + int layer_id = trg.first_layer_id; + + for (const LightPolygon& poly : trg.polygons) { + expolys[layer_id].emplace_back(std::move(poly.pts)); + ++layer_id; + } + } + + } // loop over ModelVolumes +} + } // namespace Slic3r diff --git a/src/libslic3r/SLA/AGGRaster.hpp b/src/libslic3r/SLA/AGGRaster.hpp new file mode 100644 index 0000000000..37baed9e88 --- /dev/null +++ b/src/libslic3r/SLA/AGGRaster.hpp @@ -0,0 +1,222 @@ +#ifndef AGGRASTER_HPP +#define AGGRASTER_HPP + +#include +#include "libslic3r/ExPolygon.hpp" +#include "libslic3r/MTUtils.hpp" +#include + +// For rasterizing +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Slic3r { + +inline const Polygon& contour(const ExPolygon& p) { return p.contour; } +inline const ClipperLib::Path& contour(const ClipperLib::Polygon& p) { return p.Contour; } + +inline const Polygons& holes(const ExPolygon& p) { return p.holes; } +inline const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.Holes; } + +namespace sla { + +template struct Colors { + static const Color White; + static const Color Black; +}; + +template const Color Colors::White = Color{255}; +template const Color Colors::Black = Color{0}; + +template*/> class Renderer, + class Rasterizer = agg::rasterizer_scanline_aa<>, + class Scanline = agg::scanline_p8> +class AGGRaster: public RasterBase { +public: + using TColor = typename PixelRenderer::color_type; + using TValue = typename TColor::value_type; + using TPixel = typename PixelRenderer::pixel_type; + using TRawBuffer = agg::rendering_buffer; + +protected: + + Resolution m_resolution; + PixelDim m_pxdim_scaled; // used for scaled coordinate polygons + + std::vector m_buf; + agg::rendering_buffer m_rbuf; + + PixelRenderer m_pixrenderer; + + agg::renderer_base m_raw_renderer; + Renderer> m_renderer; + + Trafo m_trafo; + Scanline m_scanlines; + Rasterizer m_rasterizer; + + void flipy(agg::path_storage &path) const + { + path.flip_y(0, double(m_resolution.height_px)); + } + + void flipx(agg::path_storage &path) const + { + path.flip_x(0, double(m_resolution.width_px)); + } + + double getPx(const Point &p) { return p(0) * m_pxdim_scaled.w_mm; } + double getPy(const Point &p) { return p(1) * m_pxdim_scaled.h_mm; } + agg::path_storage to_path(const Polygon &poly) { return to_path(poly.points); } + double getPx(const ClipperLib::IntPoint &p) { return p.X * m_pxdim_scaled.w_mm; } + double getPy(const ClipperLib::IntPoint& p) { return p.Y * m_pxdim_scaled.h_mm; } + + template agg::path_storage _to_path(const PointVec& v) + { + agg::path_storage path; + + auto it = v.begin(); + path.move_to(getPx(*it), getPy(*it)); + while(++it != v.end()) path.line_to(getPx(*it), getPy(*it)); + path.line_to(getPx(v.front()), getPy(v.front())); + + return path; + } + + template agg::path_storage _to_path_flpxy(const PointVec& v) + { + agg::path_storage path; + + auto it = v.begin(); + path.move_to(getPy(*it), getPx(*it)); + while(++it != v.end()) path.line_to(getPy(*it), getPx(*it)); + path.line_to(getPy(v.front()), getPx(v.front())); + + return path; + } + + template agg::path_storage to_path(const PointVec &v) + { + auto path = m_trafo.flipXY ? _to_path_flpxy(v) : _to_path(v); + + path.translate_all_paths(m_trafo.center_x * m_pxdim_scaled.w_mm, + m_trafo.center_y * m_pxdim_scaled.h_mm); + + if(m_trafo.mirror_x) flipx(path); + if(m_trafo.mirror_y) flipy(path); + + return path; + } + + template void _draw(const P &poly) + { + m_rasterizer.reset(); + + m_rasterizer.add_path(to_path(contour(poly))); + for(auto& h : holes(poly)) m_rasterizer.add_path(to_path(h)); + + agg::render_scanlines(m_rasterizer, m_scanlines, m_renderer); + } + +public: + template AGGRaster(const Resolution &res, + const PixelDim & pd, + const Trafo & trafo, + const TColor & foreground, + const TColor & background, + GammaFn && gammafn) + : m_resolution(res) + , m_pxdim_scaled(SCALING_FACTOR / pd.w_mm, SCALING_FACTOR / pd.h_mm) + , m_buf(res.pixels()) + , m_rbuf(reinterpret_cast(m_buf.data()), + unsigned(res.width_px), + unsigned(res.height_px), + int(res.width_px *PixelRenderer::num_components)) + , m_pixrenderer(m_rbuf) + , m_raw_renderer(m_pixrenderer) + , m_renderer(m_raw_renderer) + , m_trafo(trafo) + { + m_renderer.color(foreground); + clear(background); + + m_rasterizer.gamma(gammafn); + } + + Trafo trafo() const override { return m_trafo; } + Resolution resolution() const override { return m_resolution; } + PixelDim pixel_dimensions() const override + { + return {SCALING_FACTOR / m_pxdim_scaled.w_mm, + SCALING_FACTOR / m_pxdim_scaled.h_mm}; + } + + void draw(const ExPolygon &poly) override { _draw(poly); } + void draw(const ClipperLib::Polygon &poly) override { _draw(poly); } + + EncodedRaster encode(RasterEncoder encoder) const override + { + return encoder(m_buf.data(), m_resolution.width_px, m_resolution.height_px, 1); + } + + void clear(const TColor color) { m_raw_renderer.clear(color); } +}; + +/* + * Captures an anti-aliased monochrome canvas where vectorial + * polygons can be rasterized. Fill color is always white and the background is + * black. Contours are anti-aliased. + * + * A gamma function can be specified at compile time to make it more flexible. + */ +using _RasterGrayscaleAA = + AGGRaster; + +class RasterGrayscaleAA : public _RasterGrayscaleAA { + using Base = _RasterGrayscaleAA; + using typename Base::TColor; + using typename Base::TValue; +public: + template + RasterGrayscaleAA(const RasterBase::Resolution &res, + const RasterBase::PixelDim & pd, + const RasterBase::Trafo & trafo, + GammaFn && fn) + : Base(res, pd, trafo, Colors::White, Colors::Black, + std::forward(fn)) + {} + + uint8_t read_pixel(size_t col, size_t row) const + { + static_assert(std::is_same::value, "Not grayscale pix"); + + uint8_t px; + Base::m_buf[row * Base::resolution().width_px + col].get(px); + return px; + } + + void clear() { Base::clear(Colors::Black); } +}; + +class RasterGrayscaleAAGammaPower: public RasterGrayscaleAA { +public: + RasterGrayscaleAAGammaPower(const RasterBase::Resolution &res, + const RasterBase::PixelDim & pd, + const RasterBase::Trafo & trafo, + double gamma = 1.) + : RasterGrayscaleAA(res, pd, trafo, agg::gamma_power(gamma)) + {} +}; + +}} // namespace Slic3r::sla + +#endif // AGGRASTER_HPP diff --git a/src/libslic3r/SLA/Common.cpp b/src/libslic3r/SLA/Common.cpp index 9945ed7faf..b57039ad13 100644 --- a/src/libslic3r/SLA/Common.cpp +++ b/src/libslic3r/SLA/Common.cpp @@ -303,8 +303,10 @@ EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const ret.m_t = double(hit.t); ret.m_dir = dir; ret.m_source = s; - if(!std::isinf(hit.t) && !std::isnan(hit.t)) + if(!std::isinf(hit.t) && !std::isnan(hit.t)) { ret.m_normal = this->normal_by_face_id(hit.id); + ret.m_face_id = hit.id; + } return ret; } @@ -340,8 +342,10 @@ EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const outs.back().m_t = double(hit.t); outs.back().m_dir = dir; outs.back().m_source = s; - if(!std::isinf(hit.t) && !std::isnan(hit.t)) + if(!std::isinf(hit.t) && !std::isnan(hit.t)) { outs.back().m_normal = this->normal_by_face_id(hit.id); + outs.back().m_face_id = hit.id; + } } return outs; diff --git a/src/libslic3r/SLA/EigenMesh3D.hpp b/src/libslic3r/SLA/EigenMesh3D.hpp index bcf94ec160..014a57e820 100644 --- a/src/libslic3r/SLA/EigenMesh3D.hpp +++ b/src/libslic3r/SLA/EigenMesh3D.hpp @@ -55,6 +55,7 @@ public: class hit_result { // m_t holds a distance from m_source to the intersection. double m_t = infty(); + int m_face_id = -1; const EigenMesh3D *m_mesh = nullptr; Vec3d m_dir; Vec3d m_source; @@ -74,6 +75,7 @@ public: inline const Vec3d& direction() const { return m_dir; } inline const Vec3d& source() const { return m_source; } inline Vec3d position() const { return m_source + m_dir * m_t; } + inline int face() const { return m_face_id; } inline bool is_valid() const { return m_mesh != nullptr; } inline bool is_hit() const { return !std::isinf(m_t); } diff --git a/src/libslic3r/SLA/Pad.cpp b/src/libslic3r/SLA/Pad.cpp index cf17867583..d933ef5ed7 100644 --- a/src/libslic3r/SLA/Pad.cpp +++ b/src/libslic3r/SLA/Pad.cpp @@ -11,6 +11,8 @@ #include "Tesselate.hpp" #include "MTUtils.hpp" +#include "TriangulateWall.hpp" + // For debugging: // #include // #include @@ -27,186 +29,27 @@ namespace Slic3r { namespace sla { namespace { -/// This function will return a triangulation of a sheet connecting an upper -/// and a lower plate given as input polygons. It will not triangulate the -/// plates themselves only the sheet. The caller has to specify the lower and -/// upper z levels in world coordinates as well as the offset difference -/// between the sheets. If the lower_z_mm is higher than upper_z_mm or the -/// offset difference is negative, the resulting triangle orientation will be -/// reversed. -/// -/// IMPORTANT: This is not a universal triangulation algorithm. It assumes -/// that the lower and upper polygons are offsetted versions of the same -/// original polygon. In general, it assumes that one of the polygons is -/// completely inside the other. The offset difference is the reference -/// distance from the inner polygon's perimeter to the outer polygon's -/// perimeter. The real distance will be variable as the clipper offset has -/// different strategies (rounding, etc...). This algorithm should have -/// O(2n + 3m) complexity where n is the number of upper vertices and m is the -/// number of lower vertices. Contour3D walls( const Polygon &lower, const Polygon &upper, double lower_z_mm, - double upper_z_mm, - double offset_difference_mm, - ThrowOnCancel thr = [] {}) + double upper_z_mm) { + Wall w = triangulate_wall(lower, upper, lower_z_mm, upper_z_mm); + Contour3D ret; - - if(upper.points.size() < 3 || lower.size() < 3) return ret; - - // The concept of the algorithm is relatively simple. It will try to find - // the closest vertices from the upper and the lower polygon and use those - // as starting points. Then it will create the triangles sequentially using - // an edge from the upper polygon and a vertex from the lower or vice versa, - // depending on the resulting triangle's quality. - // The quality is measured by a scalar value. So far it looks like it is - // enough to derive it from the slope of the triangle's two edges connecting - // the upper and the lower part. A reference slope is calculated from the - // height and the offset difference. - - // Offset in the index array for the ceiling - const auto offs = upper.points.size(); - - // Shorthand for the vertex arrays - auto& upts = upper.points, &lpts = lower.points; - auto& rpts = ret.points; auto& ind = ret.faces3; - - // If the Z levels are flipped, or the offset difference is negative, we - // will interpret that as the triangles normals should be inverted. - bool inverted = upper_z_mm < lower_z_mm || offset_difference_mm < 0; - - // Copy the points into the mesh, convert them from 2D to 3D - rpts.reserve(upts.size() + lpts.size()); - ind.reserve(2 * upts.size() + 2 * lpts.size()); - for (auto &p : upts) - rpts.emplace_back(unscaled(p.x()), unscaled(p.y()), upper_z_mm); - for (auto &p : lpts) - rpts.emplace_back(unscaled(p.x()), unscaled(p.y()), lower_z_mm); - - // Create pointing indices into vertex arrays. u-upper, l-lower - size_t uidx = 0, lidx = offs, unextidx = 1, lnextidx = offs + 1; - - // Simple squared distance calculation. - auto distfn = [](const Vec3d& p1, const Vec3d& p2) { - auto p = p1 - p2; return p.transpose() * p; - }; - - // We need to find the closest point on lower polygon to the first point on - // the upper polygon. These will be our starting points. - double distmin = std::numeric_limits::max(); - for(size_t l = lidx; l < rpts.size(); ++l) { - thr(); - double d = distfn(rpts[l], rpts[uidx]); - if(d < distmin) { lidx = l; distmin = d; } - } - - // Set up lnextidx to be ahead of lidx in cyclic mode - lnextidx = lidx + 1; - if(lnextidx == rpts.size()) lnextidx = offs; - - // This will be the flip switch to toggle between upper and lower triangle - // creation mode - enum class Proceed { - UPPER, // A segment from the upper polygon and one vertex from the lower - LOWER // A segment from the lower polygon and one vertex from the upper - } proceed = Proceed::UPPER; - - // Flags to help evaluating loop termination. - bool ustarted = false, lstarted = false; - - // The variables for the fitness values, one for the actual and one for the - // previous. - double current_fit = 0, prev_fit = 0; - - // Every triangle of the wall has two edges connecting the upper plate with - // the lower plate. From the length of these two edges and the zdiff we - // can calculate the momentary squared offset distance at a particular - // position on the wall. The average of the differences from the reference - // (squared) offset distance will give us the driving fitness value. - const double offsdiff2 = std::pow(offset_difference_mm, 2); - const double zdiff2 = std::pow(upper_z_mm - lower_z_mm, 2); - - // Mark the current vertex iterator positions. If the iterators return to - // the same position, the loop can be terminated. - size_t uendidx = uidx, lendidx = lidx; - - do { thr(); // check throw if canceled - - prev_fit = current_fit; - - switch(proceed) { // proceed depending on the current state - case Proceed::UPPER: - if(!ustarted || uidx != uendidx) { // there are vertices remaining - // Get the 3D vertices in order - const Vec3d& p_up1 = rpts[uidx]; - const Vec3d& p_low = rpts[lidx]; - const Vec3d& p_up2 = rpts[unextidx]; - - // Calculate fitness: the average of the two connecting edges - double a = offsdiff2 - (distfn(p_up1, p_low) - zdiff2); - double b = offsdiff2 - (distfn(p_up2, p_low) - zdiff2); - current_fit = (std::abs(a) + std::abs(b)) / 2; - - if(current_fit > prev_fit) { // fit is worse than previously - proceed = Proceed::LOWER; - } else { // good to go, create the triangle - inverted - ? ind.emplace_back(int(unextidx), int(lidx), int(uidx)) - : ind.emplace_back(int(uidx), int(lidx), int(unextidx)); - - // Increment the iterators, rotate if necessary - ++uidx; ++unextidx; - if(unextidx == offs) unextidx = 0; - if(uidx == offs) uidx = 0; - - ustarted = true; // mark the movement of the iterators - // so that the comparison to uendidx can be made correctly - } - } else proceed = Proceed::LOWER; - - break; - case Proceed::LOWER: - // Mode with lower segment, upper vertex. Same structure: - if(!lstarted || lidx != lendidx) { - const Vec3d& p_low1 = rpts[lidx]; - const Vec3d& p_low2 = rpts[lnextidx]; - const Vec3d& p_up = rpts[uidx]; - - double a = offsdiff2 - (distfn(p_up, p_low1) - zdiff2); - double b = offsdiff2 - (distfn(p_up, p_low2) - zdiff2); - current_fit = (std::abs(a) + std::abs(b)) / 2; - - if(current_fit > prev_fit) { - proceed = Proceed::UPPER; - } else { - inverted - ? ind.emplace_back(int(uidx), int(lnextidx), int(lidx)) - : ind.emplace_back(int(lidx), int(lnextidx), int(uidx)); - - ++lidx; ++lnextidx; - if(lnextidx == rpts.size()) lnextidx = offs; - if(lidx == rpts.size()) lidx = offs; - - lstarted = true; - } - } else proceed = Proceed::UPPER; - - break; - } // end of switch - } while(!ustarted || !lstarted || uidx != uendidx || lidx != lendidx); - + ret.points = std::move(w.first); + ret.faces3 = std::move(w.second); + return ret; } // Same as walls() but with identical higher and lower polygons. Contour3D inline straight_walls(const Polygon &plate, double lo_z, - double hi_z, - ThrowOnCancel thr) + double hi_z) { - return walls(plate, plate, lo_z, hi_z, .0 /*offset_diff*/, thr); + return walls(plate, plate, lo_z, hi_z); } // Function to cut tiny connector cavities for a given polygon. The input poly @@ -534,10 +377,8 @@ bool add_cavity(Contour3D &pad, ExPolygon &top_poly, const PadConfig3D &cfg, top_poly = pdiff.front(); double z_min = -cfg.wing_height, z_max = 0; - double offset_difference = -wing_distance; - pad.merge(walls(inner_base.contour, middle_base.contour, z_min, z_max, - offset_difference, thr)); - + pad.merge(walls(inner_base.contour, middle_base.contour, z_min, z_max)); + thr(); pad.merge(triangulate_expolygon_3d(inner_base, z_min, NORMALS_UP)); return true; @@ -555,17 +396,17 @@ Contour3D create_outer_pad_geometry(const ExPolygons & skeleton, offset_contour_only(pad_part, -scaled(cfg.bottom_offset())); if (bottom_poly.empty()) continue; - + thr(); + double z_min = -cfg.height, z_max = 0; - ret.merge(walls(top_poly.contour, bottom_poly.contour, z_max, z_min, - cfg.bottom_offset(), thr)); + ret.merge(walls(top_poly.contour, bottom_poly.contour, z_max, z_min)); if (cfg.wing_height > 0. && add_cavity(ret, top_poly, cfg, thr)) z_max = -cfg.wing_height; for (auto &h : bottom_poly.holes) - ret.merge(straight_walls(h, z_max, z_min, thr)); - + ret.merge(straight_walls(h, z_max, z_min)); + ret.merge(triangulate_expolygon_3d(bottom_poly, z_min, NORMALS_DOWN)); ret.merge(triangulate_expolygon_3d(top_poly, NORMALS_UP)); } @@ -581,11 +422,12 @@ Contour3D create_inner_pad_geometry(const ExPolygons & skeleton, double z_max = 0., z_min = -cfg.height; for (const ExPolygon &pad_part : skeleton) { - ret.merge(straight_walls(pad_part.contour, z_max, z_min,thr)); + thr(); + ret.merge(straight_walls(pad_part.contour, z_max, z_min)); for (auto &h : pad_part.holes) - ret.merge(straight_walls(h, z_max, z_min, thr)); - + ret.merge(straight_walls(h, z_max, z_min)); + ret.merge(triangulate_expolygon_3d(pad_part, z_min, NORMALS_DOWN)); ret.merge(triangulate_expolygon_3d(pad_part, z_max, NORMALS_UP)); } diff --git a/src/libslic3r/SLA/Raster.cpp b/src/libslic3r/SLA/Raster.cpp deleted file mode 100644 index 9b7f1db7d9..0000000000 --- a/src/libslic3r/SLA/Raster.cpp +++ /dev/null @@ -1,320 +0,0 @@ -#ifndef SLARASTER_CPP -#define SLARASTER_CPP - -#include - -#include -#include "libslic3r/ExPolygon.hpp" -#include "libslic3r/MTUtils.hpp" -#include - -// For rasterizing -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -// Experimental minz image write: -#include - -namespace Slic3r { - -inline const Polygon& contour(const ExPolygon& p) { return p.contour; } -inline const ClipperLib::Path& contour(const ClipperLib::Polygon& p) { return p.Contour; } - -inline const Polygons& holes(const ExPolygon& p) { return p.holes; } -inline const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.Holes; } - -namespace sla { - -const Raster::TMirroring Raster::NoMirror = {false, false}; -const Raster::TMirroring Raster::MirrorX = {true, false}; -const Raster::TMirroring Raster::MirrorY = {false, true}; -const Raster::TMirroring Raster::MirrorXY = {true, true}; - - -using TPixelRenderer = agg::pixfmt_gray8; // agg::pixfmt_rgb24; -using TRawRenderer = agg::renderer_base; -using TPixel = TPixelRenderer::color_type; -using TRawBuffer = agg::rendering_buffer; -using TBuffer = std::vector; - -using TRendererAA = agg::renderer_scanline_aa_solid; - -class Raster::Impl { -public: - - static const TPixel ColorWhite; - static const TPixel ColorBlack; - - using Format = Raster::RawData; - -private: - Raster::Resolution m_resolution; - Raster::PixelDim m_pxdim_scaled; // used for scaled coordinate polygons - TBuffer m_buf; - TRawBuffer m_rbuf; - TPixelRenderer m_pixfmt; - TRawRenderer m_raw_renderer; - TRendererAA m_renderer; - - std::function m_gammafn; - Trafo m_trafo; - - inline void flipy(agg::path_storage& path) const { - path.flip_y(0, double(m_resolution.height_px)); - } - - inline void flipx(agg::path_storage& path) const { - path.flip_x(0, double(m_resolution.width_px)); - } - -public: - inline Impl(const Raster::Resolution & res, - const Raster::PixelDim & pd, - const Trafo &trafo) - : m_resolution(res) - , m_pxdim_scaled(SCALING_FACTOR / pd.w_mm, SCALING_FACTOR / pd.h_mm) - , m_buf(res.pixels()) - , m_rbuf(reinterpret_cast(m_buf.data()), - unsigned(res.width_px), - unsigned(res.height_px), - int(res.width_px * TPixelRenderer::num_components)) - , m_pixfmt(m_rbuf) - , m_raw_renderer(m_pixfmt) - , m_renderer(m_raw_renderer) - , m_trafo(trafo) - { - m_renderer.color(ColorWhite); - - if (trafo.gamma > 0) m_gammafn = agg::gamma_power(trafo.gamma); - else m_gammafn = agg::gamma_threshold(0.5); - - clear(); - } - - template void draw(const P &poly) { - agg::rasterizer_scanline_aa<> ras; - agg::scanline_p8 scanlines; - - ras.gamma(m_gammafn); - - ras.add_path(to_path(contour(poly))); - for(auto& h : holes(poly)) ras.add_path(to_path(h)); - - agg::render_scanlines(ras, scanlines, m_renderer); - } - - inline void clear() { - m_raw_renderer.clear(ColorBlack); - } - - inline TBuffer& buffer() { return m_buf; } - inline const TBuffer& buffer() const { return m_buf; } - - - inline const Raster::Resolution resolution() { return m_resolution; } - inline const Raster::PixelDim pixdim() - { - return {SCALING_FACTOR / m_pxdim_scaled.w_mm, - SCALING_FACTOR / m_pxdim_scaled.h_mm}; - } - -private: - inline double getPx(const Point& p) { - return p(0) * m_pxdim_scaled.w_mm; - } - - inline double getPy(const Point& p) { - return p(1) * m_pxdim_scaled.h_mm; - } - - inline agg::path_storage to_path(const Polygon& poly) - { - return to_path(poly.points); - } - - inline double getPx(const ClipperLib::IntPoint& p) { - return p.X * m_pxdim_scaled.w_mm; - } - - inline double getPy(const ClipperLib::IntPoint& p) { - return p.Y * m_pxdim_scaled.h_mm; - } - - template agg::path_storage _to_path(const PointVec& v) - { - agg::path_storage path; - - auto it = v.begin(); - path.move_to(getPx(*it), getPy(*it)); - while(++it != v.end()) path.line_to(getPx(*it), getPy(*it)); - path.line_to(getPx(v.front()), getPy(v.front())); - - return path; - } - - template agg::path_storage _to_path_flpxy(const PointVec& v) - { - agg::path_storage path; - - auto it = v.begin(); - path.move_to(getPy(*it), getPx(*it)); - while(++it != v.end()) path.line_to(getPy(*it), getPx(*it)); - path.line_to(getPy(v.front()), getPx(v.front())); - - return path; - } - - template agg::path_storage to_path(const PointVec &v) - { - auto path = m_trafo.flipXY ? _to_path_flpxy(v) : _to_path(v); - - path.translate_all_paths(m_trafo.origin_x * m_pxdim_scaled.w_mm, - m_trafo.origin_y * m_pxdim_scaled.h_mm); - - if(m_trafo.mirror_x) flipx(path); - if(m_trafo.mirror_y) flipy(path); - - return path; - } - -}; - -const TPixel Raster::Impl::ColorWhite = TPixel(255); -const TPixel Raster::Impl::ColorBlack = TPixel(0); - -Raster::Raster() { reset(); } - -Raster::Raster(const Raster::Resolution &r, - const Raster::PixelDim & pd, - const Raster::Trafo & tr) -{ - reset(r, pd, tr); -} - -Raster::~Raster() = default; - -Raster::Raster(Raster &&m) = default; -Raster &Raster::operator=(Raster &&) = default; - -void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, - const Trafo &trafo) -{ - m_impl.reset(); - m_impl.reset(new Impl(r, pd, trafo)); -} - -void Raster::reset() -{ - m_impl.reset(); -} - -Raster::Resolution Raster::resolution() const -{ - if (m_impl) return m_impl->resolution(); - - return Resolution{0, 0}; -} - -Raster::PixelDim Raster::pixel_dimensions() const -{ - if (m_impl) return m_impl->pixdim(); - - return PixelDim{0., 0.}; -} - -void Raster::clear() -{ - assert(m_impl); - m_impl->clear(); -} - -void Raster::draw(const ExPolygon &expoly) -{ - assert(m_impl); - m_impl->draw(expoly); -} - -void Raster::draw(const ClipperLib::Polygon &poly) -{ - assert(m_impl); - m_impl->draw(poly); -} - -uint8_t Raster::read_pixel(size_t x, size_t y) const -{ - assert (m_impl); - TPixel::value_type px; - m_impl->buffer()[y * resolution().width_px + x].get(px); - return px; -} - -PNGImage & PNGImage::serialize(const Raster &raster) -{ - size_t s = 0; - m_buffer.clear(); - - void *rawdata = tdefl_write_image_to_png_file_in_memory( - get_internals(raster).buffer().data(), - int(raster.resolution().width_px), - int(raster.resolution().height_px), 1, &s); - - // On error, data() will return an empty vector. No other info can be - // retrieved from miniz anyway... - if (rawdata == nullptr) return *this; - - auto ptr = static_cast(rawdata); - - m_buffer.reserve(s); - std::copy(ptr, ptr + s, std::back_inserter(m_buffer)); - - MZ_FREE(rawdata); - return *this; -} - -std::ostream &operator<<(std::ostream &stream, const Raster::RawData &bytes) -{ - stream.write(reinterpret_cast(bytes.data()), - std::streamsize(bytes.size())); - - return stream; -} - -Raster::RawData::~RawData() = default; - -PPMImage & PPMImage::serialize(const Raster &raster) -{ - auto header = std::string("P5 ") + - std::to_string(raster.resolution().width_px) + " " + - std::to_string(raster.resolution().height_px) + " " + "255 "; - - const auto &impl = get_internals(raster); - auto sz = impl.buffer().size() * sizeof(TBuffer::value_type); - size_t s = sz + header.size(); - - m_buffer.clear(); - m_buffer.reserve(s); - - auto buff = reinterpret_cast(impl.buffer().data()); - std::copy(header.begin(), header.end(), std::back_inserter(m_buffer)); - std::copy(buff, buff+sz, std::back_inserter(m_buffer)); - - return *this; -} - -const Raster::Impl &Raster::RawData::get_internals(const Raster &raster) -{ - return *raster.m_impl; -} - -} // namespace sla -} // namespace Slic3r - -#endif // SLARASTER_CPP diff --git a/src/libslic3r/SLA/Raster.hpp b/src/libslic3r/SLA/Raster.hpp deleted file mode 100644 index 4d76b32909..0000000000 --- a/src/libslic3r/SLA/Raster.hpp +++ /dev/null @@ -1,157 +0,0 @@ -#ifndef SLA_RASTER_HPP -#define SLA_RASTER_HPP - -#include -#include -#include -#include -#include -#include - -#include - -namespace ClipperLib { struct Polygon; } - -namespace Slic3r { -namespace sla { - -/** - * @brief Raster captures an anti-aliased monochrome canvas where vectorial - * polygons can be rasterized. Fill color is always white and the background is - * black. Contours are anti-aliased. - * - * It also supports saving the raster data into a standard output stream in raw - * or PNG format. - */ -class Raster { - class Impl; - std::unique_ptr m_impl; -public: - - // Raw byte buffer paired with its size. Suitable for compressed image data. - class RawData - { - protected: - std::vector m_buffer; - const Impl& get_internals(const Raster& raster); - public: - RawData() = default; - RawData(std::vector&& data): m_buffer(std::move(data)) {} - virtual ~RawData(); - - RawData(const RawData &) = delete; - RawData &operator=(const RawData &) = delete; - - RawData(RawData &&) = default; - RawData &operator=(RawData &&) = default; - - size_t size() const { return m_buffer.size(); } - const uint8_t * data() const { return m_buffer.data(); } - - virtual RawData& serialize(const Raster &/*raster*/) { return *this; } - virtual std::string get_file_extension() const = 0; - }; - - /// Type that represents a resolution in pixels. - struct Resolution { - size_t width_px; - size_t height_px; - - inline Resolution(size_t w = 0, size_t h = 0) - : width_px(w), height_px(h) - {} - - inline size_t pixels() const { return width_px * height_px; } - }; - - /// Types that represents the dimension of a pixel in millimeters. - struct PixelDim { - double w_mm; - double h_mm; - inline PixelDim(double px_width_mm = 0.0, double px_height_mm = 0.0): - w_mm(px_width_mm), h_mm(px_height_mm) {} - }; - - enum Orientation { roLandscape, roPortrait }; - - using TMirroring = std::array; - static const TMirroring NoMirror; - static const TMirroring MirrorX; - static const TMirroring MirrorY; - static const TMirroring MirrorXY; - - struct Trafo { - bool mirror_x = false, mirror_y = false, flipXY = false; - coord_t origin_x = 0, origin_y = 0; - - // If gamma is zero, thresholding will be performed which disables AA. - double gamma = 1.; - - // Portrait orientation will make sure the drawed polygons are rotated - // by 90 degrees. - Trafo(Orientation o = roLandscape, const TMirroring &mirror = NoMirror) - // XY flipping implicitly does an X mirror - : mirror_x(o == roPortrait ? !mirror[0] : mirror[0]) - , mirror_y(!mirror[1]) // Makes raster origin to be top left corner - , flipXY(o == roPortrait) - {} - }; - - Raster(); - Raster(const Resolution &r, - const PixelDim & pd, - const Trafo & tr = {}); - - Raster(const Raster& cpy) = delete; - Raster& operator=(const Raster& cpy) = delete; - Raster(Raster&& m); - Raster& operator=(Raster&&); - ~Raster(); - - /// Reallocated everything for the given resolution and pixel dimension. - void reset(const Resolution& r, - const PixelDim& pd, - const Trafo &tr = {}); - - /** - * Release the allocated resources. Drawing in this state ends in - * unspecified behavior. - */ - void reset(); - - /// Get the resolution of the raster. - Resolution resolution() const; - PixelDim pixel_dimensions() const; - - /// Clear the raster with black color. - void clear(); - - /// Draw a polygon with holes. - void draw(const ExPolygon& poly); - void draw(const ClipperLib::Polygon& poly); - - uint8_t read_pixel(size_t w, size_t h) const; - - inline bool empty() const { return ! bool(m_impl); } - -}; - -class PNGImage: public Raster::RawData { -public: - PNGImage& serialize(const Raster &raster) override; - std::string get_file_extension() const override { return "png"; } -}; - -class PPMImage: public Raster::RawData { -public: - PPMImage& serialize(const Raster &raster) override; - std::string get_file_extension() const override { return "ppm"; } -}; - -std::ostream& operator<<(std::ostream &stream, const Raster::RawData &bytes); - -} // sla -} // Slic3r - - -#endif // SLARASTER_HPP diff --git a/src/libslic3r/SLA/RasterBase.cpp b/src/libslic3r/SLA/RasterBase.cpp new file mode 100644 index 0000000000..581e84880b --- /dev/null +++ b/src/libslic3r/SLA/RasterBase.cpp @@ -0,0 +1,89 @@ +#ifndef SLARASTER_CPP +#define SLARASTER_CPP + +#include + +#include +#include + +// minz image write: +#include + +namespace Slic3r { namespace sla { + +const RasterBase::TMirroring RasterBase::NoMirror = {false, false}; +const RasterBase::TMirroring RasterBase::MirrorX = {true, false}; +const RasterBase::TMirroring RasterBase::MirrorY = {false, true}; +const RasterBase::TMirroring RasterBase::MirrorXY = {true, true}; + +EncodedRaster PNGRasterEncoder::operator()(const void *ptr, size_t w, size_t h, + size_t num_components) +{ + std::vector buf; + size_t s = 0; + + void *rawdata = tdefl_write_image_to_png_file_in_memory( + ptr, int(w), int(h), int(num_components), &s); + + // On error, data() will return an empty vector. No other info can be + // retrieved from miniz anyway... + if (rawdata == nullptr) return EncodedRaster({}, "png"); + + auto pptr = static_cast(rawdata); + + buf.reserve(s); + std::copy(pptr, pptr + s, std::back_inserter(buf)); + + MZ_FREE(rawdata); + return EncodedRaster(std::move(buf), "png"); +} + +std::ostream &operator<<(std::ostream &stream, const EncodedRaster &bytes) +{ + stream.write(reinterpret_cast(bytes.data()), + std::streamsize(bytes.size())); + + return stream; +} + +EncodedRaster PPMRasterEncoder::operator()(const void *ptr, size_t w, size_t h, + size_t num_components) +{ + std::vector buf; + + auto header = std::string("P5 ") + + std::to_string(w) + " " + + std::to_string(h) + " " + "255 "; + + auto sz = w * h * num_components; + size_t s = sz + header.size(); + + buf.reserve(s); + + auto buff = reinterpret_cast(ptr); + std::copy(header.begin(), header.end(), std::back_inserter(buf)); + std::copy(buff, buff+sz, std::back_inserter(buf)); + + return EncodedRaster(std::move(buf), "ppm"); +} + +std::unique_ptr create_raster_grayscale_aa( + const RasterBase::Resolution &res, + const RasterBase::PixelDim & pxdim, + double gamma, + const RasterBase::Trafo & tr) +{ + std::unique_ptr rst; + + if (gamma > 0) + rst = std::make_unique(res, pxdim, tr, gamma); + else + rst = std::make_unique(res, pxdim, tr, agg::gamma_threshold(.5)); + + return rst; +} + +} // namespace sla +} // namespace Slic3r + +#endif // SLARASTER_CPP diff --git a/src/libslic3r/SLA/RasterBase.hpp b/src/libslic3r/SLA/RasterBase.hpp new file mode 100644 index 0000000000..431c731a66 --- /dev/null +++ b/src/libslic3r/SLA/RasterBase.hpp @@ -0,0 +1,124 @@ +#ifndef SLA_RASTERBASE_HPP +#define SLA_RASTERBASE_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace ClipperLib { struct Polygon; } + +namespace Slic3r { + +template using uqptr = std::unique_ptr; +template using shptr = std::shared_ptr; +template using wkptr = std::weak_ptr; + +namespace sla { + +// Raw byte buffer paired with its size. Suitable for compressed image data. +class EncodedRaster { +protected: + std::vector m_buffer; + std::string m_ext; +public: + EncodedRaster() = default; + explicit EncodedRaster(std::vector &&buf, std::string ext) + : m_buffer(std::move(buf)), m_ext(std::move(ext)) + {} + + size_t size() const { return m_buffer.size(); } + const void * data() const { return m_buffer.data(); } + const char * extension() const { return m_ext.c_str(); } +}; + +using RasterEncoder = + std::function; + +class RasterBase { +public: + + enum Orientation { roLandscape, roPortrait }; + + using TMirroring = std::array; + static const TMirroring NoMirror; + static const TMirroring MirrorX; + static const TMirroring MirrorY; + static const TMirroring MirrorXY; + + struct Trafo { + bool mirror_x = false, mirror_y = false, flipXY = false; + coord_t center_x = 0, center_y = 0; + + // Portrait orientation will make sure the drawed polygons are rotated + // by 90 degrees. + Trafo(Orientation o = roLandscape, const TMirroring &mirror = NoMirror) + // XY flipping implicitly does an X mirror + : mirror_x(o == roPortrait ? !mirror[0] : mirror[0]) + , mirror_y(!mirror[1]) // Makes raster origin to be top left corner + , flipXY(o == roPortrait) + {} + + TMirroring get_mirror() const { return { (roPortrait ? !mirror_x : mirror_x), mirror_y}; } + Orientation get_orientation() const { return flipXY ? roPortrait : roLandscape; } + Point get_center() const { return {center_x, center_y}; } + }; + + /// Type that represents a resolution in pixels. + struct Resolution { + size_t width_px = 0; + size_t height_px = 0; + + Resolution(size_t w = 0, size_t h = 0) : width_px(w), height_px(h) {} + size_t pixels() const { return width_px * height_px; } + }; + + /// Types that represents the dimension of a pixel in millimeters. + struct PixelDim { + double w_mm = 0.; + double h_mm = 0.; + + PixelDim(double px_width_mm = 0.0, double px_height_mm = 0.0) + : w_mm(px_width_mm), h_mm(px_height_mm) + {} + }; + + virtual ~RasterBase() = default; + + /// Draw a polygon with holes. + virtual void draw(const ExPolygon& poly) = 0; + virtual void draw(const ClipperLib::Polygon& poly) = 0; + + /// Get the resolution of the raster. + virtual Resolution resolution() const = 0; + virtual PixelDim pixel_dimensions() const = 0; + virtual Trafo trafo() const = 0; + + virtual EncodedRaster encode(RasterEncoder encoder) const = 0; +}; + +struct PNGRasterEncoder { + EncodedRaster operator()(const void *ptr, size_t w, size_t h, size_t num_components); +}; + +struct PPMRasterEncoder { + EncodedRaster operator()(const void *ptr, size_t w, size_t h, size_t num_components); +}; + +std::ostream& operator<<(std::ostream &stream, const EncodedRaster &bytes); + +// If gamma is zero, thresholding will be performed which disables AA. +uqptr create_raster_grayscale_aa( + const RasterBase::Resolution &res, + const RasterBase::PixelDim & pxdim, + double gamma = 1.0, + const RasterBase::Trafo & tr = {}); + +}} // namespace Slic3r::sla + +#endif // SLARASTERBASE_HPP diff --git a/src/libslic3r/SLA/RasterToPolygons.cpp b/src/libslic3r/SLA/RasterToPolygons.cpp new file mode 100644 index 0000000000..cd84a3cb4a --- /dev/null +++ b/src/libslic3r/SLA/RasterToPolygons.cpp @@ -0,0 +1,91 @@ +#include "RasterToPolygons.hpp" + +#include "AGGRaster.hpp" +#include "libslic3r/MarchingSquares.hpp" +#include "MTUtils.hpp" +#include "ClipperUtils.hpp" + +namespace marchsq { + +// Specialize this struct to register a raster type for the Marching squares alg +template<> struct _RasterTraits { + using Rst = Slic3r::sla::RasterGrayscaleAA; + + // The type of pixel cell in the raster + using ValueType = uint8_t; + + // Value at a given position + static uint8_t get(const Rst &rst, size_t row, size_t col) { return rst.read_pixel(col, row); } + + // Number of rows and cols of the raster + static size_t rows(const Rst &rst) { return rst.resolution().height_px; } + static size_t cols(const Rst &rst) { return rst.resolution().width_px; } +}; + +} // namespace Slic3r::marchsq + +namespace Slic3r { namespace sla { + +template void foreach_vertex(ExPolygon &poly, Fn &&fn) +{ + for (auto &p : poly.contour.points) fn(p); + for (auto &h : poly.holes) + for (auto &p : h.points) fn(p); +} + +ExPolygons raster_to_polygons(const RasterGrayscaleAA &rst, Vec2i windowsize) +{ + size_t rows = rst.resolution().height_px, cols = rst.resolution().width_px; + + if (rows < 2 || cols < 2) return {}; + + Polygons polys; + long w_rows = std::max(2l, long(windowsize.y())); + long w_cols = std::max(2l, long(windowsize.x())); + + std::vector rings = + marchsq::execute(rst, 128, {w_rows, w_cols}); + + polys.reserve(rings.size()); + + auto pxd = rst.pixel_dimensions(); + pxd.w_mm = (rst.resolution().width_px * pxd.w_mm) / (rst.resolution().width_px - 1); + pxd.h_mm = (rst.resolution().height_px * pxd.h_mm) / (rst.resolution().height_px - 1); + + for (const marchsq::Ring &ring : rings) { + Polygon poly; Points &pts = poly.points; + pts.reserve(ring.size()); + + for (const marchsq::Coord &crd : ring) + pts.emplace_back(scaled(crd.c * pxd.w_mm), scaled(crd.r * pxd.h_mm)); + + polys.emplace_back(poly); + } + + // reverse the raster transformations + ExPolygons unioned = union_ex(polys); + coord_t width = scaled(cols * pxd.h_mm), height = scaled(rows * pxd.w_mm); + + auto tr = rst.trafo(); + for (ExPolygon &expoly : unioned) { + if (tr.mirror_y) + foreach_vertex(expoly, [height](Point &p) {p.y() = height - p.y(); }); + + if (tr.mirror_x) + foreach_vertex(expoly, [width](Point &p) {p.x() = width - p.x(); }); + + expoly.translate(-tr.center_x, -tr.center_y); + + if (tr.flipXY) + foreach_vertex(expoly, [](Point &p) { std::swap(p.x(), p.y()); }); + + if ((tr.mirror_x + tr.mirror_y + tr.flipXY) % 2) { + expoly.contour.reverse(); + for (auto &h : expoly.holes) h.reverse(); + } + } + + return unioned; +} + +}} // namespace Slic3r diff --git a/src/libslic3r/SLA/RasterToPolygons.hpp b/src/libslic3r/SLA/RasterToPolygons.hpp new file mode 100644 index 0000000000..c0e1f41145 --- /dev/null +++ b/src/libslic3r/SLA/RasterToPolygons.hpp @@ -0,0 +1,15 @@ +#ifndef RASTERTOPOLYGONS_HPP +#define RASTERTOPOLYGONS_HPP + +#include "libslic3r/ExPolygon.hpp" + +namespace Slic3r { +namespace sla { + +class RasterGrayscaleAA; + +ExPolygons raster_to_polygons(const RasterGrayscaleAA &rst, Vec2i windowsize = {2, 2}); + +}} // namespace Slic3r::sla + +#endif // RASTERTOPOLYGONS_HPP diff --git a/src/libslic3r/SLA/RasterWriter.cpp b/src/libslic3r/SLA/RasterWriter.cpp deleted file mode 100644 index 13aef7d8a3..0000000000 --- a/src/libslic3r/SLA/RasterWriter.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include - -#include - -#include "libslic3r/PrintConfig.hpp" -#include -#include - -#include "ExPolygon.hpp" -#include - -#include -#include - -namespace Slic3r { namespace sla { - -void RasterWriter::write_ini(const std::map &m, std::string &ini) -{ - for (auto ¶m : m) ini += param.first + " = " + param.second + "\n"; -} - -std::string RasterWriter::create_ini_content(const std::string& projectname) const -{ - std::string out("action = print\njobDir = "); - out += projectname + "\n"; - write_ini(m_config, out); - return out; -} - -RasterWriter::RasterWriter(const Raster::Resolution &res, - const Raster::PixelDim & pixdim, - const Raster::Trafo & trafo, - double gamma) - : m_res(res), m_pxdim(pixdim), m_trafo(trafo), m_gamma(gamma) -{} - -void RasterWriter::save(const std::string &fpath, const std::string &prjname) -{ - try { - Zipper zipper(fpath); // zipper with no compression - save(zipper, prjname); - zipper.finalize(); - } catch(std::exception& e) { - BOOST_LOG_TRIVIAL(error) << e.what(); - // Rethrow the exception - throw; - } -} - -void RasterWriter::save(Zipper &zipper, const std::string &prjname) -{ - try { - std::string project = - prjname.empty() ? - boost::filesystem::path(zipper.get_filename()).stem().string() : - prjname; - - zipper.add_entry("config.ini"); - - zipper << create_ini_content(project); - - zipper.add_entry("prusaslicer.ini"); - std::string prusaslicer_ini; - write_ini(m_slicer_config, prusaslicer_ini); - zipper << prusaslicer_ini; - - for(unsigned i = 0; i < m_layers_rst.size(); i++) - { - if(m_layers_rst[i].rawbytes.size() > 0) { - char lyrnum[6]; - std::sprintf(lyrnum, "%.5d", i); - auto zfilename = project + lyrnum + ".png"; - - // Add binary entry to the zipper - zipper.add_entry(zfilename, - m_layers_rst[i].rawbytes.data(), - m_layers_rst[i].rawbytes.size()); - } - } - } catch(std::exception& e) { - BOOST_LOG_TRIVIAL(error) << e.what(); - // Rethrow the exception - throw; - } -} - -namespace { - -std::string get_cfg_value(const DynamicPrintConfig &cfg, const std::string &key) -{ - std::string ret; - - if (cfg.has(key)) { - auto opt = cfg.option(key); - if (opt) ret = opt->serialize(); - } - - return ret; -} - -void append_full_config(const DynamicPrintConfig &cfg, std::map &keys) -{ - using namespace std::literals::string_view_literals; - - // Sorted list of config keys, which shall not be stored into the ini. - static constexpr auto banned_keys = { - "compatible_printers"sv, - "compatible_prints"sv, - "print_host"sv, - "printhost_apikey"sv, - "printhost_cafile"sv - }; - - assert(std::is_sorted(banned_keys.begin(), banned_keys.end())); - auto is_banned = [](const std::string &key) { - return std::binary_search(banned_keys.begin(), banned_keys.end(), key); - }; - - for (const std::string &key : cfg.keys()) - if (! is_banned(key) && ! cfg.option(key)->is_nil()) - keys[key] = cfg.opt_serialize(key); -} - -} // namespace - -void RasterWriter::set_config(const DynamicPrintConfig &cfg) -{ - m_config["layerHeight"] = get_cfg_value(cfg, "layer_height"); - m_config["expTime"] = get_cfg_value(cfg, "exposure_time"); - m_config["expTimeFirst"] = get_cfg_value(cfg, "initial_exposure_time"); - m_config["materialName"] = get_cfg_value(cfg, "sla_material_settings_id"); - m_config["printerModel"] = get_cfg_value(cfg, "printer_model"); - m_config["printerVariant"] = get_cfg_value(cfg, "printer_variant"); - m_config["printerProfile"] = get_cfg_value(cfg, "printer_settings_id"); - m_config["printProfile"] = get_cfg_value(cfg, "sla_print_settings_id"); - m_config["fileCreationTimestamp"] = Utils::utc_timestamp(); - m_config["prusaSlicerVersion"] = SLIC3R_BUILD_ID; - append_full_config(cfg, m_slicer_config); -} - -void RasterWriter::set_statistics(const PrintStatistics &stats) -{ - m_config["usedMaterial"] = std::to_string(stats.used_material); - m_config["numFade"] = std::to_string(stats.num_fade); - m_config["numSlow"] = std::to_string(stats.num_slow); - m_config["numFast"] = std::to_string(stats.num_fast); - m_config["printTime"] = std::to_string(stats.estimated_print_time_s); -} - -} // namespace sla -} // namespace Slic3r diff --git a/src/libslic3r/SLA/RasterWriter.hpp b/src/libslic3r/SLA/RasterWriter.hpp deleted file mode 100644 index 75162893de..0000000000 --- a/src/libslic3r/SLA/RasterWriter.hpp +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef SLA_RASTERWRITER_HPP -#define SLA_RASTERWRITER_HPP - -// For png export of the sliced model -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace Slic3r { - -class DynamicPrintConfig; - -namespace sla { - -// API to write the zipped sla output layers and metadata. -// Implementation uses PNG raster output. -// Be aware that if a large number of layers are allocated, it can very well -// exhaust the available memory especially on 32 bit platform. -// This class is designed to be used in parallel mode. Layers have an ID and -// each layer can be written and compressed independently (in parallel). -// At the end when all layers where written, the save method can be used to -// write out the result into a zipped archive. -class RasterWriter -{ -public: - - // Used for addressing parameters of set_statistics() - struct PrintStatistics - { - double used_material = 0.; - double estimated_print_time_s = 0.; - size_t num_fade = 0; - size_t num_slow = 0; - size_t num_fast = 0; - }; - -private: - - // A struct to bind the raster image data and its compressed bytes together. - struct Layer { - Raster raster; - PNGImage rawbytes; - - Layer() = default; - - // The image is big, do not copy by accident - Layer(const Layer&) = delete; - Layer& operator=(const Layer&) = delete; - - Layer(Layer &&m) = default; - Layer &operator=(Layer &&) = default; - }; - - // We will save the compressed PNG data into RawBytes type buffers in - // parallel. Later we can write every layer to the disk sequentially. - std::vector m_layers_rst; - Raster::Resolution m_res; - Raster::PixelDim m_pxdim; - Raster::Trafo m_trafo; - double m_gamma; - - std::map m_config; - std::map m_slicer_config; - - static void write_ini(const std::map &m, std::string &ini); - std::string create_ini_content(const std::string& projectname) const; - -public: - - // SLARasterWriter is using Raster in custom mirroring mode - RasterWriter(const Raster::Resolution &res, - const Raster::PixelDim & pixdim, - const Raster::Trafo & trafo, - double gamma = 1.); - - RasterWriter(const RasterWriter& ) = delete; - RasterWriter& operator=(const RasterWriter&) = delete; - RasterWriter(RasterWriter&& m) = default; - RasterWriter& operator=(RasterWriter&&) = default; - - inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); } - inline unsigned layers() const { return unsigned(m_layers_rst.size()); } - - template void draw_polygon(const Poly& p, unsigned lyr) - { - assert(lyr < m_layers_rst.size()); - m_layers_rst[lyr].raster.draw(p); - } - - inline void begin_layer(unsigned lyr) { - if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1); - m_layers_rst[lyr].raster.reset(m_res, m_pxdim, m_trafo); - } - - inline void begin_layer() { - m_layers_rst.emplace_back(); - m_layers_rst.front().raster.reset(m_res, m_pxdim, m_trafo); - } - - inline void finish_layer(unsigned lyr_id) { - assert(lyr_id < m_layers_rst.size()); - m_layers_rst[lyr_id].rawbytes.serialize(m_layers_rst[lyr_id].raster); - m_layers_rst[lyr_id].raster.reset(); - } - - inline void finish_layer() { - if(!m_layers_rst.empty()) { - m_layers_rst.back().rawbytes.serialize(m_layers_rst.back().raster); - m_layers_rst.back().raster.reset(); - } - } - - void save(const std::string &fpath, const std::string &prjname = ""); - void save(Zipper &zipper, const std::string &prjname = ""); - - void set_statistics(const PrintStatistics &statistics); - - void set_config(const DynamicPrintConfig &cfg); -}; - -} // namespace sla -} // namespace Slic3r - -#endif // SLARASTERWRITER_HPP diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 4ec5aae29f..2402207a8a 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -227,6 +227,8 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con m_material_config.apply_only(config, material_diff, true); // Handle changes to object config defaults m_default_object_config.apply_only(config, object_diff, true); + + if (m_printer) m_printer->apply(m_printer_config); struct ModelObjectStatus { enum Status { @@ -482,7 +484,6 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con } if(m_objects.empty()) { - m_printer.reset(); m_printer_input = {}; m_print_statistics = {}; } @@ -657,6 +658,12 @@ std::string SLAPrint::validate() const return ""; } +void SLAPrint::set_printer(SLAPrinter *arch) +{ + invalidate_step(slapsRasterize); + m_printer = arch; +} + bool SLAPrint::invalidate_step(SLAPrintStep step) { bool invalidated = Inherited::invalidate_step(step); @@ -676,7 +683,7 @@ void SLAPrint::process() // Assumption: at this point the print objects should be populated only with // the model objects we have to process and the instances are also filtered - Steps printsteps{this}; + Steps printsteps(this); // We want to first process all objects... std::vector level1_obj_steps = { @@ -729,7 +736,7 @@ void SLAPrint::process() throw_if_canceled(); po->set_done(step); } - + incr = printsteps.progressrange(step); } } @@ -754,7 +761,7 @@ void SLAPrint::process() throw_if_canceled(); set_done(currentstep); } - + st += printsteps.progressrange(currentstep); } @@ -855,36 +862,6 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector mirror; - - double w = m_printer_config.display_width.getFloat(); - double h = m_printer_config.display_height.getFloat(); - auto pw = size_t(m_printer_config.display_pixels_x.getInt()); - auto ph = size_t(m_printer_config.display_pixels_y.getInt()); - - mirror[X] = m_printer_config.display_mirror_x.getBool(); - mirror[Y] = m_printer_config.display_mirror_y.getBool(); - - auto orientation = get_printer_orientation(); - if (orientation == sla::Raster::roPortrait) { - std::swap(w, h); - std::swap(pw, ph); - } - - res = sla::Raster::Resolution{pw, ph}; - pxdim = sla::Raster::PixelDim{w / pw, h / ph}; - sla::Raster::Trafo tr{orientation, mirror}; - tr.gamma = m_printer_config.gamma_correction.getFloat(); - - m_printer.reset(new sla::RasterWriter(res, pxdim, tr)); - m_printer->set_config(m_full_print_config); - return *m_printer; -} - // Returns true if an object step is done on all objects and there's at least one object. bool SLAPrint::is_step_done(SLAPrintObjectStep step) const { diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 70f773f6b2..a272075656 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -3,7 +3,7 @@ #include #include "PrintBase.hpp" -#include "SLA/RasterWriter.hpp" +#include "SLA/RasterBase.hpp" #include "SLA/SupportTree.hpp" #include "Point.hpp" #include "MTUtils.hpp" @@ -369,6 +369,31 @@ struct SLAPrintStatistics } }; +class SLAPrinter { +protected: + std::vector m_layers; + + virtual uqptr create_raster() const = 0; + virtual sla::EncodedRaster encode_raster(const sla::RasterBase &rst) const = 0; + +public: + virtual ~SLAPrinter() = default; + + virtual void apply(const SLAPrinterConfig &cfg) = 0; + + // Fn have to be thread safe: void(sla::RasterBase& raster, size_t lyrid); + template void draw_layers(size_t layer_num, Fn &&drawfn) + { + m_layers.resize(layer_num); + sla::ccr::enumerate(m_layers.begin(), m_layers.end(), + [this, &drawfn](sla::EncodedRaster& enc, size_t idx) { + auto rst = create_raster(); + drawfn(*rst, idx); + enc = encode_raster(*rst); + }); + } +}; + /** * @brief This class is the high level FSM for the SLA printing process. * @@ -403,18 +428,6 @@ public: // Returns true if the last step was finished with success. bool finished() const override { return this->is_step_done(slaposSliceSupports) && this->Inherited::is_step_done(slapsRasterize); } - inline void export_raster(const std::string& fpath, - const std::string& projectname = "") - { - if(m_printer) m_printer->save(fpath, projectname); - } - - inline void export_raster(Zipper &zipper, - const std::string& projectname = "") - { - if(m_printer) m_printer->save(zipper, projectname); - } - const PrintObjects& objects() const { return m_objects; } const SLAPrintConfig& print_config() const { return m_print_config; } @@ -445,14 +458,15 @@ public: std::vector m_transformed_slices; - template void transformed_slices(Container&& c) { + template void transformed_slices(Container&& c) + { m_transformed_slices = std::forward(c); } friend class SLAPrint::Steps; public: - + explicit PrintLayer(coord_t lvl) : m_level(lvl) {} // for being sorted in their container (see m_printer_input) @@ -474,8 +488,11 @@ public: // The aggregated and leveled print records from various objects. // TODO: use this structure for the preview in the future. const std::vector& print_layers() const { return m_printer_input; } - + + void set_printer(SLAPrinter *archiver); + private: + // Implement same logic as in SLAPrintObject bool invalidate_step(SLAPrintStep st); @@ -491,13 +508,13 @@ private: std::vector m_stepmask; // Ready-made data for rasterization. - std::vector m_printer_input; - - // The printer itself - std::unique_ptr m_printer; - + std::vector m_printer_input; + + // The archive object which collects the raster images after slicing + SLAPrinter *m_printer = nullptr; + // Estimated print time, material consumed. - SLAPrintStatistics m_print_statistics; + SLAPrintStatistics m_print_statistics; class StatusReporter { @@ -512,15 +529,6 @@ private: double status() const { return m_st; } } m_report_status; - - sla::RasterWriter &init_printer(); - - inline sla::Raster::Orientation get_printer_orientation() const - { - auto ro = m_printer_config.display_orientation.getInt(); - return ro == sla::Raster::roPortrait ? sla::Raster::roPortrait : - sla::Raster::roLandscape; - } friend SLAPrintObject; }; diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 01220a633e..e421e9c1dc 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -816,16 +816,7 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { // Rasterizing the model objects, and their supports void SLAPrint::Steps::rasterize() { - if(canceled()) return; - - auto &print_statistics = m_print->m_print_statistics; - auto &printer_input = m_print->m_printer_input; - - // Set up the printer, allocate space for all the layers - sla::RasterWriter &printer = m_print->init_printer(); - - auto lvlcnt = unsigned(printer_input.size()); - printer.layers(lvlcnt); + if(canceled() || !m_print->m_printer) return; // coefficient to map the rasterization state (0-99) to the allocated // portion (slot) of the process state @@ -837,7 +828,7 @@ void SLAPrint::Steps::rasterize() // pst: previous state double pst = current_status(); - double increment = (slot * sd) / printer_input.size(); + double increment = (slot * sd) / m_print->m_printer_input.size(); double dstatus = current_status(); sla::ccr::SpinningMutex slck; @@ -845,20 +836,14 @@ void SLAPrint::Steps::rasterize() // procedure to process one height level. This will run in parallel auto lvlfn = - [this, &slck, &printer, increment, &dstatus, &pst] - (PrintLayer& printlayer, size_t idx) + [this, &slck, increment, &dstatus, &pst] + (sla::RasterBase& raster, size_t idx) { + PrintLayer& printlayer = m_print->m_printer_input[idx]; if(canceled()) return; - auto level_id = unsigned(idx); - // Switch to the appropriate layer in the printer - printer.begin_layer(level_id); - - for(const ClipperLib::Polygon& poly : printlayer.transformed_slices()) - printer.draw_polygon(poly, level_id); - - // Finish the layer for later saving it. - printer.finish_layer(level_id); + for (const ClipperLib::Polygon& poly : printlayer.transformed_slices()) + raster.draw(poly); // Status indication guarded with the spinlock { @@ -875,24 +860,8 @@ void SLAPrint::Steps::rasterize() // last minute escape if(canceled()) return; - // Sequential version (for testing) - // for(unsigned l = 0; l < lvlcnt; ++l) lvlfn(l); - // Print all the layers in parallel - sla::ccr::enumerate(printer_input.begin(), printer_input.end(), lvlfn); - - // Set statistics values to the printer - sla::RasterWriter::PrintStatistics stats; - stats.used_material = (print_statistics.objects_used_material + - print_statistics.support_used_material) / 1000; - - int num_fade = m_print->m_default_object_config.faded_layers.getInt(); - stats.num_fade = num_fade >= 0 ? size_t(num_fade) : size_t(0); - stats.num_fast = print_statistics.fast_layers_count; - stats.num_slow = print_statistics.slow_layers_count; - stats.estimated_print_time_s = print_statistics.estimated_print_time; - - printer.set_statistics(stats); + m_print->m_printer->draw_layers(m_print->m_printer_input.size(), lvlfn); } std::string SLAPrint::Steps::label(SLAPrintObjectStep step) diff --git a/src/libslic3r/SLAPrintSteps.hpp b/src/libslic3r/SLAPrintSteps.hpp index d3341bc146..19b64d4a98 100644 --- a/src/libslic3r/SLAPrintSteps.hpp +++ b/src/libslic3r/SLAPrintSteps.hpp @@ -46,7 +46,7 @@ private: void apply_printer_corrections(SLAPrintObject &po, SliceOrigin o); public: - Steps(SLAPrint *print); + explicit Steps(SLAPrint *print); void hollow_model(SLAPrintObject &po); void drill_holes (SLAPrintObject &po); diff --git a/src/libslic3r/SlicesToTriangleMesh.cpp b/src/libslic3r/SlicesToTriangleMesh.cpp new file mode 100644 index 0000000000..d6a5469619 --- /dev/null +++ b/src/libslic3r/SlicesToTriangleMesh.cpp @@ -0,0 +1,128 @@ + +#include "SlicesToTriangleMesh.hpp" + +#include "libslic3r/MTUtils.hpp" +#include "libslic3r/SLA/Contour3D.hpp" +#include "libslic3r/ClipperUtils.hpp" +#include "libslic3r/Tesselate.hpp" + +#include +#include + +namespace Slic3r { + +inline sla::Contour3D wall_strip(const Polygon &poly, + double lower_z_mm, + double upper_z_mm) +{ + sla::Contour3D ret; + + size_t startidx = ret.points.size(); + size_t offs = poly.points.size(); + + ret.points.reserve(ret.points.size() + 2 *offs); + + for (const Point &p : poly.points) + ret.points.emplace_back(to_3d(unscaled(p), lower_z_mm)); + + for (const Point &p : poly.points) + ret.points.emplace_back(to_3d(unscaled(p), upper_z_mm)); + + for (size_t i = startidx + 1; i < startidx + offs; ++i) { + ret.faces3.emplace_back(i - 1, i, i + offs - 1); + ret.faces3.emplace_back(i, i + offs, i + offs - 1); + } + + ret.faces3.emplace_back(startidx + offs - 1, startidx, startidx + 2 * offs - 1); + ret.faces3.emplace_back(startidx, startidx + offs, startidx + 2 * offs - 1); + + return ret; +} + +// Same as walls() but with identical higher and lower polygons. +sla::Contour3D inline straight_walls(const Polygon &plate, + double lo_z, + double hi_z) +{ + return wall_strip(plate, lo_z, hi_z); +} + +sla::Contour3D inline straight_walls(const ExPolygon &plate, + double lo_z, + double hi_z) +{ + sla::Contour3D ret; + ret.merge(straight_walls(plate.contour, lo_z, hi_z)); + for (auto &h : plate.holes) ret.merge(straight_walls(h, lo_z, hi_z)); + return ret; +} + +sla::Contour3D inline straight_walls(const ExPolygons &slice, + double lo_z, + double hi_z) +{ + sla::Contour3D ret; + for (const ExPolygon &poly : slice) + ret.merge(straight_walls(poly, lo_z, hi_z)); + + return ret; +} + +sla::Contour3D slices_to_triangle_mesh(const std::vector &slices, + double zmin, + const std::vector & grid) +{ + assert(slices.size() == grid.size()); + + using Layers = std::vector; + std::vector layers(slices.size()); + size_t len = slices.size() - 1; + + tbb::parallel_for(size_t(0), len, [&slices, &layers, &grid](size_t i) { + const ExPolygons &upper = slices[i + 1]; + const ExPolygons &lower = slices[i]; + + ExPolygons dff1 = diff_ex(lower, upper); + ExPolygons dff2 = diff_ex(upper, lower); + layers[i].merge(triangulate_expolygons_3d(dff1, grid[i], NORMALS_UP)); + layers[i].merge(triangulate_expolygons_3d(dff2, grid[i], NORMALS_DOWN)); + layers[i].merge(straight_walls(upper, grid[i], grid[i + 1])); + + }); + + sla::Contour3D ret = tbb::parallel_reduce( + tbb::blocked_range(layers.begin(), layers.end()), + sla::Contour3D{}, + [](const tbb::blocked_range& r, sla::Contour3D init) { + for(auto it = r.begin(); it != r.end(); ++it ) init.merge(*it); + return init; + }, + []( const sla::Contour3D &a, const sla::Contour3D &b ) { + sla::Contour3D res{a}; res.merge(b); return res; + }); + + ret.merge(triangulate_expolygons_3d(slices.front(), zmin, NORMALS_DOWN)); + ret.merge(straight_walls(slices.front(), zmin, grid.front())); + ret.merge(triangulate_expolygons_3d(slices.back(), grid.back(), NORMALS_UP)); + + return ret; +} + +void slices_to_triangle_mesh(TriangleMesh & mesh, + const std::vector &slices, + double zmin, + double lh, + double ilh) +{ + std::vector wall_meshes(slices.size()); + std::vector grid(slices.size(), zmin + ilh); + + for (size_t i = 1; i < grid.size(); ++i) grid[i] = grid[i - 1] + lh; + + sla::Contour3D cntr = slices_to_triangle_mesh(slices, zmin, grid); + mesh.merge(sla::to_triangle_mesh(cntr)); + mesh.repaired = true; + mesh.require_shared_vertices(); +} + +} // namespace Slic3r diff --git a/src/libslic3r/SlicesToTriangleMesh.hpp b/src/libslic3r/SlicesToTriangleMesh.hpp new file mode 100644 index 0000000000..133312d569 --- /dev/null +++ b/src/libslic3r/SlicesToTriangleMesh.hpp @@ -0,0 +1,24 @@ +#ifndef SLICESTOTRIANGLEMESH_HPP +#define SLICESTOTRIANGLEMESH_HPP + +#include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/ExPolygon.hpp" + +namespace Slic3r { + +void slices_to_triangle_mesh(TriangleMesh & mesh, + const std::vector &slices, + double zmin, + double lh, + double ilh); + +inline TriangleMesh slices_to_triangle_mesh( + const std::vector &slices, double zmin, double lh, double ilh) +{ + TriangleMesh out; slices_to_triangle_mesh(out, slices, zmin, lh, ilh); + return out; +} + +} // namespace Slic3r + +#endif // SLICESTOTRIANGLEMESH_HPP diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 72b4f465b0..43f582d5f9 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -971,6 +971,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ std::vector enforcers = object.slice_support_enforcers(); std::vector blockers = object.slice_support_blockers(); + // Append custom supports. + object.project_and_append_custom_enforcers(enforcers); + object.project_and_append_custom_blockers(blockers); + // Output layers, sorted by top Z. MyLayersPtr contact_out; @@ -1097,10 +1101,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ if (! enforcers.empty()) { // Apply the "support enforcers". //FIXME add the "enforcers" to the sparse support regions only. - const ExPolygons &enforcer = enforcers[layer_id - 1]; + const ExPolygons &enforcer = enforcers[layer_id]; if (! enforcer.empty()) { // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes. - Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(enforcer)), + Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(std::move(enforcer))), offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (! new_contacts.empty()) { if (diff_polygons.empty()) @@ -1111,19 +1115,26 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ } } } - // Apply the "support blockers". - if (! diff_polygons.empty() && ! blockers.empty() && ! blockers[layer_id].empty()) { - // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes. - diff_polygons = diff(diff_polygons, to_polygons(blockers[layer_id])); - } + if (diff_polygons.empty()) continue; + // Apply the "support blockers". + if (! blockers.empty() && ! blockers[layer_id].empty()) { + // Expand the blocker a bit. Custom blockers produce strips + // spanning just the projection between the two slices. + // Subtracting them as they are may leave unwanted narrow + // residues of diff_polygons that would then be supported. + diff_polygons = diff(diff_polygons, + offset(union_(to_polygons(std::move(blockers[layer_id]))), + 1000.*SCALED_EPSILON)); + } + #ifdef SLIC3R_DEBUG { ::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg", iRun, layer_id, - std::find_if(layer.regions.begin(), layer.regions.end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions.begin()), + std::find_if(layer.regions.begin(), layer.regions.end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions.begin()), get_extents(diff_polygons)); Slic3r::ExPolygons expolys = union_ex(diff_polygons, false); svg.draw(expolys); diff --git a/src/libslic3r/TriangulateWall.cpp b/src/libslic3r/TriangulateWall.cpp new file mode 100644 index 0000000000..ec2945b10b --- /dev/null +++ b/src/libslic3r/TriangulateWall.cpp @@ -0,0 +1,133 @@ +#include "TriangulateWall.hpp" +#include "MTUtils.hpp" + +namespace Slic3r { + +class Ring { + size_t idx = 0, nextidx = 1, startidx = 0, begin = 0, end = 0; + +public: + explicit Ring(size_t from, size_t to) : begin(from), end(to) { init(begin); } + + size_t size() const { return end - begin; } + std::pair pos() const { return {idx, nextidx}; } + bool is_lower() const { return idx < size(); } + + void inc() + { + if (nextidx != startidx) nextidx++; + if (nextidx == end) nextidx = begin; + idx ++; + if (idx == end) idx = begin; + } + + void init(size_t pos) + { + startidx = begin + (pos - begin) % size(); + idx = startidx; + nextidx = begin + (idx + 1 - begin) % size(); + } + + bool is_finished() const { return nextidx == idx; } +}; + +static double sq_dst(const Vec3d &v1, const Vec3d& v2) +{ + Vec3d v = v1 - v2; + return v.x() * v.x() + v.y() * v.y() /*+ v.z() * v.z()*/; +} + +static double score(const Ring& onring, const Ring &offring, + const std::vector &pts) +{ + double a = sq_dst(pts[onring.pos().first], pts[offring.pos().first]); + double b = sq_dst(pts[onring.pos().second], pts[offring.pos().first]); + return (std::abs(a) + std::abs(b)) / 2.; +} + +class Triangulator { + const std::vector *pts; + Ring *onring, *offring; + + double calc_score() const + { + return Slic3r::score(*onring, *offring, *pts); + } + + void synchronize_rings() + { + Ring lring = *offring; + auto minsc = Slic3r::score(*onring, lring, *pts); + size_t imin = lring.pos().first; + + lring.inc(); + + while(!lring.is_finished()) { + double score = Slic3r::score(*onring, lring, *pts); + if (score < minsc) { minsc = score; imin = lring.pos().first; } + lring.inc(); + } + + offring->init(imin); + } + + void emplace_indices(std::vector &indices) + { + Vec3i tr{int(onring->pos().first), int(onring->pos().second), + int(offring->pos().first)}; + if (onring->is_lower()) std::swap(tr(0), tr(1)); + indices.emplace_back(tr); + } + +public: + void run(std::vector &indices) + { + synchronize_rings(); + + double score = 0, prev_score = 0; + while (!onring->is_finished() || !offring->is_finished()) { + prev_score = score; + if (onring->is_finished() || (score = calc_score()) > prev_score) { + std::swap(onring, offring); + } else { + emplace_indices(indices); + onring->inc(); + } + } + } + + explicit Triangulator(const std::vector *points, + Ring & lower, + Ring & upper) + : pts{points}, onring{&upper}, offring{&lower} + {} +}; + +Wall triangulate_wall( + const Polygon & lower, + const Polygon & upper, + double lower_z_mm, + double upper_z_mm) +{ + if (upper.points.size() < 3 || lower.points.size() < 3) return {}; + + Wall wall; + auto &pts = wall.first; + auto &ind = wall.second; + + pts.reserve(lower.points.size() + upper.points.size()); + for (auto &p : lower.points) + wall.first.emplace_back(unscaled(p.x()), unscaled(p.y()), lower_z_mm); + for (auto &p : upper.points) + wall.first.emplace_back(unscaled(p.x()), unscaled(p.y()), upper_z_mm); + + ind.reserve(2 * (lower.size() + upper.size())); + + Ring lring{0, lower.points.size()}, uring{lower.points.size(), pts.size()}; + Triangulator t{&pts, lring, uring}; + t.run(ind); + + return wall; +} + +} // namespace Slic3r diff --git a/src/libslic3r/TriangulateWall.hpp b/src/libslic3r/TriangulateWall.hpp new file mode 100644 index 0000000000..68bf4b0ac6 --- /dev/null +++ b/src/libslic3r/TriangulateWall.hpp @@ -0,0 +1,17 @@ +#ifndef TRIANGULATEWALL_HPP +#define TRIANGULATEWALL_HPP + +#include "libslic3r/Polygon.hpp" + +namespace Slic3r { + +using Wall = std::pair, std::vector>; + +Wall triangulate_wall( + const Polygon & lower, + const Polygon & upper, + double lower_z_mm, + double upper_z_mm); +} + +#endif // TRIANGULATEWALL_HPP diff --git a/src/libslic3r/Zipper.cpp b/src/libslic3r/Zipper.cpp index a5b53584d7..02f022083b 100644 --- a/src/libslic3r/Zipper.cpp +++ b/src/libslic3r/Zipper.cpp @@ -17,90 +17,14 @@ namespace Slic3r { -class Zipper::Impl { +class Zipper::Impl: public MZ_Archive { public: - mz_zip_archive arch; std::string m_zipname; - static std::string get_errorstr(mz_zip_error mz_err) - { - switch (mz_err) - { - case MZ_ZIP_NO_ERROR: - return "no error"; - case MZ_ZIP_UNDEFINED_ERROR: - return L("undefined error"); - case MZ_ZIP_TOO_MANY_FILES: - return L("too many files"); - case MZ_ZIP_FILE_TOO_LARGE: - return L("file too large"); - case MZ_ZIP_UNSUPPORTED_METHOD: - return L("unsupported method"); - case MZ_ZIP_UNSUPPORTED_ENCRYPTION: - return L("unsupported encryption"); - case MZ_ZIP_UNSUPPORTED_FEATURE: - return L("unsupported feature"); - case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: - return L("failed finding central directory"); - case MZ_ZIP_NOT_AN_ARCHIVE: - return L("not a ZIP archive"); - case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: - return L("invalid header or archive is corrupted"); - case MZ_ZIP_UNSUPPORTED_MULTIDISK: - return L("unsupported multidisk archive"); - case MZ_ZIP_DECOMPRESSION_FAILED: - return L("decompression failed or archive is corrupted"); - case MZ_ZIP_COMPRESSION_FAILED: - return L("compression failed"); - case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: - return L("unexpected decompressed size"); - case MZ_ZIP_CRC_CHECK_FAILED: - return L("CRC-32 check failed"); - case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: - return L("unsupported central directory size"); - case MZ_ZIP_ALLOC_FAILED: - return L("allocation failed"); - case MZ_ZIP_FILE_OPEN_FAILED: - return L("file open failed"); - case MZ_ZIP_FILE_CREATE_FAILED: - return L("file create failed"); - case MZ_ZIP_FILE_WRITE_FAILED: - return L("file write failed"); - case MZ_ZIP_FILE_READ_FAILED: - return L("file read failed"); - case MZ_ZIP_FILE_CLOSE_FAILED: - return L("file close failed"); - case MZ_ZIP_FILE_SEEK_FAILED: - return L("file seek failed"); - case MZ_ZIP_FILE_STAT_FAILED: - return L("file stat failed"); - case MZ_ZIP_INVALID_PARAMETER: - return L("invalid parameter"); - case MZ_ZIP_INVALID_FILENAME: - return L("invalid filename"); - case MZ_ZIP_BUF_TOO_SMALL: - return L("buffer too small"); - case MZ_ZIP_INTERNAL_ERROR: - return L("internal error"); - case MZ_ZIP_FILE_NOT_FOUND: - return L("file not found"); - case MZ_ZIP_ARCHIVE_TOO_LARGE: - return L("archive is too large"); - case MZ_ZIP_VALIDATION_FAILED: - return L("validation failed"); - case MZ_ZIP_WRITE_CALLBACK_FAILED: - return L("write calledback failed"); - default: - break; - } - - return "unknown error"; - } - std::string formatted_errorstr() const { return L("Error with zip archive") + " " + m_zipname + ": " + - get_errorstr(arch.m_last_error) + "!"; + get_errorstr() + "!"; } SLIC3R_NORETURN void blow_up() const @@ -167,7 +91,7 @@ void Zipper::add_entry(const std::string &name) m_entry = name; } -void Zipper::add_entry(const std::string &name, const uint8_t *data, size_t l) +void Zipper::add_entry(const std::string &name, const void *data, size_t l) { if(!m_impl->is_alive()) return; diff --git a/src/libslic3r/Zipper.hpp b/src/libslic3r/Zipper.hpp index be1e69b5c3..bbaf2f05e7 100644 --- a/src/libslic3r/Zipper.hpp +++ b/src/libslic3r/Zipper.hpp @@ -28,7 +28,7 @@ public: // Will blow up in a runtime exception if the file cannot be created. explicit Zipper(const std::string& zipfname, - e_compression level = NO_COMPRESSION); + e_compression level = FAST_COMPRESSION); ~Zipper(); // No copies allwed, this is a file resource... @@ -49,7 +49,7 @@ public: /// Add a new binary file entry with an instantly given byte buffer. /// This method throws exactly like finish_entry() does. - void add_entry(const std::string& name, const std::uint8_t* data, size_t l); + void add_entry(const std::string& name, const void* data, size_t bytes); // Writing data to the archive works like with standard streams. The target // within the zip file is the entry created with the add_entry method. diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index f6fbba9940..814ee08071 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "Technologies.hpp" #include "Semver.hpp" @@ -248,6 +249,37 @@ static inline bool is_approx(Number value, Number test_value) return std::fabs(double(value) - double(test_value)) < double(EPSILON); } +// A meta-predicate which is true for integers wider than or equal to coord_t +template struct is_scaled_coord +{ + static const constexpr bool value = + std::is_integral::value && + std::numeric_limits::digits >= + std::numeric_limits::digits; +}; + +// Meta predicates for floating, 'scaled coord' and generic arithmetic types +// Can be used to restrict templates to work for only the specified set of types. +// parameter T is the type we want to restrict +// parameter O (Optional defaults to T) is the type that the whole expression +// will be evaluated to. +// e.g. template FloatingOnly is_nan(T val); +// The whole template will be defined only for floating point types and the +// return type will be bool. +// For more info how to use, see docs for std::enable_if +// +template +using FloatingOnly = std::enable_if_t::value, O>; + +template +using ScaledCoordOnly = std::enable_if_t::value, O>; + +template +using IntegerOnly = std::enable_if_t::value, O>; + +template +using ArithmeticOnly = std::enable_if_t::value, O>; + } // namespace Slic3r #endif diff --git a/src/libslic3r/miniz_extension.cpp b/src/libslic3r/miniz_extension.cpp index 17cc136fc4..76b4cb4e55 100644 --- a/src/libslic3r/miniz_extension.cpp +++ b/src/libslic3r/miniz_extension.cpp @@ -1,9 +1,17 @@ +#include + #include "miniz_extension.hpp" #if defined(_MSC_VER) || defined(__MINGW64__) #include "boost/nowide/cstdio.hpp" #endif +#include "I18N.hpp" + +//! macro used to mark string used at localization, +//! return same string +#define L(s) Slic3r::I18N::translate(s) + namespace Slic3r { namespace { @@ -68,4 +76,84 @@ bool open_zip_writer(mz_zip_archive *zip, const std::string &fname) bool close_zip_reader(mz_zip_archive *zip) { return close_zip(zip, true); } bool close_zip_writer(mz_zip_archive *zip) { return close_zip(zip, false); } +MZ_Archive::MZ_Archive() +{ + mz_zip_zero_struct(&arch); } + +std::string MZ_Archive::get_errorstr(mz_zip_error mz_err) +{ + switch (mz_err) + { + case MZ_ZIP_NO_ERROR: + return "no error"; + case MZ_ZIP_UNDEFINED_ERROR: + return L("undefined error"); + case MZ_ZIP_TOO_MANY_FILES: + return L("too many files"); + case MZ_ZIP_FILE_TOO_LARGE: + return L("file too large"); + case MZ_ZIP_UNSUPPORTED_METHOD: + return L("unsupported method"); + case MZ_ZIP_UNSUPPORTED_ENCRYPTION: + return L("unsupported encryption"); + case MZ_ZIP_UNSUPPORTED_FEATURE: + return L("unsupported feature"); + case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: + return L("failed finding central directory"); + case MZ_ZIP_NOT_AN_ARCHIVE: + return L("not a ZIP archive"); + case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: + return L("invalid header or archive is corrupted"); + case MZ_ZIP_UNSUPPORTED_MULTIDISK: + return L("unsupported multidisk archive"); + case MZ_ZIP_DECOMPRESSION_FAILED: + return L("decompression failed or archive is corrupted"); + case MZ_ZIP_COMPRESSION_FAILED: + return L("compression failed"); + case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: + return L("unexpected decompressed size"); + case MZ_ZIP_CRC_CHECK_FAILED: + return L("CRC-32 check failed"); + case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: + return L("unsupported central directory size"); + case MZ_ZIP_ALLOC_FAILED: + return L("allocation failed"); + case MZ_ZIP_FILE_OPEN_FAILED: + return L("file open failed"); + case MZ_ZIP_FILE_CREATE_FAILED: + return L("file create failed"); + case MZ_ZIP_FILE_WRITE_FAILED: + return L("file write failed"); + case MZ_ZIP_FILE_READ_FAILED: + return L("file read failed"); + case MZ_ZIP_FILE_CLOSE_FAILED: + return L("file close failed"); + case MZ_ZIP_FILE_SEEK_FAILED: + return L("file seek failed"); + case MZ_ZIP_FILE_STAT_FAILED: + return L("file stat failed"); + case MZ_ZIP_INVALID_PARAMETER: + return L("invalid parameter"); + case MZ_ZIP_INVALID_FILENAME: + return L("invalid filename"); + case MZ_ZIP_BUF_TOO_SMALL: + return L("buffer too small"); + case MZ_ZIP_INTERNAL_ERROR: + return L("internal error"); + case MZ_ZIP_FILE_NOT_FOUND: + return L("file not found"); + case MZ_ZIP_ARCHIVE_TOO_LARGE: + return L("archive is too large"); + case MZ_ZIP_VALIDATION_FAILED: + return L("validation failed"); + case MZ_ZIP_WRITE_CALLBACK_FAILED: + return L("write calledback failed"); + default: + break; + } + + return "unknown error"; +} + +} // namespace Slic3r diff --git a/src/libslic3r/miniz_extension.hpp b/src/libslic3r/miniz_extension.hpp index 8d0967cbcc..006226bf24 100644 --- a/src/libslic3r/miniz_extension.hpp +++ b/src/libslic3r/miniz_extension.hpp @@ -11,6 +11,25 @@ bool open_zip_writer(mz_zip_archive *zip, const std::string &fname_utf8); bool close_zip_reader(mz_zip_archive *zip); bool close_zip_writer(mz_zip_archive *zip); -} +class MZ_Archive { +public: + mz_zip_archive arch; + + MZ_Archive(); + + static std::string get_errorstr(mz_zip_error mz_err); + + std::string get_errorstr() const + { + return get_errorstr(arch.m_last_error) + "!"; + } + + bool is_alive() const + { + return arch.m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + } +}; + +} // namespace Slic3r #endif // MINIZ_EXTENSION_HPP diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index c8f7e9f1c9..3711b12a10 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -31,9 +31,10 @@ set(SLIC3R_GUI_SOURCES GUI/GLCanvas3DManager.cpp GUI/Selection.hpp GUI/Selection.cpp - GUI/Gizmos/GLGizmos.hpp GUI/Gizmos/GLGizmosManager.cpp GUI/Gizmos/GLGizmosManager.hpp + GUI/Gizmos/GLGizmosCommon.cpp + GUI/Gizmos/GLGizmosCommon.hpp GUI/Gizmos/GLGizmoBase.cpp GUI/Gizmos/GLGizmoBase.hpp GUI/Gizmos/GLGizmoMove.cpp @@ -44,6 +45,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoScale.hpp GUI/Gizmos/GLGizmoSlaSupports.cpp GUI/Gizmos/GLGizmoSlaSupports.hpp + GUI/Gizmos/GLGizmoFdmSupports.cpp + GUI/Gizmos/GLGizmoFdmSupports.hpp GUI/Gizmos/GLGizmoFlatten.cpp GUI/Gizmos/GLGizmoFlatten.hpp GUI/Gizmos/GLGizmoCut.cpp @@ -139,12 +142,19 @@ set(SLIC3R_GUI_SOURCES GUI/UpdateDialogs.hpp GUI/FirmwareDialog.cpp GUI/FirmwareDialog.hpp - GUI/ProgressIndicator.hpp - GUI/ProgressStatusBar.hpp - GUI/ProgressStatusBar.cpp GUI/PrintHostDialogs.cpp GUI/PrintHostDialogs.hpp - GUI/Job.hpp + GUI/Jobs/Job.hpp + GUI/Jobs/Job.cpp + GUI/Jobs/ArrangeJob.hpp + GUI/Jobs/ArrangeJob.cpp + GUI/Jobs/RotoptimizeJob.hpp + GUI/Jobs/RotoptimizeJob.cpp + GUI/Jobs/SLAImportJob.hpp + GUI/Jobs/SLAImportJob.cpp + GUI/Jobs/ProgressIndicator.hpp + GUI/ProgressStatusBar.hpp + GUI/ProgressStatusBar.cpp GUI/Mouse3DController.cpp GUI/Mouse3DController.hpp GUI/DoubleSlider.cpp @@ -174,6 +184,8 @@ set(SLIC3R_GUI_SOURCES Utils/HexFile.cpp Utils/HexFile.hpp Utils/Thread.hpp + Utils/SLAImport.hpp + Utils/SLAImport.cpp ) if (APPLE) @@ -192,7 +204,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES}) +target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES} rt) if(APPLE) target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY}) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 742941b845..2cc9de38cb 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -259,7 +259,8 @@ Point Bed3D::point_projection(const Point& point) const return m_polygon.point_projection(point); } -void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes) const +void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, + bool show_axes, bool show_texture) const { m_scale_factor = scale_factor; @@ -270,9 +271,9 @@ void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool sho switch (m_type) { - case System: { render_system(canvas, bottom); break; } + case System: { render_system(canvas, bottom, show_texture); break; } default: - case Custom: { render_custom(canvas, bottom); break; } + case Custom: { render_custom(canvas, bottom, show_texture); break; } } glsafe(::glDisable(GL_DEPTH_TEST)); @@ -384,12 +385,13 @@ void Bed3D::render_axes() const m_axes.render(); } -void Bed3D::render_system(GLCanvas3D& canvas, bool bottom) const +void Bed3D::render_system(GLCanvas3D& canvas, bool bottom, bool show_texture) const { if (!bottom) render_model(); - render_texture(bottom, canvas); + if (show_texture) + render_texture(bottom, canvas); } void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const @@ -564,7 +566,7 @@ void Bed3D::render_model() const } } -void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom) const +void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture) const { if (m_texture_filename.empty() && m_model_filename.empty()) { @@ -575,7 +577,8 @@ void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom) const if (!bottom) render_model(); - render_texture(bottom, canvas); + if (show_texture) + render_texture(bottom, canvas); } void Bed3D::render_default(bool bottom) const diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 3a5f959788..abdfca1fe0 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -103,11 +103,15 @@ public: // Return true if the bed shape changed, so the calee will update the UI. bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model); - const BoundingBoxf3& get_bounding_box(bool extended) const { return extended ? m_extended_bounding_box : m_bounding_box; } + const BoundingBoxf3& get_bounding_box(bool extended) const { + return extended ? m_extended_bounding_box : m_bounding_box; + } + bool contains(const Point& point) const; Point point_projection(const Point& point) const; - void render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes) const; + void render(GLCanvas3D& canvas, bool bottom, float scale_factor, + bool show_axes, bool show_texture) const; private: void calc_bounding_boxes() const; @@ -115,10 +119,10 @@ private: void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); std::tuple detect_type(const Pointfs& shape) const; void render_axes() const; - void render_system(GLCanvas3D& canvas, bool bottom) const; + void render_system(GLCanvas3D& canvas, bool bottom, bool show_texture) const; void render_texture(bool bottom, GLCanvas3D& canvas) const; void render_model() const; - void render_custom(GLCanvas3D& canvas, bool bottom) const; + void render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture) const; void render_default(bool bottom) const; void reset(); }; diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp index 346d42b21e..1e90d32e00 100644 --- a/src/slic3r/GUI/AppConfig.hpp +++ b/src/slic3r/GUI/AppConfig.hpp @@ -60,7 +60,7 @@ public: boost::trim_all(key_trimmed); assert(key_trimmed == key); assert(! key_trimmed.empty()); -#endif _NDEBUG +#endif // _NDEBUG std::string &old = m_storage[section][key]; if (old != value) { old = value; diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 7ce942855f..cfd38354b6 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -19,6 +19,7 @@ #include "libslic3r/Utils.hpp" #include "libslic3r/GCode/PostProcessor.hpp" #include "libslic3r/GCode/PreviewData.hpp" +#include "libslic3r/Format/SL1.hpp" #include "libslic3r/libslic3r.h" #include @@ -149,7 +150,7 @@ void BackgroundSlicingProcess::process_sla() const std::string export_path = m_sla_print->print_statistics().finalize_output_path(m_export_path); Zipper zipper(export_path); - m_sla_print->export_raster(zipper); + m_sla_archive.export_print(zipper, *m_sla_print); if (m_thumbnail_cb != nullptr) { @@ -473,9 +474,9 @@ void BackgroundSlicingProcess::prepare_upload() m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); } else { m_upload_job.upload_data.upload_path = m_sla_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); - + Zipper zipper{source_path.string()}; - m_sla_print->export_raster(zipper, m_upload_job.upload_data.upload_path.string()); + m_sla_archive.export_print(zipper, *m_sla_print, m_upload_job.upload_data.upload_path.string()); if (m_thumbnail_cb != nullptr) { ThumbnailsList thumbnails; diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index b868a233f1..efaea1d11d 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -10,6 +10,7 @@ #include #include "libslic3r/Print.hpp" +#include "libslic3r/Format/SL1.hpp" #include "slic3r/Utils/PrintHost.hpp" @@ -19,6 +20,7 @@ class DynamicPrintConfig; class GCodePreviewData; class Model; class SLAPrint; +class SL1Archive; class SlicingStatusEvent : public wxEvent { @@ -47,7 +49,7 @@ public: ~BackgroundSlicingProcess(); void set_fff_print(Print *print) { m_fff_print = print; } - void set_sla_print(SLAPrint *print) { m_sla_print = print; } + void set_sla_print(SLAPrint *print) { m_sla_print = print; m_sla_print->set_printer(&m_sla_archive); } void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; } void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; } @@ -155,6 +157,7 @@ private: GCodePreviewData *m_gcode_preview_data = nullptr; // Callback function, used to write thumbnails into gcode. ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr; + SL1Archive m_sla_archive; // Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID. std::string m_temp_output_path; // Output path provided by the user. The output path may be set even if the slicing is running, diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index d0c768de50..e242033d95 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -2096,7 +2096,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent) p->add_page(p->page_filaments = new PageMaterials(this, &p->filaments, _(L("Filament Profiles Selection")), _(L("Filaments")), _(L("Type:")) )); p->add_page(p->page_sla_materials = new PageMaterials(this, &p->sla_materials, - _(L("SLA Material Profiles Selection")) + " ", _(L("SLA Materials")), _(L("Layer height:")) )); + _(L("SLA Material Profiles Selection")) + " ", _(L("SLA Materials")), _(L("Type:")) )); p->add_page(p->page_update = new PageUpdate(this)); diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 186444f662..68193f84d9 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -1291,9 +1291,13 @@ void PointCtrl::set_value(const boost::any& value, bool change_event) boost::any& PointCtrl::get_value() { double x, y; - x_textctrl->GetValue().ToDouble(&x); - y_textctrl->GetValue().ToDouble(&y); - + if (!x_textctrl->GetValue().ToDouble(&x) || + !y_textctrl->GetValue().ToDouble(&y)) + { + set_value(m_value.empty() ? Vec2d(0.0, 0.0) : m_value, true); + show_error(m_parent, _L("Invalid numeric input.")); + } + else if (m_opt.min > x || x > m_opt.max || m_opt.min > y || y > m_opt.max) { @@ -1303,7 +1307,7 @@ boost::any& PointCtrl::get_value() if (y > m_opt.max) y = m_opt.max; set_value(Vec2d(x, y), true); - show_error(m_parent, _(L("Input value is out of range"))); + show_error(m_parent, _L("Input value is out of range")); } return m_value = Vec2d(x, y); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 74abbebdd5..40568f8ded 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1,5 +1,4 @@ #include "libslic3r/libslic3r.h" -#include "slic3r/GUI/Gizmos/GLGizmos.hpp" #include "GLCanvas3D.hpp" #include "admesh/stl.h" @@ -1744,6 +1743,8 @@ void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObje m_render_sla_auxiliaries = visible; for (GLVolume* vol : m_volumes.volumes) { + if (vol->composite_id.object_id == 1000) + continue; // the wipe tower if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo) && (instance_idx == -1 || vol->composite_id.instance_id == instance_idx) && vol->composite_id.volume_id < 0) @@ -1754,10 +1755,15 @@ void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObje void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject* mo, int instance_idx) { for (GLVolume* vol : m_volumes.volumes) { - if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo) - && (instance_idx == -1 || vol->composite_id.instance_id == instance_idx)) { - vol->is_active = visible; - vol->force_native_color = (instance_idx != -1); + if (vol->composite_id.object_id == 1000) { // wipe tower + vol->is_active = (visible && mo == nullptr); + } + else { + if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo) + && (instance_idx == -1 || vol->composite_id.instance_id == instance_idx)) { + vol->is_active = visible; + vol->force_native_color = (instance_idx != -1); + } } } if (visible && !mo) @@ -5186,7 +5192,7 @@ void GLCanvas3D::_picking_pass() const glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); - m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane(); + m_camera_clipping_plane = m_gizmos.get_clipping_plane(); if (m_camera_clipping_plane.is_active()) { ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data()); ::glEnable(GL_CLIP_PLANE0); @@ -5335,14 +5341,19 @@ void GLCanvas3D::_render_background() const glsafe(::glPopMatrix()); } -void GLCanvas3D::_render_bed(float theta, bool show_axes) const +void GLCanvas3D::_render_bed(bool bottom, bool show_axes) const { float scale_factor = 1.0; #if ENABLE_RETINA_GL scale_factor = m_retina_helper->get_scale_factor(); #endif // ENABLE_RETINA_GL + + bool show_texture = ! bottom || + (m_gizmos.get_current_type() != GLGizmosManager::FdmSupports + && m_gizmos.get_current_type() != GLGizmosManager::SlaSupports); + #if ENABLE_NON_STATIC_CANVAS_MANAGER - wxGetApp().plater()->get_bed().render(const_cast(*this), theta, scale_factor, show_axes); + wxGetApp().plater()->get_bed().render(const_cast(*this), bottom, scale_factor, show_axes, show_texture); #else m_bed.render(const_cast(*this), theta, scale_factor, show_axes); #endif // ENABLE_NON_STATIC_CANVAS_MANAGER @@ -5355,7 +5366,7 @@ void GLCanvas3D::_render_objects() const glsafe(::glEnable(GL_DEPTH_TEST)); - m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane(); + m_camera_clipping_plane = m_gizmos.get_clipping_plane(); if (m_picking_enabled) { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 07fa00b9d9..c7d4f4a064 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -755,7 +755,7 @@ private: void _picking_pass() const; void _rectangular_selection_picking_pass() const; void _render_background() const; - void _render_bed(float theta, bool show_axes) const; + void _render_bed(bool bottom, bool show_axes) const; void _render_objects() const; void _render_selection() const; #if ENABLE_RENDER_SELECTION_CENTER diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 234847bc4b..aee71f16e9 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -321,20 +322,41 @@ bool GUI_App::on_init_inner() set_data_dir(wxStandardPaths::Get().GetUserDataDir().ToUTF8().data()); app_config = new AppConfig(); - preset_bundle = new PresetBundle(); - - // just checking for existence of Slic3r::data_dir is not enough : it may be an empty directory - // supplied as argument to --datadir; in that case we should still run the wizard - preset_bundle->setup_directories(); // load settings app_conf_exists = app_config->exists(); if (app_conf_exists) { app_config->load(); } + + std::string msg = Http::tls_global_init(); + wxRichMessageDialog + dlg(nullptr, + wxString::Format(_(L("%s\nDo you want to continue?")), _(msg)), + "PrusaSlicer", wxICON_QUESTION | wxYES_NO); + + bool ssl_accept = app_config->get("tls_cert_store_accepted") == "yes"; + std::string ssl_cert_store = app_config->get("tls_accepted_cert_store_location"); + ssl_accept = ssl_accept && ssl_cert_store == Http::tls_system_cert_store(); + + dlg.ShowCheckBox(_(L("Remember my choice"))); + if (!msg.empty() && !ssl_accept) { + if (dlg.ShowModal() != wxID_YES) return false; + app_config->set("tls_cert_store_accepted", + dlg.IsCheckBoxChecked() ? "yes" : "no"); + app_config->set("tls_accepted_cert_store_location", + dlg.IsCheckBoxChecked() ? Http::tls_system_cert_store() : ""); + } + app_config->set("version", SLIC3R_VERSION); app_config->save(); + + preset_bundle = new PresetBundle(); + + // just checking for existence of Slic3r::data_dir is not enough : it may be an empty directory + // supplied as argument to --datadir; in that case we should still run the wizard + preset_bundle->setup_directories(); #ifdef __WXMSW__ associate_3mf_files(); @@ -925,7 +947,8 @@ void GUI_App::add_config_menu(wxMenuBar *menu) local_menu->AppendSeparator(); auto mode_menu = new wxMenu(); mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _(L("Simple")), _(L("Simple View Mode"))); - mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _(L("Advanced")), _(L("Advanced View Mode"))); +// mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _(L("Advanced")), _(L("Advanced View Mode"))); + mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _CTX(L_CONTEXT("Advanced", "Mode"), "Mode"), _L("Advanced View Mode")); mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeExpert, _(L("Expert")), _(L("Expert View Mode"))); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if(get_mode() == comSimple) evt.Check(true); }, config_id_base + ConfigMenuModeSimple); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if(get_mode() == comAdvanced) evt.Check(true); }, config_id_base + ConfigMenuModeAdvanced); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 5d06edad9c..1f2ce0221d 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1209,6 +1209,13 @@ static bool improper_category(const std::string& category, const int extruders_c (!is_object_settings && category == "Support material"); } +static bool is_object_item(ItemType item_type) +{ + return item_type & itObject || item_type & itInstance || + // multi-selection in ObjectList, but full_object in Selection + (item_type == itUndef && scene_selection().is_single_full_object()); +} + void ObjectList::get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part) { auto options = get_options(is_part); @@ -1579,9 +1586,7 @@ wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_) const ItemType item_type = m_objects_model->GetItemType(GetSelection()); if (item_type == itUndef && !selection.is_single_full_object()) return nullptr; - const bool is_object_settings = item_type & itObject || item_type & itInstance || - // multi-selection in ObjectList, but full_object in Selection - (item_type == itUndef && selection.is_single_full_object()); + const bool is_object_settings = is_object_item(item_type); create_freq_settings_popupmenu(menu, is_object_settings); if (mode == comAdvanced) @@ -1821,8 +1826,7 @@ wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu) const wxDataViewItem selected_item = GetSelection(); wxDataViewItem item = m_objects_model->GetItemType(selected_item) & itSettings ? m_objects_model->GetParent(selected_item) : selected_item; - const bool is_part = !(m_objects_model->GetItemType(item) == itObject || scene_selection().is_single_full_object()); - get_options_menu(settings_menu, is_part); + get_options_menu(settings_menu, !is_object_item(m_objects_model->GetItemType(item))); for (auto cat : settings_menu) { append_menu_item(menu, wxID_ANY, _(cat.first), "", @@ -2067,37 +2071,40 @@ void ObjectList::load_shape_object(const std::string& type_name) // Create mesh BoundingBoxf3 bb; TriangleMesh mesh = create_mesh(type_name, bb); + load_mesh_object(mesh, _(L("Shape")) + "-" + _(type_name)); +} +void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name) +{ // Add mesh to model as a new object Model& model = wxGetApp().plater()->model(); - const wxString name = _(L("Shape")) + "-" + _(type_name); #ifdef _DEBUG check_model_ids_validity(model); #endif /* _DEBUG */ - + std::vector object_idxs; ModelObject* new_object = model.add_object(); new_object->name = into_u8(name); new_object->add_instance(); // each object should have at list one instance - + ModelVolume* new_volume = new_object->add_volume(mesh); new_volume->name = into_u8(name); // set a default extruder value, since user can't add it manually new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); new_object->invalidate_bounding_box(); - + new_object->center_around_origin(); new_object->ensure_on_bed(); - + const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb(); new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast(), -new_object->origin_translation(2))); - + object_idxs.push_back(model.objects.size() - 1); #ifdef _DEBUG check_model_ids_validity(model); #endif /* _DEBUG */ - + paste_objects_into_list(object_idxs); #ifdef _DEBUG diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 609411cd59..72e130737c 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -24,6 +24,7 @@ class ConfigOptionsGroup; class DynamicPrintConfig; class ModelObject; class ModelVolume; +class TriangleMesh; enum class ModelVolumeType : int; // FIXME: broken build on mac os because of this is missing: @@ -265,6 +266,7 @@ public: void load_part(ModelObject* model_object, std::vector> &volumes_info, ModelVolumeType type); void load_generic_subobject(const std::string& type_name, const ModelVolumeType type); void load_shape_object(const std::string &type_name); + void load_mesh_object(const TriangleMesh &mesh, const wxString &name); void del_object(const int obj_idx); void del_subobject_item(wxDataViewItem& item); void del_settings_from_config(const wxDataViewItem& parent_item); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 0d19a86afe..8a2b719763 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -32,6 +32,8 @@ static const float CONSTRAINED_COLOR[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; class ImGuiWrapper; class GLCanvas3D; class ClippingPlane; +enum class CommonGizmosDataID; +class CommonGizmosDataPool; class GLGizmoBase { @@ -101,6 +103,7 @@ protected: ImGuiWrapper* m_imgui; bool m_first_input_window_render; mutable std::string m_tooltip; + CommonGizmosDataPool* m_c; public: GLGizmoBase(GLCanvas3D& parent, @@ -128,6 +131,8 @@ public: bool is_activable() const { return on_is_activable(); } bool is_selectable() const { return on_is_selectable(); } + CommonGizmosDataID get_requirements() const { return on_get_requirements(); } + void set_common_data_pool(CommonGizmosDataPool* ptr) { m_c = ptr; } unsigned int get_sprite_id() const { return m_sprite_id; } @@ -161,6 +166,7 @@ protected: virtual void on_set_hover_id() {} virtual bool on_is_activable() const { return true; } virtual bool on_is_selectable() const { return true; } + virtual CommonGizmosDataID on_get_requirements() const { return CommonGizmosDataID(0); } virtual void on_enable_grabber(unsigned int id) {} virtual void on_disable_grabber(unsigned int id) {} virtual void on_start_dragging() {} diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp new file mode 100644 index 0000000000..8094d10ad0 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -0,0 +1,673 @@ +// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. +#include "GLGizmoFdmSupports.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" + +#include + +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/PresetBundle.hpp" +#include "slic3r/GUI/Camera.hpp" +#include "libslic3r/Model.hpp" + + + +namespace Slic3r { +namespace GUI { + +GLGizmoFdmSupports::GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoBase(parent, icon_filename, sprite_id) + , m_quadric(nullptr) +{ + m_clipping_plane.reset(new ClippingPlane()); + m_quadric = ::gluNewQuadric(); + if (m_quadric != nullptr) + // using GLU_FILL does not work when the instance's transformation + // contains mirroring (normals are reverted) + ::gluQuadricDrawStyle(m_quadric, GLU_FILL); +} + +GLGizmoFdmSupports::~GLGizmoFdmSupports() +{ + if (m_quadric != nullptr) + ::gluDeleteQuadric(m_quadric); +} + +bool GLGizmoFdmSupports::on_init() +{ + m_shortcut_key = WXK_CONTROL_L; + + m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; + m_desc["reset_direction"] = _L("Reset direction"); + m_desc["cursor_size"] = _L("Cursor size") + ": "; + m_desc["enforce_caption"] = _L("Left mouse button") + ": "; + m_desc["enforce"] = _L("Enforce supports"); + m_desc["block_caption"] = _L("Right mouse button") + " "; + m_desc["block"] = _L("Block supports"); + m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": "; + m_desc["remove"] = _L("Remove selection"); + m_desc["remove_all"] = _L("Remove all"); + + return true; +} + +void GLGizmoFdmSupports::set_fdm_support_data(ModelObject* model_object, const Selection& selection) +{ + const ModelObject* mo = m_c->selection_info() ? m_c->selection_info()->model_object() : nullptr; + if (! mo) + return; + + if (mo && selection.is_from_single_instance() + && (mo != m_old_mo || mo->volumes.size() != m_old_volumes_size)) + { + update_mesh(); + m_old_mo = mo; + m_old_volumes_size = mo->volumes.size(); + } +} + + + +void GLGizmoFdmSupports::on_render() const +{ + const Selection& selection = m_parent.get_selection(); + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + render_triangles(selection); + m_c->object_clipper()->render_cut(); + render_cursor_circle(); + + glsafe(::glDisable(GL_BLEND)); +} + +void GLGizmoFdmSupports::render_triangles(const Selection& selection) const +{ + const ModelObject* mo = m_c->selection_info()->model_object(); + + glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); + ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); } ); + glsafe(::glPolygonOffset(-1.0, 1.0)); + + int mesh_id = -1; + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + + ++mesh_id; + + const Transform3d trafo_matrix = + mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * + mv->get_matrix(); + + glsafe(::glPushMatrix()); + glsafe(::glMultMatrixd(trafo_matrix.data())); + + // Now render both enforcers and blockers. + for (int i=0; i<2; ++i) { + if (m_ivas[mesh_id][i].has_VBOs()) { + glsafe(::glColor4f(i ? 1.f : 0.2f, 0.2f, i ? 0.2f : 1.0f, 0.5f)); + m_ivas[mesh_id][i].render(); + } + } + glsafe(::glPopMatrix()); + } +} + + +void GLGizmoFdmSupports::render_cursor_circle() const +{ + const Camera& camera = wxGetApp().plater()->get_camera(); + float zoom = (float)camera.get_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + Size cnv_size = m_parent.get_canvas_size(); + float cnv_half_width = 0.5f * (float)cnv_size.get_width(); + float cnv_half_height = 0.5f * (float)cnv_size.get_height(); + if ((cnv_half_width == 0.0f) || (cnv_half_height == 0.0f)) + return; + Vec2d mouse_pos(m_parent.get_local_mouse_position()(0), m_parent.get_local_mouse_position()(1)); + Vec2d center(mouse_pos(0) - cnv_half_width, cnv_half_height - mouse_pos(1)); + center = center * inv_zoom; + + glsafe(::glLineWidth(1.5f)); + float color[3]; + color[0] = 0.f; + color[1] = 1.f; + color[2] = 0.3f; + glsafe(::glColor3fv(color)); + glsafe(::glDisable(GL_DEPTH_TEST)); + + glsafe(::glPushMatrix()); + glsafe(::glLoadIdentity()); + // ensure that the circle is renderered inside the frustrum + glsafe(::glTranslated(0.0, 0.0, -(camera.get_near_z() + 0.5))); + // ensure that the overlay fits the frustrum near z plane + double gui_scale = camera.get_gui_scale(); + glsafe(::glScaled(gui_scale, gui_scale, 1.0)); + + glsafe(::glPushAttrib(GL_ENABLE_BIT)); + glsafe(::glLineStipple(4, 0xAAAA)); + glsafe(::glEnable(GL_LINE_STIPPLE)); + + ::glBegin(GL_LINE_LOOP); + for (double angle=0; angle<2*M_PI; angle+=M_PI/20.) + ::glVertex2f(GLfloat(center.x()+m_cursor_radius*cos(angle)), GLfloat(center.y()+m_cursor_radius*sin(angle))); + glsafe(::glEnd()); + + glsafe(::glPopAttrib()); + glsafe(::glPopMatrix()); +} + + +void GLGizmoFdmSupports::on_render_for_picking() const +{ + +} + + + +void GLGizmoFdmSupports::update_mesh() +{ + wxBusyCursor wait; + + const ModelObject* mo = m_c->selection_info()->model_object(); + size_t num_of_volumes = 0; + for (const ModelVolume* mv : mo->volumes) + if (mv->is_model_part()) + ++num_of_volumes; + + m_selected_facets.resize(num_of_volumes); + m_neighbors.resize(num_of_volumes); + m_ivas.clear(); + m_ivas.resize(num_of_volumes); + + int volume_id = -1; + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + + ++volume_id; + + // This mesh does not account for the possible Z up SLA offset. + const TriangleMesh* mesh = &mv->mesh(); + + m_selected_facets[volume_id].assign(mesh->its.indices.size(), FacetSupportType::NONE); + + // Load current state from ModelVolume. + for (FacetSupportType type : {FacetSupportType::ENFORCER, FacetSupportType::BLOCKER}) { + const std::vector& list = mv->m_supported_facets.get_facets(type); + for (int i : list) + m_selected_facets[volume_id][i] = type; + } + update_vertex_buffers(mv, volume_id, true, true); + + m_neighbors[volume_id].resize(3 * mesh->its.indices.size()); + + // Prepare vector of vertex_index - facet_index pairs to quickly find adjacent facets + for (size_t i=0; iits.indices.size(); ++i) { + const stl_triangle_vertex_indices& ind = mesh->its.indices[i]; + m_neighbors[volume_id][3*i] = std::make_pair(ind(0), i); + m_neighbors[volume_id][3*i+1] = std::make_pair(ind(1), i); + m_neighbors[volume_id][3*i+2] = std::make_pair(ind(2), i); + } + std::sort(m_neighbors[volume_id].begin(), m_neighbors[volume_id].end()); + } +} + + + + +bool operator<(const GLGizmoFdmSupports::NeighborData& a, const GLGizmoFdmSupports::NeighborData& b) { + return a.first < b.first; +} + + +// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event. +// The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is +// aware that the event was reacted to and stops trying to make different sense of it. If the gizmo +// concludes that the event was not intended for it, it should return false. +bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) +{ + if (action == SLAGizmoEventType::MouseWheelUp + || action == SLAGizmoEventType::MouseWheelDown) { + if (control_down) { + double pos = m_c->object_clipper()->get_position(); + pos = action == SLAGizmoEventType::MouseWheelDown + ? std::max(0., pos - 0.01) + : std::min(1., pos + 0.01); + m_c->object_clipper()->set_position(pos, true); + return true; + } + else if (alt_down) { + m_cursor_radius = action == SLAGizmoEventType::MouseWheelDown + ? std::max(m_cursor_radius - CursorRadiusStep, CursorRadiusMin) + : std::min(m_cursor_radius + CursorRadiusStep, CursorRadiusMax); + m_parent.set_as_dirty(); + return true; + } + } + + if (action == SLAGizmoEventType::ResetClippingPlane) { + m_c->object_clipper()->set_position(-1., false); + return true; + } + + if (action == SLAGizmoEventType::LeftDown + || action == SLAGizmoEventType::RightDown + || (action == SLAGizmoEventType::Dragging && m_button_down != Button::None)) { + + FacetSupportType new_state = FacetSupportType::NONE; + if (! shift_down) { + if (action == SLAGizmoEventType::Dragging) + new_state = m_button_down == Button::Left + ? FacetSupportType::ENFORCER + : FacetSupportType::BLOCKER; + else + new_state = action == SLAGizmoEventType::LeftDown + ? FacetSupportType::ENFORCER + : FacetSupportType::BLOCKER; + } + + const Camera& camera = wxGetApp().plater()->get_camera(); + const Selection& selection = m_parent.get_selection(); + const ModelObject* mo = m_c->selection_info()->model_object(); + const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; + const Transform3d& instance_trafo = mi->get_transformation().get_matrix(); + + std::vector>> hit_positions_and_facet_ids; + bool some_mesh_was_hit = false; + + Vec3f normal = Vec3f::Zero(); + Vec3f hit = Vec3f::Zero(); + size_t facet = 0; + Vec3f closest_hit = Vec3f::Zero(); + double closest_hit_squared_distance = std::numeric_limits::max(); + size_t closest_facet = 0; + size_t closest_hit_mesh_id = size_t(-1); + + // Transformations of individual meshes + std::vector trafo_matrices; + + int mesh_id = -1; + // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + + ++mesh_id; + + trafo_matrices.push_back(instance_trafo * mv->get_matrix()); + hit_positions_and_facet_ids.push_back(std::vector>()); + + if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh( + mouse_position, + trafo_matrices[mesh_id], + camera, + hit, + normal, + m_clipping_plane.get(), + &facet)) + { + // Is this hit the closest to the camera so far? + double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast()).squaredNorm(); + if (hit_squared_distance < closest_hit_squared_distance) { + closest_hit_squared_distance = hit_squared_distance; + closest_facet = facet; + closest_hit_mesh_id = mesh_id; + closest_hit = hit; + } + } + } + // We now know where the ray hit, let's save it and cast another ray + if (closest_hit_mesh_id != size_t(-1)) // only if there is at least one hit + hit_positions_and_facet_ids[closest_hit_mesh_id].emplace_back(closest_hit, closest_facet); + + + // Now propagate the hits + mesh_id = -1; + for (const ModelVolume* mv : mo->volumes) { + + if (! mv->is_model_part()) + continue; + + ++mesh_id; + bool update_both = false; + + const Transform3d& trafo_matrix = trafo_matrices[mesh_id]; + + // Calculate how far can a point be from the line (in mesh coords). + // FIXME: The scaling of the mesh can be non-uniform. + const Vec3d sf = Geometry::Transformation(trafo_matrix).get_scaling_factor(); + const float avg_scaling = (sf(0) + sf(1) + sf(2))/3.; + const float limit = pow(m_cursor_radius/avg_scaling , 2.f); + + // For all hits on this mesh... + for (const std::pair& hit_and_facet : hit_positions_and_facet_ids[mesh_id]) { + some_mesh_was_hit = true; + const TriangleMesh* mesh = &mv->mesh(); + std::vector& neighbors = m_neighbors[mesh_id]; + + // Calculate direction from camera to the hit (in mesh coords): + Vec3f dir = ((trafo_matrix.inverse() * camera.get_position()).cast() - hit_and_facet.first).normalized(); + + // A lambda to calculate distance from the centerline: + auto squared_distance_from_line = [&hit_and_facet, &dir](const Vec3f point) -> float { + Vec3f diff = hit_and_facet.first - point; + return (diff - diff.dot(dir) * dir).squaredNorm(); + }; + + // A lambda to determine whether this facet is potentionally visible (still can be obscured) + auto faces_camera = [&dir](const ModelVolume* mv, const size_t& facet) -> bool { + return (mv->mesh().stl.facet_start[facet].normal.dot(dir) > 0.); + }; + // Now start with the facet the pointer points to and check all adjacent facets. neighbors vector stores + // pairs of vertex_idx - facet_idx and is sorted with respect to the former. Neighboring facet index can be + // quickly found by finding a vertex in the list and read the respective facet ids. + std::vector facets_to_select{hit_and_facet.second}; + NeighborData vertex = std::make_pair(0, 0); + std::vector visited(m_selected_facets[mesh_id].size(), false); // keep track of facets we already processed + size_t facet_idx = 0; // index into facets_to_select + auto it = neighbors.end(); + while (facet_idx < facets_to_select.size()) { + size_t facet = facets_to_select[facet_idx]; + if (! visited[facet]) { + // check all three vertices and in case they're close enough, find the remaining facets + // and add them to the list to be proccessed later + for (size_t i=0; i<3; ++i) { + vertex.first = mesh->its.indices[facet](i); // vertex index + float dist = squared_distance_from_line(mesh->its.vertices[vertex.first]); + if (dist < limit) { + it = std::lower_bound(neighbors.begin(), neighbors.end(), vertex); + while (it != neighbors.end() && it->first == vertex.first) { + if (it->second != facet && faces_camera(mv, it->second)) + facets_to_select.push_back(it->second); + ++it; + } + } + } + visited[facet] = true; + } + ++facet_idx; + } + + // Now just select all facets that passed. + for (size_t next_facet : facets_to_select) { + FacetSupportType& facet = m_selected_facets[mesh_id][next_facet]; + + if (facet != new_state && facet != FacetSupportType::NONE) { + // this triangle is currently in the other VBA. + // Both VBAs need to be refreshed. + update_both = true; + } + facet = new_state; + } + } + + update_vertex_buffers(mv, mesh_id, + new_state == FacetSupportType::ENFORCER || update_both, + new_state == FacetSupportType::BLOCKER || update_both + ); + } + + if (some_mesh_was_hit) + { + if (m_button_down == Button::None) + m_button_down = ((action == SLAGizmoEventType::LeftDown) ? Button::Left : Button::Right); + // Force rendering. In case the user is dragging, the queue can be + // flooded by wxEVT_MOVING event and rendering would be skipped. + m_parent.render(); + return true; + } + if (action == SLAGizmoEventType::Dragging && m_button_down != Button::None) { + // Same as above. We don't want the cursor to freeze when we + // leave the mesh while painting. + m_parent.render(); + return true; + } + } + + if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp) + && m_button_down != Button::None) { + m_button_down = Button::None; + + // Synchronize gizmo with ModelVolume data. + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + ++idx; + if (! mv->is_model_part()) + continue; + for (int i=0; im_supported_facets.set_facet(i, m_selected_facets[idx][i]); + } + + return true; + } + + return false; +} + + +void GLGizmoFdmSupports::update_vertex_buffers(const ModelVolume* mv, + int mesh_id, + bool update_enforcers, + bool update_blockers) +{ + const TriangleMesh* mesh = &mv->mesh(); + + for (FacetSupportType type : {FacetSupportType::ENFORCER, FacetSupportType::BLOCKER}) { + if ((type == FacetSupportType::ENFORCER && ! update_enforcers) + || (type == FacetSupportType::BLOCKER && ! update_blockers)) + continue; + + GLIndexedVertexArray& iva = m_ivas[mesh_id][type==FacetSupportType::ENFORCER ? 0 : 1]; + iva.release_geometry(); + size_t triangle_cnt=0; + for (size_t facet_idx=0; facet_idxits.vertices[mesh->its.indices[facet_idx](i)].cast(), + MeshRaycaster::get_triangle_normal(mesh->its, facet_idx).cast()); + iva.push_triangle(3*triangle_cnt, 3*triangle_cnt+1, 3*triangle_cnt+2); + ++triangle_cnt; + } + if (! m_selected_facets[mesh_id].empty()) + iva.finalize_geometry(true); + } +} + + +void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_limit) +{ + if (! m_c->selection_info()->model_object()) + return; + + const float approx_height = m_imgui->scaled(18.0f); + y = std::min(y, bottom_limit - approx_height); + m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + + // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: + const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); + const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); + const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); + const float minimal_slider_width = m_imgui->scaled(4.f); + + float caption_max = 0.f; + float total_text_max = 0.; + for (const std::string& t : {"enforce", "block", "remove"}) { + caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc.at(t+"_caption")).x); + total_text_max = std::max(total_text_max, caption_max + m_imgui->calc_text_size(m_desc.at(t)).x); + } + caption_max += m_imgui->scaled(1.f); + total_text_max += m_imgui->scaled(1.f); + + float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left); + window_width = std::max(window_width, total_text_max); + window_width = std::max(window_width, button_width); + + auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { + static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + m_imgui->text(caption); + ImGui::PopStyleColor(); + ImGui::SameLine(caption_max); + m_imgui->text(text); + }; + + for (const std::string& t : {"enforce", "block", "remove"}) + draw_text_with_caption(m_desc.at(t + "_caption"), m_desc.at(t)); + + m_imgui->text(""); + + if (m_imgui->button(m_desc.at("remove_all"))) { + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + ++idx; + if (mv->is_model_part()) { + m_selected_facets[idx].assign(m_selected_facets[idx].size(), FacetSupportType::NONE); + mv->m_supported_facets.clear(); + update_vertex_buffers(mv, idx, true, true); + m_parent.set_as_dirty(); + } + } + } + + const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; + + m_imgui->text(m_desc.at("cursor_size")); + ImGui::SameLine(clipping_slider_left); + ImGui::PushItemWidth(window_width - clipping_slider_left); + ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f"); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Alt + Mouse wheel").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + ImGui::Separator(); + if (m_c->object_clipper()->get_position() == 0.f) + m_imgui->text(m_desc.at("clipping_of_view")); + else { + if (m_imgui->button(m_desc.at("reset_direction"))) { + wxGetApp().CallAfter([this](){ + m_c->object_clipper()->set_position(-1., false); + }); + } + } + + ImGui::SameLine(clipping_slider_left); + ImGui::PushItemWidth(window_width - clipping_slider_left); + float clp_dist = m_c->object_clipper()->get_position(); + if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f")) + m_c->object_clipper()->set_position(clp_dist, true); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(max_tooltip_width); + ImGui::TextUnformatted(_L("Ctrl + Mouse wheel").ToUTF8().data()); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } + + + + m_imgui->end(); +} + +bool GLGizmoFdmSupports::on_is_activable() const +{ + const Selection& selection = m_parent.get_selection(); + + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptFFF + || !selection.is_single_full_instance()) + return false; + + // Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside. + const Selection::IndicesList& list = selection.get_volume_idxs(); + for (const auto& idx : list) + if (selection.get_volume(idx)->is_outside) + return false; + + return true; +} + +bool GLGizmoFdmSupports::on_is_selectable() const +{ + return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF ); +} + +std::string GLGizmoFdmSupports::on_get_name() const +{ + return (_(L("FDM Support Editing")) + " [L]").ToUTF8().data(); +} + + +CommonGizmosDataID GLGizmoFdmSupports::on_get_requirements() const +{ + return CommonGizmosDataID( + int(CommonGizmosDataID::SelectionInfo) + | int(CommonGizmosDataID::InstancesHider) + | int(CommonGizmosDataID::Raycaster) + | int(CommonGizmosDataID::HollowedMesh) + | int(CommonGizmosDataID::ObjectClipper) + | int(CommonGizmosDataID::SupportsClipper)); +} + + +void GLGizmoFdmSupports::on_set_state() +{ + if (m_state == m_old_state) + return; + + if (m_state == On && m_old_state != On) { // the gizmo was just turned on + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned on"))); + } + if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off + // we are actually shutting down + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("FDM gizmo turned off"))); + m_old_mo = nullptr; + m_ivas.clear(); + m_neighbors.clear(); + m_selected_facets.clear(); + } + m_old_state = m_state; +} + + + +void GLGizmoFdmSupports::on_start_dragging() +{ + +} + + +void GLGizmoFdmSupports::on_stop_dragging() +{ + +} + + + +void GLGizmoFdmSupports::on_load(cereal::BinaryInputArchive& ar) +{ + +} + + + +void GLGizmoFdmSupports::on_save(cereal::BinaryOutputArchive& ar) const +{ + +} + + + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp new file mode 100644 index 0000000000..994218f698 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -0,0 +1,97 @@ +#ifndef slic3r_GLGizmoFdmSupports_hpp_ +#define slic3r_GLGizmoFdmSupports_hpp_ + +#include "GLGizmoBase.hpp" + +#include "slic3r/GUI/3DScene.hpp" + +#include + + +namespace Slic3r { + +enum class FacetSupportType : int8_t; + +namespace GUI { + +enum class SLAGizmoEventType : unsigned char; + +class GLGizmoFdmSupports : public GLGizmoBase +{ +private: + const ModelObject* m_old_mo = nullptr; + size_t m_old_volumes_size = 0; + + GLUquadricObj* m_quadric; + + float m_cursor_radius = 2.f; + static constexpr float CursorRadiusMin = 0.f; + static constexpr float CursorRadiusMax = 8.f; + static constexpr float CursorRadiusStep = 0.2f; + + // For each model-part volume, store a list of statuses of + // individual facets (one of the enum values above). + std::vector> m_selected_facets; + + // Store two vertex buffer arrays (for enforcers/blockers) + // for each model-part volume. + std::vector> m_ivas; + + void update_vertex_buffers(const ModelVolume* mv, + int mesh_id, + bool update_enforcers, + bool update_blockers); + +public: + GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + ~GLGizmoFdmSupports() override; + void set_fdm_support_data(ModelObject* model_object, const Selection& selection); + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + using NeighborData = std::pair; + + +private: + bool on_init() override; + void on_render() const override; + void on_render_for_picking() const override; + + void render_triangles(const Selection& selection) const; + void render_cursor_circle() const; + void update_mesh(); + + float m_clipping_plane_distance = 0.f; + std::unique_ptr m_clipping_plane; + + // This map holds all translated description texts, so they can be easily referenced during layout calculations + // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. + std::map m_desc; + + enum class Button { + None, + Left, + Right + }; + + Button m_button_down = Button::None; + EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) + + std::vector> m_neighbors; // pairs of vertex_index - facet_index for each mesh + +protected: + void on_set_state() override; + void on_start_dragging() override; + void on_stop_dragging() override; + void on_render_input_window(float x, float y, float bottom_limit) override; + std::string on_get_name() const override; + bool on_is_activable() const override; + bool on_is_selectable() const override; + void on_load(cereal::BinaryInputArchive& ar) override; + void on_save(cereal::BinaryOutputArchive& ar) const override; + CommonGizmosDataID on_get_requirements() const override; +}; + + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoFdmSupports_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 9fae8893ac..7f33916cd5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -2,6 +2,7 @@ #include "GLGizmoFlatten.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" #include @@ -26,20 +27,15 @@ bool GLGizmoFlatten::on_init() void GLGizmoFlatten::on_set_state() { - // m_model_object pointer can be invalid (for instance because of undo/redo action), - // we should recover it from the object id - m_model_object = nullptr; - for (const auto mo : wxGetApp().model().objects) { - if (mo->id() == m_model_object_id) { - m_model_object = mo; - break; - } - } - if (m_state == On && is_plane_update_necessary()) update_planes(); } +CommonGizmosDataID GLGizmoFlatten::on_get_requirements() const +{ + return CommonGizmosDataID::SelectionInfo; +} + std::string GLGizmoFlatten::on_get_name() const { return (_(L("Place on face")) + " [F]").ToUTF8().data(); @@ -132,18 +128,17 @@ void GLGizmoFlatten::on_render_for_picking() const void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) { m_starting_center = Vec3d::Zero(); - if (m_model_object != model_object) { + if (model_object != m_old_model_object) { m_planes.clear(); m_planes_valid = false; } - m_model_object = model_object; - m_model_object_id = model_object ? model_object->id() : 0; } void GLGizmoFlatten::update_planes() { + const ModelObject* mo = m_c->selection_info()->model_object(); TriangleMesh ch; - for (const ModelVolume* vol : m_model_object->volumes) + for (const ModelVolume* vol : mo->volumes) { if (vol->type() != ModelVolumeType::MODEL_PART) continue; @@ -153,7 +148,7 @@ void GLGizmoFlatten::update_planes() } ch = ch.convex_hull_3d(); m_planes.clear(); - const Transform3d& inst_matrix = m_model_object->instances.front()->get_matrix(true); + const Transform3d& inst_matrix = mo->instances.front()->get_matrix(true); // Following constants are used for discarding too small polygons. const float minimal_area = 5.f; // in square mm (world coordinates) @@ -331,12 +326,13 @@ void GLGizmoFlatten::update_planes() // Planes are finished - let's save what we calculated it from: m_volumes_matrices.clear(); m_volumes_types.clear(); - for (const ModelVolume* vol : m_model_object->volumes) { + for (const ModelVolume* vol : mo->volumes) { m_volumes_matrices.push_back(vol->get_matrix()); m_volumes_types.push_back(vol->type()); } - m_first_instance_scale = m_model_object->instances.front()->get_scaling_factor(); - m_first_instance_mirror = m_model_object->instances.front()->get_mirror(); + m_first_instance_scale = mo->instances.front()->get_scaling_factor(); + m_first_instance_mirror = mo->instances.front()->get_mirror(); + m_old_model_object = mo; m_planes_valid = true; } @@ -344,20 +340,22 @@ void GLGizmoFlatten::update_planes() bool GLGizmoFlatten::is_plane_update_necessary() const { - if (m_state != On || !m_model_object || m_model_object->instances.empty()) + const ModelObject* mo = m_c->selection_info()->model_object(); + if (m_state != On || ! mo || mo->instances.empty()) return false; - if (! m_planes_valid || m_model_object->volumes.size() != m_volumes_matrices.size()) + if (! m_planes_valid || mo != m_old_model_object + || mo->volumes.size() != m_volumes_matrices.size()) return true; // We want to recalculate when the scale changes - some planes could (dis)appear. - if (! m_model_object->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) - || ! m_model_object->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) + if (! mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) + || ! mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) return true; - for (unsigned int i=0; i < m_model_object->volumes.size(); ++i) - if (! m_model_object->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) - || m_model_object->volumes[i]->type() != m_volumes_types[i]) + for (unsigned int i=0; i < mo->volumes.size(); ++i) + if (! mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) + || mo->volumes[i]->type() != m_volumes_types[i]) return true; return false; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp index 9cd2ab6bb1..05b4ae4cdc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp @@ -30,8 +30,7 @@ private: std::vector m_planes; bool m_planes_valid = false; mutable Vec3d m_starting_center; - const ModelObject* m_model_object = nullptr; - ObjectID m_model_object_id = 0; + const ModelObject* m_old_model_object = nullptr; std::vector instances_matrices; void update_planes(); @@ -51,6 +50,7 @@ protected: virtual void on_render() const override; virtual void on_render_for_picking() const override; virtual void on_set_state() override; + virtual CommonGizmosDataID on_get_requirements() const override; }; } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 51e6d7458a..d2f668dab0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -1,21 +1,15 @@ #include "GLGizmoHollow.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/Camera.hpp" -#include "slic3r/GUI/Gizmos/GLGizmos.hpp" +#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" #include #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_ObjectSettings.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" -#include "slic3r/GUI/MeshUtils.hpp" #include "slic3r/GUI/Plater.hpp" -#if ENABLE_NON_STATIC_CANVAS_MANAGER -#include "slic3r/GUI/Camera.hpp" -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER #include "slic3r/GUI/PresetBundle.hpp" -#include "libslic3r/SLAPrint.hpp" -#include "libslic3r/TriangleMesh.hpp" namespace Slic3r { @@ -59,32 +53,14 @@ bool GLGizmoHollow::on_init() void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&) { - if (m_c->recent_update) { + if (! m_c->selection_info()) + return; - if (m_state == On) - m_c->build_AABB_if_needed(); - - update_clipping_plane(m_c->m_clipping_plane_was_moved); - - // This is a temporary and not very nice hack, to make sure that - // if the cp was moved by the data returned by backend, it will - // remember its direction. FIXME: Refactor this mess and make - // the clipping plane itself part of the shared data. - if (! m_c->m_clipping_plane_was_moved && m_c->m_clipping_plane_distance == 0.25f) - m_c->m_clipping_plane_was_moved = true; - - - if (m_c->m_model_object) { - reload_cache(); - if (m_c->has_drilled_mesh()) - m_holes_in_drilled_mesh = m_c->m_model_object->sla_drain_holes; - } - } - - if (m_state == On) { - m_parent.toggle_model_objects_visibility(false); - m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); - m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); + const ModelObject* mo = m_c->selection_info()->model_object(); + if (mo) { + reload_cache(); + if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh()) + m_holes_in_drilled_mesh = mo->sla_drain_holes; } } @@ -93,12 +69,12 @@ void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&) void GLGizmoHollow::on_render() const { const Selection& selection = m_parent.get_selection(); + const CommonGizmosDataObjects::SelectionInfo* sel_info = m_c->selection_info(); // If current m_c->m_model_object does not match selection, ask GLCanvas3D to turn us off if (m_state == On - && (m_c->m_model_object != selection.get_model()->objects[selection.get_object_idx()] - || m_c->m_active_instance != selection.get_instance_idx() - || m_c->m_model_object_id != m_c->m_model_object->id())) { + && (sel_info->model_object() != selection.get_model()->objects[selection.get_object_idx()] + || sel_info->get_active_instance() != selection.get_instance_idx())) { m_parent.post_event(SimpleEvent(EVT_GLCANVAS_RESETGIZMOS)); return; } @@ -106,92 +82,17 @@ void GLGizmoHollow::on_render() const glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); - m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); - if (m_quadric != nullptr && selection.is_from_single_instance()) render_points(selection, false); m_selection_rectangle.render(m_parent); - render_clipping_plane(selection); + m_c->object_clipper()->render_cut(); + m_c->supports_clipper()->render_cut(); glsafe(::glDisable(GL_BLEND)); } - -void GLGizmoHollow::render_clipping_plane(const Selection& selection) const -{ - if (m_c->m_clipping_plane_distance == 0.f) - return; - - // Get transformation of the instance - const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); - Geometry::Transformation trafo = vol->get_instance_transformation(); - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); - - // Get transformation of supports - Geometry::Transformation supports_trafo; - supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), vol->get_sla_shift_z())); - supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2))); - // I don't know why, but following seems to be correct. - supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2), - 1, - 1.)); - - // Now initialize the TMS for the object, perform the cut and save the result. - if (! m_c->m_object_clipper) { - m_c->m_object_clipper.reset(new MeshClipper); - m_c->m_object_clipper->set_mesh(*m_c->mesh()); - } - m_c->m_object_clipper->set_plane(*m_c->m_clipping_plane); - m_c->m_object_clipper->set_transformation(trafo); - - if (m_c->m_print_object_idx >= 0) { - const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_c->m_print_object_idx]; - - if (print_object->is_step_done(slaposSupportTree) && !print_object->get_mesh(slaposSupportTree).empty()) { - // If the supports are already calculated, save the timestamp of the respective step - // so we can later tell they were recalculated. - size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp; - - if (! m_c->m_supports_clipper || (int)timestamp != m_c->m_old_timestamp) { - // The timestamp has changed. - m_c->m_supports_clipper.reset(new MeshClipper); - // The mesh should already have the shared vertices calculated. - m_c->m_supports_clipper->set_mesh(print_object->support_mesh()); - m_c->m_old_timestamp = timestamp; - } - m_c->m_supports_clipper->set_plane(*m_c->m_clipping_plane); - m_c->m_supports_clipper->set_transformation(supports_trafo); - } - else - // The supports are not valid. We better dump the cached data. - m_c->m_supports_clipper.reset(); - } - - // At this point we have the triangulated cuts for both the object and supports - let's render. - if (! m_c->m_object_clipper->get_triangles().empty()) { - ::glPushMatrix(); - ::glColor3f(1.0f, 0.37f, 0.0f); - ::glBegin(GL_TRIANGLES); - for (const Vec3f& point : m_c->m_object_clipper->get_triangles()) - ::glVertex3f(point(0), point(1), point(2)); - ::glEnd(); - ::glPopMatrix(); - } - - if (m_show_supports && m_c->m_supports_clipper && ! m_c->m_supports_clipper->get_triangles().empty()) { - ::glPushMatrix(); - ::glColor3f(1.0f, 0.f, 0.37f); - ::glBegin(GL_TRIANGLES); - for (const Vec3f& point : m_c->m_supports_clipper->get_triangles()) - ::glVertex3f(point(0), point(1), point(2)); - ::glEnd(); - ::glPopMatrix(); - } -} - - void GLGizmoHollow::on_render_for_picking() const { const Selection& selection = m_parent.get_selection(); @@ -213,17 +114,18 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); glsafe(::glPushMatrix()); - glsafe(::glTranslated(0.0, 0.0, m_z_shift)); + glsafe(::glTranslated(0.0, 0.0, m_c->selection_info()->get_sla_shift())); glsafe(::glMultMatrixd(instance_matrix.data())); float render_color[4]; - size_t cache_size = m_c->m_model_object->sla_drain_holes.size(); + const sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes; + size_t cache_size = drain_holes.size(); for (size_t i = 0; i < cache_size; ++i) { - const sla::DrainHole& drain_hole = m_c->m_model_object->sla_drain_holes[i]; + const sla::DrainHole& drain_hole = drain_holes[i]; const bool& point_selected = m_selected[i]; - if (is_mesh_point_clipped((drain_hole.pos+m_c->HoleStickOutLength*drain_hole.normal).cast())) + if (is_mesh_point_clipped((drain_hole.pos+HoleStickOutLength*drain_hole.normal).cast())) continue; // First decide about the color of the point. @@ -261,7 +163,6 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons glFrontFace(GL_CW); // Matrices set, we can render the point mark now. - Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); Eigen::AngleAxisd aa(q); @@ -297,12 +198,17 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const { - if (m_c->m_clipping_plane_distance == 0.f) + if (m_c->object_clipper()->get_position() == 0.) return false; - Vec3d transformed_point = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation().get_matrix() * point; - transformed_point(2) += m_z_shift; - return m_c->m_clipping_plane->is_point_clipped(transformed_point); + auto sel_info = m_c->selection_info(); + int active_inst = m_c->selection_info()->get_active_instance(); + const ModelInstance* mi = sel_info->model_object()->instances[active_inst]; + const Transform3d& trafo = mi->get_transformation().get_matrix(); + + Vec3d transformed_point = trafo * point; + transformed_point(2) += sel_info->get_sla_shift(); + return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point); } @@ -311,7 +217,7 @@ bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const // Return false if no intersection was found, true otherwise. bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) { - if (! m_c->m_mesh_raycaster) + if (! m_c->raycaster()->raycaster()) return false; #if ENABLE_NON_STATIC_CANVAS_MANAGER @@ -322,20 +228,23 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pairget_instance_transformation(); - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift())); + + double clp_dist = m_c->object_clipper()->get_position(); + const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane(); // The raycaster query Vec3f hit; Vec3f normal; - if (m_c->m_mesh_raycaster->unproject_on_mesh( + if (m_c->raycaster()->raycaster()->unproject_on_mesh( mouse_pos, trafo.get_matrix(), camera, hit, normal, - m_c->m_clipping_plane_distance != 0.f ? m_c->m_clipping_plane.get() : nullptr)) + clp_dist != 0. ? clp : nullptr)) { - if (m_c->has_drilled_mesh()) { + if (m_c->hollowed_mesh() && m_c->hollowed_mesh()->get_hollowed_mesh()) { // in this case the raycaster sees the hollowed and drilled mesh. // if the point lies on the surface created by the hole, we want // to ignore it. @@ -362,6 +271,10 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pairselection_info()->model_object(); + int active_inst = m_c->selection_info()->get_active_instance(); + + // left down with shift - show the selection rectangle: if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) { if (m_hover_id == -1) { @@ -393,15 +306,15 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole"))); - Vec3d scaling = m_c->m_model_object->instances[m_c->m_active_instance]->get_scaling_factor(); + Vec3d scaling = mo->instances[active_inst]->get_scaling_factor(); Vec3f normal_transformed(pos_and_normal.second(0)/scaling(0), pos_and_normal.second(1)/scaling(1), pos_and_normal.second(2)/scaling(2)); - m_c->m_model_object->sla_drain_holes.emplace_back(pos_and_normal.first + m_c->HoleStickOutLength * pos_and_normal.second/* normal_transformed.normalized()*/, + mo->sla_drain_holes.emplace_back(pos_and_normal.first + HoleStickOutLength * pos_and_normal.second/* normal_transformed.normalized()*/, -pos_and_normal.second, m_new_hole_radius, m_new_hole_height); m_selected.push_back(false); - assert(m_selected.size() == m_c->m_model_object->sla_drain_holes.size()); + assert(m_selected.size() == mo->sla_drain_holes.size()); m_parent.set_as_dirty(); m_wait_for_up_event = true; } @@ -420,11 +333,11 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); // First collect positions of all the points in world coordinates. - Geometry::Transformation trafo = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation(); - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + Geometry::Transformation trafo = mo->instances[active_inst]->get_transformation(); + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift())); std::vector points; - for (unsigned int i=0; im_model_object->sla_drain_holes.size(); ++i) - points.push_back(trafo.get_matrix() * m_c->m_model_object->sla_drain_holes[i].pos.cast()); + for (unsigned int i=0; isla_drain_holes.size(); ++i) + points.push_back(trafo.get_matrix() * mo->sla_drain_holes[i].pos.cast()); // Now ask the rectangle which of the points are inside. std::vector points_inside; @@ -433,11 +346,9 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos points_inside.push_back(points[idx].cast()); // Only select/deselect points that are actually visible -#if ENABLE_NON_STATIC_CANVAS_MANAGER - for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, wxGetApp().plater()->get_camera(), points_inside, m_c->m_clipping_plane.get())) -#else - for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_c->m_clipping_plane.get())) -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER + for (size_t idx : m_c->raycaster()->raycaster()->get_unobscured_idxs( + trafo, wxGetApp().plater()->get_camera(), points_inside, + m_c->object_clipper()->get_clipping_plane())) { if (rectangle_status == GLSelectionRectangle::Deselect) unselect_point(points_idxs[idx]); @@ -491,20 +402,21 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos } if (action == SLAGizmoEventType::MouseWheelUp && control_down) { - m_c->m_clipping_plane_distance = std::min(1.f, m_c->m_clipping_plane_distance + 0.01f); - update_clipping_plane(m_c->m_clipping_plane_was_moved); - m_c->m_clipping_plane_was_moved = true; + double pos = m_c->object_clipper()->get_position(); + pos = std::min(1., pos + 0.01); + m_c->object_clipper()->set_position(pos, true); return true; } if (action == SLAGizmoEventType::MouseWheelDown && control_down) { - m_c->m_clipping_plane_distance = std::max(0.f, m_c->m_clipping_plane_distance - 0.01f); - update_clipping_plane(true); + double pos = m_c->object_clipper()->get_position(); + pos = std::max(0., pos - 0.01); + m_c->object_clipper()->set_position(pos, true); return true; } if (action == SLAGizmoEventType::ResetClippingPlane) { - update_clipping_plane(); + m_c->object_clipper()->set_position(-1., false); return true; } @@ -514,11 +426,12 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos void GLGizmoHollow::delete_selected_points() { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete drainage hole"))); + sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes; - for (unsigned int idx=0; idxm_model_object->sla_drain_holes.size(); ++idx) { + for (unsigned int idx=0; idxm_model_object->sla_drain_holes.erase(m_c->m_model_object->sla_drain_holes.begin() + (idx--)); + drain_holes.erase(drain_holes.begin() + (idx--)); } } @@ -527,12 +440,14 @@ void GLGizmoHollow::delete_selected_points() void GLGizmoHollow::on_update(const UpdateData& data) { + sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes; + if (m_hover_id != -1) { std::pair pos_and_normal; if (! unproject_on_mesh(data.mouse_pos.cast(), pos_and_normal)) return; - m_c->m_model_object->sla_drain_holes[m_hover_id].pos = pos_and_normal.first + m_c->HoleStickOutLength * pos_and_normal.second; - m_c->m_model_object->sla_drain_holes[m_hover_id].normal = -pos_and_normal.second; + drain_holes[m_hover_id].pos = pos_and_normal.first + HoleStickOutLength * pos_and_normal.second; + drain_holes[m_hover_id].normal = -pos_and_normal.second; } } @@ -540,19 +455,22 @@ void GLGizmoHollow::on_update(const UpdateData& data) void GLGizmoHollow::hollow_mesh(bool postpone_error_messages) { wxGetApp().CallAfter([this, postpone_error_messages]() { - wxGetApp().plater()->reslice_SLA_hollowing(*m_c->m_model_object, postpone_error_messages); + wxGetApp().plater()->reslice_SLA_hollowing( + *m_c->selection_info()->model_object(), postpone_error_messages); }); } -std::vector> GLGizmoHollow::get_config_options(const std::vector& keys) const +std::vector> +GLGizmoHollow::get_config_options(const std::vector& keys) const { std::vector> out; + const ModelObject* mo = m_c->selection_info()->model_object(); - if (!m_c->m_model_object) + if (! mo) return out; - const DynamicPrintConfig& object_cfg = m_c->m_model_object->config; + const DynamicPrintConfig& object_cfg = mo->config; const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; std::unique_ptr default_cfg = nullptr; @@ -573,18 +491,10 @@ std::vector> GLGizmoHollo } -ClippingPlane GLGizmoHollow::get_sla_clipping_plane() const -{ - if (!m_c->m_model_object || m_state == Off || m_c->m_clipping_plane_distance == 0.f) - return ClippingPlane::ClipsNothing(); - else - return ClippingPlane(-m_c->m_clipping_plane->get_normal(), m_c->m_clipping_plane->get_data()[3]); -} - - void GLGizmoHollow::on_render_input_window(float x, float y, float bottom_limit) { - if (! m_c->m_model_object) + ModelObject* mo = m_c->selection_info()->model_object(); + if (! mo) return; bool first_run = true; // This is a hack to redraw the button when all points are removed, @@ -650,7 +560,7 @@ RENDER_AGAIN: auto opts = get_config_options({"hollowing_enable"}); m_enable_hollowing = static_cast(opts[0].first)->value; if (m_imgui->checkbox(m_desc["enable"], m_enable_hollowing)) { - m_c->m_model_object->config.opt("hollowing_enable", true)->value = m_enable_hollowing; + mo->config.opt("hollowing_enable", true)->value = m_enable_hollowing; wxGetApp().obj_list()->update_and_show_object_settings_item(); config_changed = true; } @@ -712,14 +622,14 @@ RENDER_AGAIN: } if (slider_edited || slider_released) { if (slider_released) { - m_c->m_model_object->config.opt("hollowing_min_thickness", true)->value = m_offset_stash; - m_c->m_model_object->config.opt("hollowing_quality", true)->value = m_quality_stash; - m_c->m_model_object->config.opt("hollowing_closing_distance", true)->value = m_closing_d_stash; + mo->config.opt("hollowing_min_thickness", true)->value = m_offset_stash; + mo->config.opt("hollowing_quality", true)->value = m_quality_stash; + mo->config.opt("hollowing_closing_distance", true)->value = m_closing_d_stash; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Hollowing parameter change"))); } - m_c->m_model_object->config.opt("hollowing_min_thickness", true)->value = offset; - m_c->m_model_object->config.opt("hollowing_quality", true)->value = quality; - m_c->m_model_object->config.opt("hollowing_closing_distance", true)->value = closing_d; + mo->config.opt("hollowing_min_thickness", true)->value = offset; + mo->config.opt("hollowing_quality", true)->value = quality; + mo->config.opt("hollowing_closing_distance", true)->value = closing_d; if (slider_released) { wxGetApp().obj_list()->update_and_show_object_settings_item(); config_changed = true; @@ -750,9 +660,9 @@ RENDER_AGAIN: m_imgui->text(m_desc["hole_depth"]); ImGui::SameLine(diameter_slider_left); - m_new_hole_height -= m_c->HoleStickOutLength; + m_new_hole_height -= HoleStickOutLength; ImGui::SliderFloat(" ", &m_new_hole_height, 0.f, 10.f, "%.1f mm"); - m_new_hole_height += m_c->HoleStickOutLength; + m_new_hole_height += HoleStickOutLength; clicked |= ImGui::IsItemClicked(); edited |= ImGui::IsItemEdited(); @@ -764,19 +674,19 @@ RENDER_AGAIN: // - take correct undo/redo snapshot after the user is done with moving the slider if (! m_selection_empty) { if (clicked) { - m_holes_stash = m_c->m_model_object->sla_drain_holes; + m_holes_stash = mo->sla_drain_holes; } if (edited) { for (size_t idx=0; idxm_model_object->sla_drain_holes[idx].radius = m_new_hole_radius; - m_c->m_model_object->sla_drain_holes[idx].height = m_new_hole_height; + mo->sla_drain_holes[idx].radius = m_new_hole_radius; + mo->sla_drain_holes[idx].height = m_new_hole_height; } } if (deactivated) { // momentarily restore the old value to take snapshot - sla::DrainHoles new_holes = m_c->m_model_object->sla_drain_holes; - m_c->m_model_object->sla_drain_holes = m_holes_stash; + sla::DrainHoles new_holes = mo->sla_drain_holes; + mo->sla_drain_holes = m_holes_stash; float backup_rad = m_new_hole_radius; float backup_hei = m_new_hole_height; for (size_t i=0; im_model_object->sla_drain_holes = new_holes; + mo->sla_drain_holes = new_holes; } } @@ -797,33 +707,33 @@ RENDER_AGAIN: remove_selected = m_imgui->button(m_desc.at("remove_selected")); m_imgui->disabled_end(); - m_imgui->disabled_begin(m_c->m_model_object->sla_drain_holes.empty()); + m_imgui->disabled_begin(mo->sla_drain_holes.empty()); remove_all = m_imgui->button(m_desc.at("remove_all")); m_imgui->disabled_end(); // Following is rendered in both editing and non-editing mode: // m_imgui->text(""); ImGui::Separator(); - if (m_c->m_clipping_plane_distance == 0.f) + if (m_c->object_clipper()->get_position() == 0.f) m_imgui->text(m_desc.at("clipping_of_view")); else { if (m_imgui->button(m_desc.at("reset_direction"))) { wxGetApp().CallAfter([this](){ - update_clipping_plane(); + m_c->object_clipper()->set_position(-1., false); }); } } ImGui::SameLine(clipping_slider_left); ImGui::PushItemWidth(window_width - clipping_slider_left); - if (ImGui::SliderFloat(" ", &m_c->m_clipping_plane_distance, 0.f, 1.f, "%.2f")) { - update_clipping_plane(m_c->m_clipping_plane_was_moved); - m_c->m_clipping_plane_was_moved = true; - } + float clp_dist = m_c->object_clipper()->get_position(); + if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f")) + m_c->object_clipper()->set_position(clp_dist, true); // make sure supports are shown/hidden as appropriate - if (m_imgui->checkbox(m_desc["show_supports"], m_show_supports)) { - m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); + bool show_sups = m_c->instances_hider()->are_supports_shown(); + if (m_imgui->checkbox(m_desc["show_supports"], show_sups)) { + m_c->instances_hider()->show_supports(show_sups); force_refresh = true; } @@ -882,54 +792,30 @@ std::string GLGizmoHollow::on_get_name() const } +CommonGizmosDataID GLGizmoHollow::on_get_requirements() const +{ + return CommonGizmosDataID( + int(CommonGizmosDataID::SelectionInfo) + | int(CommonGizmosDataID::InstancesHider) + | int(CommonGizmosDataID::Raycaster) + | int(CommonGizmosDataID::HollowedMesh) + | int(CommonGizmosDataID::ObjectClipper) + | int(CommonGizmosDataID::SupportsClipper)); +} + void GLGizmoHollow::on_set_state() { - // m_c->m_model_object pointer can be invalid (for instance because of undo/redo action), - // we should recover it from the object id - m_c->m_model_object = nullptr; - for (const auto mo : wxGetApp().model().objects) { - if (mo->id() == m_c->m_model_object_id) { - m_c->m_model_object = mo; - break; - } - } - if (m_state == m_old_state) return; if (m_state == On && m_old_state != On) { // the gizmo was just turned on - //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); - //m_c->update_from_backend(m_parent, m_c->m_model_object); - m_c->unstash_clipping_plane(); - update_clipping_plane(m_c->m_clipping_plane_was_moved); - - m_c->build_AABB_if_needed(); - // we'll now reload support points: - if (m_c->m_model_object) + if (m_c->selection_info()->model_object()) reload_cache(); - - m_parent.toggle_model_objects_visibility(false); - if (m_c->m_model_object) { - m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); - m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); - } } - if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off - //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); + if (m_state == Off && m_old_state != Off) // the gizmo was just turned Off m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE)); - m_parent.toggle_model_objects_visibility(true); - m_c->stash_clipping_plane(); - m_c->m_clipping_plane_distance = 0.f; - update_clipping_plane(true); - // Release clippers and the AABB raycaster. - m_c->m_object_clipper.reset(); - m_c->m_supports_clipper.reset(); - //m_c->m_mesh_raycaster.reset(); - //m_c->m_cavity_mesh.reset(); - //m_c->m_volume_with_cavity.reset(); - } m_old_state = m_state; } @@ -940,7 +826,7 @@ void GLGizmoHollow::on_start_dragging() if (m_hover_id != -1) { select_point(NoPoints); select_point(m_hover_id); - m_hole_before_drag = m_c->m_model_object->sla_drain_holes[m_hover_id].pos; + m_hole_before_drag = m_c->selection_info()->model_object()->sla_drain_holes[m_hover_id].pos; } else m_hole_before_drag = Vec3f::Zero(); @@ -949,15 +835,16 @@ void GLGizmoHollow::on_start_dragging() void GLGizmoHollow::on_stop_dragging() { + sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes; if (m_hover_id != -1) { - Vec3f backup = m_c->m_model_object->sla_drain_holes[m_hover_id].pos; + Vec3f backup = drain_holes[m_hover_id].pos; if (m_hole_before_drag != Vec3f::Zero() // some point was touched && backup != m_hole_before_drag) // and it was moved, not just selected { - m_c->m_model_object->sla_drain_holes[m_hover_id].pos = m_hole_before_drag; + drain_holes[m_hover_id].pos = m_hole_before_drag; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move drainage hole"))); - m_c->m_model_object->sla_drain_holes[m_hover_id].pos = backup; + drain_holes[m_hover_id].pos = backup; } } m_hole_before_drag = Vec3f::Zero(); @@ -967,10 +854,7 @@ void GLGizmoHollow::on_stop_dragging() void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar) { - ar(m_c->m_clipping_plane_distance, - *m_c->m_clipping_plane, - m_c->m_model_object_id, - m_new_hole_radius, + ar(m_new_hole_radius, m_new_hole_height, m_selected, m_selection_empty @@ -981,10 +865,7 @@ void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar) void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const { - ar(m_c->m_clipping_plane_distance, - *m_c->m_clipping_plane, - m_c->m_model_object_id, - m_new_hole_radius, + ar(m_new_hole_radius, m_new_hole_height, m_selected, m_selection_empty @@ -995,13 +876,15 @@ void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const void GLGizmoHollow::select_point(int i) { + const sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes; + if (i == AllPoints || i == NoPoints) { m_selected.assign(m_selected.size(), i == AllPoints); m_selection_empty = (i == NoPoints); if (i == AllPoints) { - m_new_hole_radius = m_c->m_model_object->sla_drain_holes[0].radius; - m_new_hole_height = m_c->m_model_object->sla_drain_holes[0].height; + m_new_hole_radius = drain_holes[0].radius; + m_new_hole_height = drain_holes[0].height; } } else { @@ -1009,8 +892,8 @@ void GLGizmoHollow::select_point(int i) m_selected.push_back(false); m_selected[i] = true; m_selection_empty = false; - m_new_hole_radius = m_c->m_model_object->sla_drain_holes[i].radius; - m_new_hole_height = m_c->m_model_object->sla_drain_holes[i].height; + m_new_hole_radius = drain_holes[i].radius; + m_new_hole_height = drain_holes[i].height; } } @@ -1030,31 +913,13 @@ void GLGizmoHollow::unselect_point(int i) void GLGizmoHollow::reload_cache() { m_selected.clear(); - m_selected.assign(m_c->m_model_object->sla_drain_holes.size(), false); -} - -void GLGizmoHollow::update_clipping_plane(bool keep_normal) const -{ - if (! m_c->m_model_object) - return; -#if ENABLE_NON_STATIC_CANVAS_MANAGER - Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ? - m_c->m_clipping_plane->get_normal() : -wxGetApp().plater()->get_camera().get_dir_forward()); -#else - Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ? - m_c->m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - - const Vec3d& center = m_c->m_model_object->instances[m_c->m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift); - float dist = normal.dot(center); - *m_c->m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_c->m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius)); - m_parent.set_as_dirty(); + m_selected.assign(m_c->selection_info()->model_object()->sla_drain_holes.size(), false); } void GLGizmoHollow::on_set_hover_id() { - if (int(m_c->m_model_object->sla_drain_holes.size()) <= m_hover_id) + if (int(m_c->selection_info()->model_object()->sla_drain_holes.size()) <= m_hover_id) m_hover_id = -1; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 3aafcbf557..3ee83c3454 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -13,16 +13,11 @@ namespace Slic3r { namespace GUI { -class ClippingPlane; -class MeshClipper; -class MeshRaycaster; -class CommonGizmosData; enum class SLAGizmoEventType : unsigned char; class GLGizmoHollow : public GLGizmoBase { private: - mutable double m_z_shift = 0.; bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); GLUquadricObj* m_quadric; @@ -33,12 +28,10 @@ public: ~GLGizmoHollow() override; void set_sla_support_data(ModelObject* model_object, const Selection& selection); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); - void delete_selected_points(); - ClippingPlane get_sla_clipping_plane() const; - - bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); } - void update_clipping_plane(bool keep_normal = false) const; - void set_common_data_ptr(CommonGizmosData* ptr) { m_c = ptr; } + void delete_selected_points(); + bool is_selection_rectangle_dragging() const { + return m_selection_rectangle.is_dragging(); + } private: bool on_init() override; @@ -47,11 +40,10 @@ private: void on_render_for_picking() const override; void render_points(const Selection& selection, bool picking = false) const; - void render_clipping_plane(const Selection& selection) const; void hollow_mesh(bool postpone_error_messages = false); bool unsaved_changes() const; - bool m_show_supports = true; + // bool m_show_supports = true; float m_new_hole_radius = 2.f; // Size of a new hole. float m_new_hole_height = 6.f; mutable std::vector m_selected; // which holes are currently selected @@ -67,10 +59,6 @@ private: sla::DrainHoles m_holes_in_drilled_mesh; sla::DrainHoles m_holes_stash; - - CommonGizmosData* m_c = nullptr; - - //std::unique_ptr m_clipping_plane; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. @@ -101,6 +89,7 @@ protected: void on_start_dragging() override; void on_stop_dragging() override; void on_render_input_window(float x, float y, float bottom_limit) override; + virtual CommonGizmosDataID on_get_requirements() const override; std::string on_get_name() const override; bool on_is_activable() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 064302c020..496568d51a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -2,7 +2,7 @@ #include "GLGizmoSlaSupports.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/Camera.hpp" -#include "slic3r/GUI/Gizmos/GLGizmos.hpp" +#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" #include @@ -14,10 +14,6 @@ #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI_ObjectSettings.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" -#if ENABLE_NON_STATIC_CANVAS_MANAGER -#include "slic3r/GUI/Camera.hpp" -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER -#include "slic3r/GUI/MeshUtils.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/PresetBundle.hpp" #include "libslic3r/SLAPrint.hpp" @@ -29,7 +25,6 @@ namespace GUI { GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) , m_quadric(nullptr) - , m_its(nullptr) { m_quadric = ::gluNewQuadric(); if (m_quadric != nullptr) @@ -66,26 +61,21 @@ bool GLGizmoSlaSupports::on_init() void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const Selection& selection) { - if (m_c->recent_update) { - if (m_state == On) - m_c->build_AABB_if_needed(); + if (! m_c->selection_info()) + return; - update_clipping_plane(m_c->m_clipping_plane_was_moved); + ModelObject* mo = m_c->selection_info()->model_object(); + if (mo != m_old_mo) { disable_editing_mode(); - if (m_c->m_model_object) + if (mo) reload_cache(); - } - - if (m_state == On) { - m_parent.toggle_model_objects_visibility(false); - m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); - m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); + m_old_mo = mo; } // If we triggered autogeneration before, check backend and fetch results if they are there - if (m_c->m_model_object) { - if (m_c->m_model_object->sla_points_status == sla::PointsStatus::Generating) + if (mo) { + if (mo->sla_points_status == sla::PointsStatus::Generating) get_data_from_backend(); } } @@ -94,13 +84,13 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S void GLGizmoSlaSupports::on_render() const { + ModelObject* mo = m_c->selection_info()->model_object(); const Selection& selection = m_parent.get_selection(); // If current m_c->m_model_object does not match selection, ask GLCanvas3D to turn us off if (m_state == On - && (m_c->m_model_object != selection.get_model()->objects[selection.get_object_idx()] - || m_c->m_active_instance != selection.get_instance_idx() - || m_c->m_model_object_id != m_c->m_model_object->id())) { + && (mo != selection.get_model()->objects[selection.get_object_idx()] + || m_c->selection_info()->get_active_instance() != selection.get_instance_idx())) { m_parent.post_event(SimpleEvent(EVT_GLCANVAS_RESETGIZMOS)); return; } @@ -108,113 +98,20 @@ void GLGizmoSlaSupports::on_render() const glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); - m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); - if (m_quadric != nullptr && selection.is_from_single_instance()) render_points(selection, false); m_selection_rectangle.render(m_parent); - render_clipping_plane(selection); + m_c->object_clipper()->render_cut(); + m_c->supports_clipper()->render_cut(); glsafe(::glDisable(GL_BLEND)); } - -void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const -{ - if (m_c->m_clipping_plane_distance == 0.f || m_c->m_mesh->empty()) - return; - - // Get transformation of the instance - const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); - Geometry::Transformation trafo = vol->get_instance_transformation(); - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); - - // Get transformation of supports - Geometry::Transformation supports_trafo; - supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), vol->get_sla_shift_z())); - supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2))); - // I don't know why, but following seems to be correct. - supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2), - 1, - 1.)); - - // Now initialize the TMS for the object, perform the cut and save the result. - if (! m_c->m_object_clipper) { - m_c->m_object_clipper.reset(new MeshClipper); - m_c->m_object_clipper->set_mesh(*m_c->mesh()); - } - m_c->m_object_clipper->set_plane(*m_c->m_clipping_plane); - m_c->m_object_clipper->set_transformation(trafo); - - - // Next, ask the backend if supports are already calculated. If so, we are gonna cut them too. - // First we need a pointer to the respective SLAPrintObject. The index into objects vector is - // cached so we don't have todo it on each render. We only search for the po if needed: - if (m_c->m_print_object_idx < 0 || (int)m_parent.sla_print()->objects().size() != m_c->m_print_objects_count) { - m_c->m_print_objects_count = m_parent.sla_print()->objects().size(); - m_c->m_print_object_idx = -1; - for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { - ++m_c->m_print_object_idx; - if (po->model_object()->id() == m_c->m_model_object->id()) - break; - } - } - if (m_c->m_print_object_idx >= 0) { - const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_c->m_print_object_idx]; - - if (print_object->is_step_done(slaposSupportTree) && !print_object->get_mesh(slaposSupportTree).empty()) { - // If the supports are already calculated, save the timestamp of the respective step - // so we can later tell they were recalculated. - size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp; - - if (! m_c->m_supports_clipper || (int)timestamp != m_c->m_old_timestamp) { - // The timestamp has changed. - m_c->m_supports_clipper.reset(new MeshClipper); - // The mesh should already have the shared vertices calculated. - m_c->m_supports_clipper->set_mesh(print_object->support_mesh()); - m_c->m_old_timestamp = timestamp; - } - m_c->m_supports_clipper->set_plane(*m_c->m_clipping_plane); - m_c->m_supports_clipper->set_transformation(supports_trafo); - } - else - // The supports are not valid. We better dump the cached data. - m_c->m_supports_clipper.reset(); - } - - // At this point we have the triangulated cuts for both the object and supports - let's render. - if (! m_c->m_object_clipper->get_triangles().empty()) { - ::glPushMatrix(); - ::glColor3f(1.0f, 0.37f, 0.0f); - ::glBegin(GL_TRIANGLES); - for (const Vec3f& point : m_c->m_object_clipper->get_triangles()) - ::glVertex3f(point(0), point(1), point(2)); - ::glEnd(); - ::glPopMatrix(); - } - - if (m_c->m_supports_clipper && ! m_c->m_supports_clipper->get_triangles().empty() && !m_editing_mode) { - // The supports are hidden in the editing mode, so it makes no sense to render the cuts. - ::glPushMatrix(); - ::glColor3f(1.0f, 0.f, 0.37f); - ::glBegin(GL_TRIANGLES); - for (const Vec3f& point : m_c->m_supports_clipper->get_triangles()) - ::glVertex3f(point(0), point(1), point(2)); - ::glEnd(); - ::glPopMatrix(); - } -} - - void GLGizmoSlaSupports::on_render_for_picking() const { const Selection& selection = m_parent.get_selection(); -#if ENABLE_RENDER_PICKING_PASS - m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); -#endif - glsafe(::glEnable(GL_DEPTH_TEST)); render_points(selection, true); } @@ -227,9 +124,10 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); + float z_shift = m_c->selection_info()->get_sla_shift(); glsafe(::glPushMatrix()); - glsafe(::glTranslated(0.0, 0.0, m_z_shift)); + glsafe(::glTranslated(0.0, 0.0, z_shift)); glsafe(::glMultMatrixd(instance_matrix.data())); float render_color[4]; @@ -285,7 +183,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) if (m_editing_mode) { // in case the normal is not yet cached, find and cache it if (m_editing_cache[i].normal == Vec3f::Zero()) - m_c->m_mesh_raycaster->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); + m_c->raycaster()->raycaster()->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); @@ -315,14 +213,15 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) } // Now render the drain holes: - if (! m_c->has_drilled_mesh()) { + //if (! m_c->has_drilled_mesh()) { + if (! m_c->hollowed_mesh()->get_hollowed_mesh()) { render_color[0] = 0.7f; render_color[1] = 0.7f; render_color[2] = 0.7f; render_color[3] = 0.7f; glsafe(::glColor4fv(render_color)); - for (const sla::DrainHole& drain_hole : m_c->m_model_object->sla_drain_holes) { - if (is_mesh_point_clipped((drain_hole.pos+m_c->HoleStickOutLength*drain_hole.normal).cast())) + for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) { + if (is_mesh_point_clipped((drain_hole.pos+HoleStickOutLength*drain_hole.normal).cast())) continue; // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. @@ -365,12 +264,17 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const { - if (m_c->m_clipping_plane_distance == 0.f) + if (m_c->object_clipper()->get_position() == 0.) return false; - Vec3d transformed_point = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation().get_matrix() * point; - transformed_point(2) += m_z_shift; - return m_c->m_clipping_plane->is_point_clipped(transformed_point); + auto sel_info = m_c->selection_info(); + int active_inst = m_c->selection_info()->get_active_instance(); + const ModelInstance* mi = sel_info->model_object()->instances[active_inst]; + const Transform3d& trafo = mi->get_transformation().get_matrix(); + + Vec3d transformed_point = trafo * point; + transformed_point(2) += sel_info->get_sla_shift(); + return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point); } @@ -379,37 +283,37 @@ bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const // Return false if no intersection was found, true otherwise. bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) { - if (! m_c->m_mesh_raycaster) + if (! m_c->raycaster()->raycaster()) return false; -#if ENABLE_NON_STATIC_CANVAS_MANAGER const Camera& camera = wxGetApp().plater()->get_camera(); -#else - const Camera& camera = m_parent.get_camera(); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER const Selection& selection = m_parent.get_selection(); const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); Geometry::Transformation trafo = volume->get_instance_transformation(); - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift())); + + double clp_dist = m_c->object_clipper()->get_position(); + const ClippingPlane* clp = m_c->object_clipper()->get_clipping_plane(); // The raycaster query Vec3f hit; Vec3f normal; - if (m_c->m_mesh_raycaster->unproject_on_mesh( + if (m_c->raycaster()->raycaster()->unproject_on_mesh( mouse_pos, trafo.get_matrix(), camera, hit, normal, - m_c->m_clipping_plane_distance != 0.f ? m_c->m_clipping_plane.get() : nullptr)) + clp_dist != 0. ? clp : nullptr)) { // Check whether the hit is in a hole bool in_hole = false; // In case the hollowed and drilled mesh is available, we can allow // placing points in holes, because they should never end up // on surface that's been drilled away. - if (! m_c->has_drilled_mesh()) { - for (const sla::DrainHole& hole : m_c->m_model_object->sla_drain_holes) { + if (! m_c->hollowed_mesh()->get_hollowed_mesh()) { + sla::DrainHoles drain_holes = m_c->selection_info()->model_object()->sla_drain_holes; + for (const sla::DrainHole& hole : drain_holes) { if (hole.is_inside(hit)) { in_hole = true; break; @@ -432,6 +336,9 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pairselection_info()->model_object(); + int active_inst = m_c->selection_info()->get_active_instance(); + if (m_editing_mode) { // left down with shift - show the selection rectangle: @@ -483,8 +390,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); // First collect positions of all the points in world coordinates. - Geometry::Transformation trafo = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation(); - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + Geometry::Transformation trafo = mo->instances[active_inst]->get_transformation(); + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift())); std::vector points; for (unsigned int i=0; i()); @@ -496,11 +403,9 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous points_inside.push_back(points[idx].cast()); // Only select/deselect points that are actually visible -#if ENABLE_NON_STATIC_CANVAS_MANAGER - for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, wxGetApp().plater()->get_camera(), points_inside, m_c->m_clipping_plane.get())) -#else - for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_c->m_clipping_plane.get())) -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER + for (size_t idx : m_c->raycaster()->raycaster()->get_unobscured_idxs( + trafo, wxGetApp().plater()->get_camera(), points_inside, + m_c->object_clipper()->get_clipping_plane())) { if (rectangle_status == GLSelectionRectangle::Deselect) unselect_point(points_idxs[idx]); @@ -577,20 +482,21 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } if (action == SLAGizmoEventType::MouseWheelUp && control_down) { - m_c->m_clipping_plane_distance = std::min(1.f, m_c->m_clipping_plane_distance + 0.01f); - update_clipping_plane(m_c->m_clipping_plane_was_moved); - m_c->m_clipping_plane_was_moved = true; + double pos = m_c->object_clipper()->get_position(); + pos = std::min(1., pos + 0.01); + m_c->object_clipper()->set_position(pos, true); return true; } if (action == SLAGizmoEventType::MouseWheelDown && control_down) { - m_c->m_clipping_plane_distance = std::max(0.f, m_c->m_clipping_plane_distance - 0.01f); - update_clipping_plane(true); + double pos = m_c->object_clipper()->get_position(); + pos = std::max(0., pos - 0.01); + m_c->object_clipper()->set_position(pos, true); return true; } if (action == SLAGizmoEventType::ResetClippingPlane) { - update_clipping_plane(); + m_c->object_clipper()->set_position(-1., false); return true; } @@ -634,11 +540,12 @@ void GLGizmoSlaSupports::on_update(const UpdateData& data) std::vector GLGizmoSlaSupports::get_config_options(const std::vector& keys) const { std::vector out; + const ModelObject* mo = m_c->selection_info()->model_object(); - if (!m_c->m_model_object) + if (! mo) return out; - const DynamicPrintConfig& object_cfg = m_c->m_model_object->config; + const DynamicPrintConfig& object_cfg = mo->config; const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; std::unique_ptr default_cfg = nullptr; @@ -659,14 +566,6 @@ std::vector GLGizmoSlaSupports::get_config_options(const st } -ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const -{ - if (!m_c->m_model_object || m_state == Off || m_c->m_clipping_plane_distance == 0.f) - return ClippingPlane::ClipsNothing(); - else - return ClippingPlane(-m_c->m_clipping_plane->get_normal(), m_c->m_clipping_plane->get_data()[3]); -} - /* void GLGizmoSlaSupports::find_intersecting_facets(const igl::AABB* aabb, const Vec3f& normal, double offset, std::vector& idxs) const @@ -714,7 +613,9 @@ void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_l static float last_y = 0.0f; static float last_h = 0.0f; - if (! m_c->m_model_object) + ModelObject* mo = m_c->selection_info()->model_object(); + + if (! mo) return; bool first_run = true; // This is a hack to redraw the button when all points are removed, @@ -851,15 +752,15 @@ RENDER_AGAIN: m_density_stash = density; } if (slider_edited) { - m_c->m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; - m_c->m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; + mo->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; + mo->config.opt("support_points_density_relative", true)->value = (int)density; } if (slider_released) { - m_c->m_model_object->config.opt("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash; - m_c->m_model_object->config.opt("support_points_density_relative", true)->value = (int)m_density_stash; + mo->config.opt("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash; + mo->config.opt("support_points_density_relative", true)->value = (int)m_density_stash; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change"))); - m_c->m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; - m_c->m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; + mo->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; + mo->config.opt("support_points_density_relative", true)->value = (int)density; wxGetApp().obj_list()->update_and_show_object_settings_item(); } @@ -886,7 +787,7 @@ RENDER_AGAIN: // Following is rendered in both editing and non-editing mode: ImGui::Separator(); - if (m_c->m_clipping_plane_distance == 0.f) + if (m_c->object_clipper()->get_position() == 0.f) { ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("clipping_of_view")); @@ -894,17 +795,16 @@ RENDER_AGAIN: else { if (m_imgui->button(m_desc.at("reset_direction"))) { wxGetApp().CallAfter([this](){ - update_clipping_plane(); + m_c->object_clipper()->set_position(-1., false); }); } } ImGui::SameLine(clipping_slider_left); ImGui::PushItemWidth(window_width - clipping_slider_left); - if (ImGui::SliderFloat(" ", &m_c->m_clipping_plane_distance, 0.f, 1.f, "%.2f")) { - update_clipping_plane(m_c->m_clipping_plane_was_moved); - m_c->m_clipping_plane_was_moved = true; - } + float clp_dist = m_c->object_clipper()->get_position(); + if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f")) + m_c->object_clipper()->set_position(clp_dist, true); if (m_imgui->button("?")) { @@ -969,18 +869,22 @@ std::string GLGizmoSlaSupports::on_get_name() const } +CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const +{ + return CommonGizmosDataID( + int(CommonGizmosDataID::SelectionInfo) + | int(CommonGizmosDataID::InstancesHider) + | int(CommonGizmosDataID::Raycaster) + | int(CommonGizmosDataID::HollowedMesh) + | int(CommonGizmosDataID::ObjectClipper) + | int(CommonGizmosDataID::SupportsClipper)); +} + + void GLGizmoSlaSupports::on_set_state() { - // m_c->m_model_object pointer can be invalid (for instance because of undo/redo action), - // we should recover it from the object id - m_c->m_model_object = nullptr; - for (const auto mo : wxGetApp().model().objects) { - if (mo->id() == m_c->m_model_object_id) { - m_c->m_model_object = mo; - break; - } - } + const ModelObject* mo = m_c->selection_info()->model_object(); if (m_state == m_old_state) return; @@ -988,28 +892,16 @@ void GLGizmoSlaSupports::on_set_state() if (m_state == On && m_old_state != On) { // the gizmo was just turned on Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); - m_c->unstash_clipping_plane(); - update_clipping_plane(m_c->m_clipping_plane_was_moved); - - m_c->build_AABB_if_needed(); - - // we'll now reload support points: - if (m_c->m_model_object) + if (mo) reload_cache(); - m_parent.toggle_model_objects_visibility(false); - if (m_c->m_model_object) { - m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); - m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); - } - // Set default head diameter from config. const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; m_new_point_head_diameter = static_cast(cfg.option("support_head_front_diameter"))->value; } if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off - bool will_ask = m_c->m_model_object && m_editing_mode && unsaved_changes(); + bool will_ask = mo && m_editing_mode && unsaved_changes(); if (will_ask) { wxGetApp().CallAfter([this]() { // Following is called through CallAfter, because otherwise there was a problem @@ -1028,16 +920,8 @@ void GLGizmoSlaSupports::on_set_state() // we are actually shutting down disable_editing_mode(); // so it is not active next time the gizmo opens Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); - m_parent.toggle_model_objects_visibility(true); m_normal_cache.clear(); - m_c->stash_clipping_plane(); - m_c->m_clipping_plane_distance = 0.f; - update_clipping_plane(true); - // Release clippers and the AABB raycaster. - m_its = nullptr; - m_c->m_object_clipper.reset(); - m_c->m_supports_clipper.reset(); - //m_c->m_mesh_raycaster.reset(); + } } m_old_state = m_state; @@ -1077,10 +961,7 @@ void GLGizmoSlaSupports::on_stop_dragging() void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar) { - ar(m_c->m_clipping_plane_distance, - *m_c->m_clipping_plane, - m_c->m_model_object_id, - m_new_point_head_diameter, + ar(m_new_point_head_diameter, m_normal_cache, m_editing_cache, m_selection_empty @@ -1091,10 +972,7 @@ void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar) void GLGizmoSlaSupports::on_save(cereal::BinaryOutputArchive& ar) const { - ar(m_c->m_clipping_plane_distance, - *m_c->m_clipping_plane, - m_c->m_model_object_id, - m_new_point_head_diameter, + ar(m_new_point_head_diameter, m_normal_cache, m_editing_cache, m_selection_empty @@ -1171,9 +1049,10 @@ void GLGizmoSlaSupports::editing_mode_apply_changes() for (const CacheEntry& ce : m_editing_cache) m_normal_cache.push_back(ce.support_point); - m_c->m_model_object->sla_points_status = sla::PointsStatus::UserModified; - m_c->m_model_object->sla_support_points.clear(); - m_c->m_model_object->sla_support_points = m_normal_cache; + ModelObject* mo = m_c->selection_info()->model_object(); + mo->sla_points_status = sla::PointsStatus::UserModified; + mo->sla_support_points.clear(); + mo->sla_support_points = m_normal_cache; reslice_SLA_supports(); } @@ -1183,23 +1062,25 @@ void GLGizmoSlaSupports::editing_mode_apply_changes() void GLGizmoSlaSupports::reload_cache() { + const ModelObject* mo = m_c->selection_info()->model_object(); m_normal_cache.clear(); - if (m_c->m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated || m_c->m_model_object->sla_points_status == sla::PointsStatus::Generating) + if (mo->sla_points_status == sla::PointsStatus::AutoGenerated || mo->sla_points_status == sla::PointsStatus::Generating) get_data_from_backend(); else - for (const sla::SupportPoint& point : m_c->m_model_object->sla_support_points) + for (const sla::SupportPoint& point : mo->sla_support_points) m_normal_cache.emplace_back(point); } bool GLGizmoSlaSupports::has_backend_supports() const { - if (! m_c->m_model_object) + const ModelObject* mo = m_c->selection_info()->model_object(); + if (! mo) return false; // find SlaPrintObject with this ID for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { - if (po->model_object()->id() == m_c->m_model_object->id()) + if (po->model_object()->id() == mo->id()) return po->is_step_done(slaposSupportPoints); } return false; @@ -1207,24 +1088,28 @@ bool GLGizmoSlaSupports::has_backend_supports() const void GLGizmoSlaSupports::reslice_SLA_supports(bool postpone_error_messages) const { - wxGetApp().CallAfter([this, postpone_error_messages]() { wxGetApp().plater()->reslice_SLA_supports(*m_c->m_model_object, postpone_error_messages); }); + wxGetApp().CallAfter([this, postpone_error_messages]() { + wxGetApp().plater()->reslice_SLA_supports( + *m_c->selection_info()->model_object(), postpone_error_messages); + }); } void GLGizmoSlaSupports::get_data_from_backend() { if (! has_backend_supports()) return; + ModelObject* mo = m_c->selection_info()->model_object(); // find the respective SLAPrintObject, we need a pointer to it for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { - if (po->model_object()->id() == m_c->m_model_object->id()) { + if (po->model_object()->id() == mo->id()) { m_normal_cache.clear(); const std::vector& points = po->get_support_points(); auto mat = po->trafo().inverse().cast(); for (unsigned int i=0; im_model_object->sla_points_status = sla::PointsStatus::AutoGenerated; + mo->sla_points_status = sla::PointsStatus::AutoGenerated; break; } } @@ -1241,10 +1126,12 @@ void GLGizmoSlaSupports::auto_generate() _(L("Are you sure you want to do it?")) + "\n", _(L("Warning")), wxICON_WARNING | wxYES | wxNO); - if (m_c->m_model_object->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) { + ModelObject* mo = m_c->selection_info()->model_object(); + + if (mo->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Autogenerate support points"))); wxGetApp().CallAfter([this]() { reslice_SLA_supports(); }); - m_c->m_model_object->sla_points_status = sla::PointsStatus::Generating; + mo->sla_points_status = sla::PointsStatus::Generating; } } @@ -1259,7 +1146,7 @@ void GLGizmoSlaSupports::switch_to_editing_mode() m_editing_cache.emplace_back(sp); select_point(NoPoints); - m_parent.toggle_sla_auxiliaries_visibility(false, m_c->m_model_object, m_c->m_active_instance); + m_c->instances_hider()->show_supports(false); m_parent.set_as_dirty(); } @@ -1269,7 +1156,7 @@ void GLGizmoSlaSupports::disable_editing_mode() if (m_editing_mode) { m_editing_mode = false; wxGetApp().plater()->leave_gizmos_stack(); - m_parent.toggle_sla_auxiliaries_visibility(true, m_c->m_model_object, m_c->m_active_instance); + m_c->instances_hider()->show_supports(true); m_parent.set_as_dirty(); } } @@ -1288,26 +1175,6 @@ bool GLGizmoSlaSupports::unsaved_changes() const return false; } - -void GLGizmoSlaSupports::update_clipping_plane(bool keep_normal) const -{ - if (! m_c->m_model_object) - return; - -#if ENABLE_NON_STATIC_CANVAS_MANAGER - Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ? - m_c->m_clipping_plane->get_normal() : -wxGetApp().plater()->get_camera().get_dir_forward()); -#else - Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ? - m_c->m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); -#endif // ENABLE_NON_STATIC_CANVAS_MANAGER - - const Vec3d& center = m_c->m_model_object->instances[m_c->m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift); - float dist = normal.dot(center); - *m_c->m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_c->m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius)); - m_parent.set_as_dirty(); -} - SlaGizmoHelpDialog::SlaGizmoHelpDialog() : wxDialog(nullptr, wxID_ANY, _(L("SLA gizmo keyboard shortcuts")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index bd48ecd650..917d22a988 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -13,34 +13,17 @@ namespace Slic3r { namespace GUI { -class ClippingPlane; -class MeshClipper; -class MeshRaycaster; -class CommonGizmosData; enum class SLAGizmoEventType : unsigned char; class GLGizmoSlaSupports : public GLGizmoBase { private: - //ModelObject* m_model_object = nullptr; - //ObjectID m_model_object_id = 0; - //int m_active_instance = -1; - //float m_active_instance_bb_radius; // to cache the bb - mutable double m_z_shift = 0.f; + bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); const float RenderPointScale = 1.f; GLUquadricObj* m_quadric; - typedef Eigen::Map> MapMatrixXfUnaligned; - typedef Eigen::Map> MapMatrixXiUnaligned; - - //std::unique_ptr m_mesh_raycaster; - //const TriangleMesh* m_mesh; - const indexed_triangle_set* m_its; - //mutable int m_old_timestamp = -1; - //mutable int m_print_object_idx = -1; - //mutable int m_print_objects_count = -1; class CacheEntry { public: @@ -75,14 +58,12 @@ public: void set_sla_support_data(ModelObject* model_object, const Selection& selection); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); void delete_selected_points(bool force = false); - ClippingPlane get_sla_clipping_plane() const; + //ClippingPlane get_sla_clipping_plane() const; bool is_in_editing_mode() const { return m_editing_mode; } bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); } bool has_backend_supports() const; void reslice_SLA_supports(bool postpone_error_messages = false) const; - void update_clipping_plane(bool keep_normal = false) const; - void set_common_data_ptr(CommonGizmosData* ptr) { m_c = ptr; } private: bool on_init() override; @@ -90,9 +71,7 @@ private: void on_render() const override; void on_render_for_picking() const override; - //void render_selection_rectangle() const; void render_points(const Selection& selection, bool picking = false) const; - void render_clipping_plane(const Selection& selection) const; bool unsaved_changes() const; bool m_lock_unique_islands = false; @@ -104,8 +83,7 @@ private: float m_density_stash = 0.f; // and again mutable std::vector m_editing_cache; // a support point and whether it is currently selected std::vector m_normal_cache; // to restore after discarding changes or undo/redo - - //std::unique_ptr m_clipping_plane; + const ModelObject* m_old_mo = nullptr; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. @@ -117,11 +95,6 @@ private: 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) - CommonGizmosData* m_c = nullptr; - - //mutable std::unique_ptr m_object_clipper; - //mutable std::unique_ptr m_supports_clipper; - std::vector get_config_options(const std::vector& keys) const; bool is_mesh_point_clipped(const Vec3d& point) const; bool is_point_in_hole(const Vec3f& pt) const; @@ -142,7 +115,6 @@ private: void auto_generate(); void switch_to_editing_mode(); void disable_editing_mode(); - void reset_clipping_plane_normal() const; protected: void on_set_state() override; @@ -159,6 +131,7 @@ protected: std::string on_get_name() const override; bool on_is_activable() const override; bool on_is_selectable() const override; + virtual CommonGizmosDataID on_get_requirements() const override; void on_load(cereal::BinaryInputArchive& ar) override; void on_save(cereal::BinaryOutputArchive& ar) const override; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmos.hpp b/src/slic3r/GUI/Gizmos/GLGizmos.hpp index 9f97c42b46..e8e73959cc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmos.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmos.hpp @@ -31,6 +31,7 @@ enum class SLAGizmoEventType : unsigned char { #include "slic3r/GUI/Gizmos/GLGizmoRotate.hpp" #include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp" #include "slic3r/GUI/Gizmos/GLGizmoCut.hpp" #include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp new file mode 100644 index 0000000000..32a6de42b9 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -0,0 +1,473 @@ +#include "GLGizmosCommon.hpp" + +#include + +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "libslic3r/SLAPrint.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Camera.hpp" + +#include + +namespace Slic3r { +namespace GUI { + +using namespace CommonGizmosDataObjects; + +CommonGizmosDataPool::CommonGizmosDataPool(GLCanvas3D* canvas) + : m_canvas(canvas) +{ + using c = CommonGizmosDataID; + m_data[c::SelectionInfo].reset( new SelectionInfo(this)); + m_data[c::InstancesHider].reset( new InstancesHider(this)); + m_data[c::HollowedMesh].reset( new HollowedMesh(this)); + m_data[c::Raycaster].reset( new Raycaster(this)); + m_data[c::ObjectClipper].reset( new ObjectClipper(this)); + m_data[c::SupportsClipper].reset( new SupportsClipper(this)); + +} + +void CommonGizmosDataPool::update(CommonGizmosDataID required) +{ + assert(check_dependencies(required)); + for (auto& [id, data] : m_data) { + if (int(required) & int(CommonGizmosDataID(id))) + data->update(); + else + if (data->is_valid()) + data->release(); + + } +} + + +SelectionInfo* CommonGizmosDataPool::selection_info() const +{ + SelectionInfo* sel_info = dynamic_cast(m_data.at(CommonGizmosDataID::SelectionInfo).get()); + assert(sel_info); + return sel_info->is_valid() ? sel_info : nullptr; +} + + +InstancesHider* CommonGizmosDataPool::instances_hider() const +{ + InstancesHider* inst_hider = dynamic_cast(m_data.at(CommonGizmosDataID::InstancesHider).get()); + assert(inst_hider); + return inst_hider->is_valid() ? inst_hider : nullptr; +} + +HollowedMesh* CommonGizmosDataPool::hollowed_mesh() const +{ + HollowedMesh* hol_mesh = dynamic_cast(m_data.at(CommonGizmosDataID::HollowedMesh).get()); + assert(hol_mesh); + return hol_mesh->is_valid() ? hol_mesh : nullptr; +} + +Raycaster* CommonGizmosDataPool::raycaster() const +{ + Raycaster* rc = dynamic_cast(m_data.at(CommonGizmosDataID::Raycaster).get()); + assert(rc); + return rc->is_valid() ? rc : nullptr; +} + +ObjectClipper* CommonGizmosDataPool::object_clipper() const +{ + ObjectClipper* oc = dynamic_cast(m_data.at(CommonGizmosDataID::ObjectClipper).get()); + // ObjectClipper is used from outside the gizmos to report current clipping plane. + // This function can be called when oc is nullptr. + return (oc && oc->is_valid()) ? oc : nullptr; +} + +SupportsClipper* CommonGizmosDataPool::supports_clipper() const +{ + SupportsClipper* sc = dynamic_cast(m_data.at(CommonGizmosDataID::SupportsClipper).get()); + assert(sc); + return sc->is_valid() ? sc : nullptr; +} + +#ifndef NDEBUG +// Check the required resources one by one and return true if all +// dependencies are met. +bool CommonGizmosDataPool::check_dependencies(CommonGizmosDataID required) const +{ + // This should iterate over currently required data. Each of them should + // be asked about its dependencies and it must check that all dependencies + // are also in required and before the current one. + for (auto& [id, data] : m_data) { + // in case we don't use this, the deps are irrelevant + if (! (int(required) & int(CommonGizmosDataID(id)))) + continue; + + + CommonGizmosDataID deps = data->get_dependencies(); + assert(int(deps) == (int(deps) & int(required))); + } + + + return true; +} +#endif // NDEBUG + + + + +void SelectionInfo::on_update() +{ + const Selection& selection = get_pool()->get_canvas()->get_selection(); + if (selection.is_single_full_instance()) { + m_model_object = selection.get_model()->objects[selection.get_object_idx()]; + m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); + } + else + m_model_object = nullptr; +} + +void SelectionInfo::on_release() +{ + m_model_object = nullptr; +} + +int SelectionInfo::get_active_instance() const +{ + const Selection& selection = get_pool()->get_canvas()->get_selection(); + return selection.get_instance_idx(); +} + + + + + +void InstancesHider::on_update() +{ + const ModelObject* mo = get_pool()->selection_info()->model_object(); + int active_inst = get_pool()->selection_info()->get_active_instance(); + GLCanvas3D* canvas = get_pool()->get_canvas(); + + if (mo && active_inst != -1) { + canvas->toggle_model_objects_visibility(false); + canvas->toggle_model_objects_visibility(true, mo, active_inst); + canvas->toggle_sla_auxiliaries_visibility(m_show_supports, mo, active_inst); + } + else + canvas->toggle_model_objects_visibility(true); +} + +void InstancesHider::on_release() +{ + get_pool()->get_canvas()->toggle_model_objects_visibility(true); +} + +void InstancesHider::show_supports(bool show) { + if (m_show_supports != show) { + m_show_supports = show; + on_update(); + } +} + + + +void HollowedMesh::on_update() +{ + const ModelObject* mo = get_pool()->selection_info()->model_object(); + if (! mo) + return; + + const GLCanvas3D* canvas = get_pool()->get_canvas(); + const PrintObjects& print_objects = canvas->sla_print()->objects(); + const SLAPrintObject* print_object = m_print_object_idx != -1 + ? print_objects[m_print_object_idx] + : nullptr; + + // Find the respective SLAPrintObject. + if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) { + m_print_objects_count = print_objects.size(); + m_print_object_idx = -1; + for (const SLAPrintObject* po : print_objects) { + ++m_print_object_idx; + if (po->model_object()->id() == mo->id()) { + print_object = po; + break; + } + } + } + + // If there is a valid SLAPrintObject, check state of Hollowing step. + if (print_object) { + if (print_object->is_step_done(slaposDrillHoles) && print_object->has_mesh(slaposDrillHoles)) { + size_t timestamp = print_object->step_state_with_timestamp(slaposDrillHoles).timestamp; + if (timestamp > m_old_hollowing_timestamp) { + const TriangleMesh& backend_mesh = print_object->get_mesh_to_print(); + if (! backend_mesh.empty()) { + m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh)); + Transform3d trafo_inv = canvas->sla_print()->sla_trafo(*mo).inverse(); + m_hollowed_mesh_transformed->transform(trafo_inv); + m_old_hollowing_timestamp = timestamp; + } + else + m_hollowed_mesh_transformed.reset(nullptr); + } + } + else + m_hollowed_mesh_transformed.reset(nullptr); + } +} + + +void HollowedMesh::on_release() +{ + m_hollowed_mesh_transformed.reset(); + m_old_hollowing_timestamp = 0; + m_print_object_idx = -1; +} + + +const TriangleMesh* HollowedMesh::get_hollowed_mesh() const +{ + return m_hollowed_mesh_transformed.get(); +} + + + + + +void Raycaster::on_update() +{ + wxBusyCursor wait; + const ModelObject* mo = get_pool()->selection_info()->model_object(); + + if (! mo) + return; + + std::vector meshes; + const std::vector& mvs = mo->volumes; + if (mvs.size() == 1) { + assert(mvs.front()->is_model_part()); + const HollowedMesh* hollowed_mesh_tracker = get_pool()->hollowed_mesh(); + if (hollowed_mesh_tracker && hollowed_mesh_tracker->get_hollowed_mesh()) + meshes.push_back(hollowed_mesh_tracker->get_hollowed_mesh()); + } + if (meshes.empty()) { + for (const ModelVolume* mv : mvs) { + if (mv->is_model_part()) + meshes.push_back(&mv->mesh()); + } + } + + if (meshes != m_old_meshes) { + m_raycasters.clear(); + for (const TriangleMesh* mesh : meshes) + m_raycasters.emplace_back(new MeshRaycaster(*mesh)); + m_old_meshes = meshes; + } +} + +void Raycaster::on_release() +{ + m_raycasters.clear(); + m_old_meshes.clear(); +} + +std::vector Raycaster::raycasters() const +{ + std::vector mrcs; + for (const auto& raycaster_unique_ptr : m_raycasters) + mrcs.push_back(raycaster_unique_ptr.get()); + return mrcs; +} + + + + + +void ObjectClipper::on_update() +{ + const ModelObject* mo = get_pool()->selection_info()->model_object(); + if (! mo) + return; + + // which mesh should be cut? + std::vector meshes; + bool has_hollowed = get_pool()->hollowed_mesh() && get_pool()->hollowed_mesh()->get_hollowed_mesh(); + if (has_hollowed) + meshes.push_back(get_pool()->hollowed_mesh()->get_hollowed_mesh()); + + if (meshes.empty()) { + for (const ModelVolume* mv : mo->volumes) + if (mv->is_model_part()) + meshes.push_back(&mv->mesh()); + } + + if (meshes != m_old_meshes) { + m_clippers.clear(); + for (const TriangleMesh* mesh : meshes) { + m_clippers.emplace_back(new MeshClipper); + m_clippers.back()->set_mesh(*mesh); + } + m_old_meshes = meshes; + m_active_inst_bb_radius = + mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius(); + //if (has_hollowed && m_clp_ratio != 0.) + // m_clp_ratio = 0.25; + } +} + + +void ObjectClipper::on_release() +{ + m_clippers.clear(); + m_old_meshes.clear(); + m_clp.reset(); + m_clp_ratio = 0.; + +} + +void ObjectClipper::render_cut() const +{ + if (m_clp_ratio == 0.) + return; + const SelectionInfo* sel_info = get_pool()->selection_info(); + const ModelObject* mo = sel_info->model_object(); + Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation(); + + size_t clipper_id = 0; + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + + Geometry::Transformation vol_trafo = mv->get_transformation(); + Geometry::Transformation trafo = inst_trafo * vol_trafo; + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); + + auto& clipper = m_clippers[clipper_id]; + clipper->set_plane(*m_clp); + clipper->set_transformation(trafo); + + if (! clipper->get_triangles().empty()) { + ::glPushMatrix(); + ::glColor3f(1.0f, 0.37f, 0.0f); + ::glBegin(GL_TRIANGLES); + for (const Vec3f& point : clipper->get_triangles()) + ::glVertex3f(point(0), point(1), point(2)); + ::glEnd(); + ::glPopMatrix(); + } + ++clipper_id; + } +} + + +void ObjectClipper::set_position(double pos, bool keep_normal) +{ + const ModelObject* mo = get_pool()->selection_info()->model_object(); + int active_inst = get_pool()->selection_info()->get_active_instance(); + double z_shift = get_pool()->selection_info()->get_sla_shift(); + + Vec3d normal = (keep_normal && m_clp) ? m_clp->get_normal() : -wxGetApp().plater()->get_camera().get_dir_forward(); + const Vec3d& center = mo->instances[active_inst]->get_offset() + Vec3d(0., 0., z_shift); + float dist = normal.dot(center); + + if (pos < 0.) + pos = m_clp_ratio; + + m_clp_ratio = pos; + m_clp.reset(new ClippingPlane(normal, (dist - (-m_active_inst_bb_radius) - m_clp_ratio * 2*m_active_inst_bb_radius))); + get_pool()->get_canvas()->set_as_dirty(); +} + + + +void SupportsClipper::on_update() +{ + const ModelObject* mo = get_pool()->selection_info()->model_object(); + if (! mo) + return; + + const GLCanvas3D* canvas = get_pool()->get_canvas(); + const PrintObjects& print_objects = canvas->sla_print()->objects(); + const SLAPrintObject* print_object = m_print_object_idx != -1 + ? print_objects[m_print_object_idx] + : nullptr; + + // Find the respective SLAPrintObject. + if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) { + m_print_objects_count = print_objects.size(); + m_print_object_idx = -1; + for (const SLAPrintObject* po : print_objects) { + ++m_print_object_idx; + if (po->model_object()->id() == mo->id()) { + print_object = po; + break; + } + } + } + + if (print_object + && print_object->is_step_done(slaposSupportTree) + && ! print_object->support_mesh().empty()) + { + // If the supports are already calculated, save the timestamp of the respective step + // so we can later tell they were recalculated. + size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp; + if (! m_clipper || timestamp != m_old_timestamp) { + // The timestamp has changed. + m_clipper.reset(new MeshClipper); + // The mesh should already have the shared vertices calculated. + m_clipper->set_mesh(print_object->support_mesh()); + m_old_timestamp = timestamp; + } + } + else + // The supports are not valid. We better dump the cached data. + m_clipper.reset(); +} + + +void SupportsClipper::on_release() +{ + m_clipper.reset(); + m_old_timestamp = 0; + m_print_object_idx = -1; +} + +void SupportsClipper::render_cut() const +{ + const CommonGizmosDataObjects::ObjectClipper* ocl = get_pool()->object_clipper(); + if (ocl->get_position() == 0. + || ! get_pool()->instances_hider()->are_supports_shown() + || ! m_clipper) + return; + + const SelectionInfo* sel_info = get_pool()->selection_info(); + const ModelObject* mo = sel_info->model_object(); + Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation(); + //Geometry::Transformation vol_trafo = mo->volumes.front()->get_transformation(); + Geometry::Transformation trafo = inst_trafo;// * vol_trafo; + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); + + + // Get transformation of supports + Geometry::Transformation supports_trafo = trafo; + supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), sel_info->get_sla_shift())); + supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2))); + // I don't know why, but following seems to be correct. + supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2), + 1, + 1.)); + + m_clipper->set_plane(*ocl->get_clipping_plane()); + m_clipper->set_transformation(supports_trafo); + + if (! m_clipper->get_triangles().empty()) { + ::glPushMatrix(); + ::glColor3f(1.0f, 0.f, 0.37f); + ::glBegin(GL_TRIANGLES); + for (const Vec3f& point : m_clipper->get_triangles()) + ::glVertex3f(point(0), point(1), point(2)); + ::glEnd(); + ::glPopMatrix(); + } +} + + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp new file mode 100644 index 0000000000..31c473bac7 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -0,0 +1,309 @@ +#ifndef slic3r_GUI_GLGizmosCommon_hpp_ +#define slic3r_GUI_GLGizmosCommon_hpp_ + +#include +#include + +#include "slic3r/GUI/MeshUtils.hpp" + +namespace Slic3r { + +class ModelObject; + + +namespace GUI { + +class GLCanvas3D; + +static constexpr float HoleStickOutLength = 1.f; + +enum class SLAGizmoEventType : unsigned char { + LeftDown = 1, + LeftUp, + RightDown, + RightUp, + Dragging, + Delete, + SelectAll, + ShiftUp, + AltUp, + ApplyChanges, + DiscardChanges, + AutomaticGeneration, + ManualEditing, + MouseWheelUp, + MouseWheelDown, + ResetClippingPlane +}; + + + +class CommonGizmosDataBase; +namespace CommonGizmosDataObjects { + class SelectionInfo; + class InstancesHider; + class HollowedMesh; + class Raycaster; + class ObjectClipper; + class SupportsClipper; +} + +// Some of the gizmos use the same data that need to be updated ocassionally. +// It is also desirable that the data are not recalculated when the gizmos +// are just switched, but on the other hand, they should be released when +// they are not in use by any gizmo anymore. + +// Enumeration of various data types that the data pool can contain. +// Each gizmo can tell which of the data it wants to use through +// on_get_requirements() method. +enum class CommonGizmosDataID { + None = 0, + SelectionInfo = 1 << 0, + InstancesHider = 1 << 1, + HollowedMesh = 1 << 2, + Raycaster = 1 << 3, + ObjectClipper = 1 << 4, + SupportsClipper = 1 << 5, + +}; + + +// Following class holds pointers to the common data objects and triggers +// their updating/releasing. There is just one object of this type (managed +// by GLGizmoManager, the gizmos keep a pointer to it. +class CommonGizmosDataPool { +public: + CommonGizmosDataPool(GLCanvas3D* canvas); + + // Update all resources and release what is not used. + // Accepts a bitmask of currently required resources. + void update(CommonGizmosDataID required); + + // Getters for the data that need to be accessed from the gizmos directly. + CommonGizmosDataObjects::SelectionInfo* selection_info() const; + CommonGizmosDataObjects::InstancesHider* instances_hider() const; + CommonGizmosDataObjects::HollowedMesh* hollowed_mesh() const; + CommonGizmosDataObjects::Raycaster* raycaster() const; + CommonGizmosDataObjects::ObjectClipper* object_clipper() const; + CommonGizmosDataObjects::SupportsClipper* supports_clipper() const; + + + GLCanvas3D* get_canvas() const { return m_canvas; } + +private: + std::map> m_data; + GLCanvas3D* m_canvas; + +#ifndef NDEBUG + bool check_dependencies(CommonGizmosDataID required) const; +#endif +}; + + + + + +// Base class for a wrapper object managing a single resource. +// Each of the enum values above (safe None) will have an object of this kind. +class CommonGizmosDataBase { +public: + // Pass a backpointer to the pool, so the individual + // objects can communicate with one another. + explicit CommonGizmosDataBase(CommonGizmosDataPool* cgdp) + : m_common{cgdp} {} + virtual ~CommonGizmosDataBase() {} + + // Update the resource. + void update() { on_update(); m_is_valid = true; } + + // Release any data that are stored internally. + void release() { on_release(); m_is_valid = false; } + + // Returns whether the resource is currently maintained. + bool is_valid() const { return m_is_valid; } + +#ifndef NDEBUG + // Return a bitmask of all resources that this one relies on. + // The dependent resource must have higher ID than the one + // it depends on. + virtual CommonGizmosDataID get_dependencies() const { return CommonGizmosDataID::None; } +#endif // NDEBUG + +protected: + virtual void on_release() = 0; + virtual void on_update() = 0; + CommonGizmosDataPool* get_pool() const { return m_common; } + + +private: + bool m_is_valid = false; + CommonGizmosDataPool* m_common = nullptr; +}; + + + +// The specializations of the CommonGizmosDataBase class live in this +// namespace to avoid clashes in GUI namespace. +namespace CommonGizmosDataObjects +{ + +class SelectionInfo : public CommonGizmosDataBase +{ +public: + explicit SelectionInfo(CommonGizmosDataPool* cgdp) + : CommonGizmosDataBase(cgdp) {} + + ModelObject* model_object() const { return m_model_object; } + int get_active_instance() const; + float get_sla_shift() const { return m_z_shift; } + +protected: + void on_update() override; + void on_release() override; + +private: + ModelObject* m_model_object = nullptr; + int m_active_inst = -1; + float m_z_shift = 0.f; +}; + + + +class InstancesHider : public CommonGizmosDataBase +{ +public: + explicit InstancesHider(CommonGizmosDataPool* cgdp) + : CommonGizmosDataBase(cgdp) {} +#ifndef NDEBUG + CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } +#endif // NDEBUG + + void show_supports(bool show); + bool are_supports_shown() const { return m_show_supports; } + +protected: + void on_update() override; + void on_release() override; + +private: + bool m_show_supports = false; +}; + + + +class HollowedMesh : public CommonGizmosDataBase +{ +public: + explicit HollowedMesh(CommonGizmosDataPool* cgdp) + : CommonGizmosDataBase(cgdp) {} +#ifndef NDEBUG + CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } +#endif // NDEBUG + + const TriangleMesh* get_hollowed_mesh() const; + +protected: + void on_update() override; + void on_release() override; + +private: + std::unique_ptr m_hollowed_mesh_transformed; + size_t m_old_hollowing_timestamp = 0; + int m_print_object_idx = -1; + int m_print_objects_count = 0; +}; + + + +class Raycaster : public CommonGizmosDataBase +{ +public: + explicit Raycaster(CommonGizmosDataPool* cgdp) + : CommonGizmosDataBase(cgdp) {} +#ifndef NDEBUG + CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } +#endif // NDEBUG + + const MeshRaycaster* raycaster() const { assert(m_raycasters.size() == 1); return m_raycasters.front().get(); } + std::vector raycasters() const; + +protected: + void on_update() override; + void on_release() override; + +private: + std::vector> m_raycasters; + std::vector m_old_meshes; +}; + + + +class ObjectClipper : public CommonGizmosDataBase +{ +public: + explicit ObjectClipper(CommonGizmosDataPool* cgdp) + : CommonGizmosDataBase(cgdp) {} +#ifndef NDEBUG + CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } +#endif // NDEBUG + + void set_position(double pos, bool keep_normal); + double get_position() const { return m_clp_ratio; } + ClippingPlane* get_clipping_plane() const { return m_clp.get(); } + void render_cut() const; + + +protected: + void on_update() override; + void on_release() override; + +private: + std::vector m_old_meshes; + std::vector> m_clippers; + std::unique_ptr m_clp; + double m_clp_ratio = 0.; + double m_active_inst_bb_radius = 0.; +}; + + + +class SupportsClipper : public CommonGizmosDataBase +{ +public: + explicit SupportsClipper(CommonGizmosDataPool* cgdp) + : CommonGizmosDataBase(cgdp) {} +#ifndef NDEBUG + CommonGizmosDataID get_dependencies() const override { + return CommonGizmosDataID( + int(CommonGizmosDataID::SelectionInfo) + | int(CommonGizmosDataID::ObjectClipper) + ); + } +#endif // NDEBUG + + void render_cut() const; + + +protected: + void on_update() override; + void on_release() override; + +private: + size_t m_old_timestamp = 0; + int m_print_object_idx = -1; + int m_print_objects_count = 0; + std::unique_ptr m_clipper; +}; + +} // namespace CommonGizmosDataObjects + + + + + + +} // namespace GUI +} // namespace Slic3r + + +#endif // slic3r_GUI_GLGizmosCommon_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 379bd48d14..685e49d367 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -9,10 +9,15 @@ #include "slic3r/GUI/GUI_ObjectManipulation.hpp" #include "slic3r/GUI/PresetBundle.hpp" #include "slic3r/Utils/UndoRedo.hpp" -#include "libslic3r/SLAPrint.hpp" -#include "slic3r/GUI/MeshUtils.hpp" -#include "slic3r/GUI/Gizmos/GLGizmos.hpp" -#include "slic3r/GUI/Camera.hpp" + +#include "slic3r/GUI/Gizmos/GLGizmoMove.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoScale.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoRotate.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoCut.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" #include @@ -97,16 +102,16 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoCut(m_parent, "cut.svg", 4)); m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", 5)); m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 6)); + m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "sla_supports.svg", 7)); - m_common_gizmos_data.reset(new CommonGizmosData()); - dynamic_cast(m_gizmos[Hollow].get())->set_common_data_ptr(m_common_gizmos_data.get()); - dynamic_cast(m_gizmos[SlaSupports].get())->set_common_data_ptr(m_common_gizmos_data.get()); + m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent)); for (auto& gizmo : m_gizmos) { if (! gizmo->init()) { m_gizmos.clear(); return false; } + gizmo->set_common_data_pool(m_common_gizmos_data.get()); } m_current = Undefined; @@ -198,6 +203,10 @@ void GLGizmosManager::update_data() enable_grabber(Scale, i, enable_scale_xyz); } + m_common_gizmos_data->update(get_current() + ? get_current()->get_requirements() + : CommonGizmosDataID(0)); + if (selection.is_single_full_instance()) { // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first @@ -207,6 +216,7 @@ void GLGizmosManager::update_data() ModelObject* model_object = selection.get_model()->objects[selection.get_object_idx()]; set_flattening_data(model_object); set_sla_support_data(model_object); + set_fdm_support_data(model_object); } else if (selection.is_single_volume() || selection.is_single_modifier()) { @@ -215,6 +225,7 @@ void GLGizmosManager::update_data() set_rotation(Vec3d::Zero()); set_flattening_data(nullptr); set_sla_support_data(nullptr); + set_fdm_support_data(nullptr); } else if (is_wipe_tower) { @@ -223,6 +234,7 @@ void GLGizmosManager::update_data() set_rotation(Vec3d(0., 0., (M_PI/180.) * dynamic_cast(config.option("wipe_tower_rotation_angle"))->value)); set_flattening_data(nullptr); set_sla_support_data(nullptr); + set_fdm_support_data(nullptr); } else { @@ -230,6 +242,7 @@ void GLGizmosManager::update_data() set_rotation(Vec3d::Zero()); set_flattening_data(selection.is_from_single_object() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr); set_sla_support_data(selection.is_from_single_instance() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr); + set_fdm_support_data(selection.is_from_single_instance() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr); } } @@ -358,15 +371,27 @@ void GLGizmosManager::set_sla_support_data(ModelObject* model_object) || wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) return; - m_common_gizmos_data->update_from_backend(m_parent, model_object); + /*m_common_gizmos_data->update_from_backend(m_parent, model_object); auto* gizmo_supports = dynamic_cast(m_gizmos[SlaSupports].get()); - auto* gizmo_hollow = dynamic_cast(m_gizmos[Hollow].get()); + // note: sla support gizmo takes care of updating the common data. // following lines are thus dependent - gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection()); + //gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection()); + */ + auto* gizmo_hollow = dynamic_cast(m_gizmos[Hollow].get()); + auto* gizmo_supports = dynamic_cast(m_gizmos[SlaSupports].get()); gizmo_hollow->set_sla_support_data(model_object, m_parent.get_selection()); + gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection()); +} + +void GLGizmosManager::set_fdm_support_data(ModelObject* model_object) +{ + if (!m_enabled || m_gizmos.empty()) + return; + + dynamic_cast(m_gizmos[FdmSupports].get())->set_fdm_support_data(model_object, m_parent.get_selection()); } // Returns true if the gizmo used the event to do something, false otherwise. @@ -377,20 +402,24 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p if (m_current == SlaSupports) return dynamic_cast(m_gizmos[SlaSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - if (m_current == Hollow) + else if (m_current == Hollow) return dynamic_cast(m_gizmos[Hollow].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - return false; + else if (m_current == FdmSupports) + return dynamic_cast(m_gizmos[FdmSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + else + return false; } -ClippingPlane GLGizmosManager::get_sla_clipping_plane() const +ClippingPlane GLGizmosManager::get_clipping_plane() const { - if (!m_enabled || (m_current != SlaSupports && m_current != Hollow) || m_gizmos.empty()) + if (! m_common_gizmos_data + || ! m_common_gizmos_data->object_clipper() + || m_common_gizmos_data->object_clipper()->get_position() == 0.) return ClippingPlane::ClipsNothing(); - - if (m_current == SlaSupports) - return dynamic_cast(m_gizmos[SlaSupports].get())->get_sla_clipping_plane(); - else - return dynamic_cast(m_gizmos[Hollow].get())->get_sla_clipping_plane(); + else { + const ClippingPlane& clp = *m_common_gizmos_data->object_clipper()->get_clipping_plane(); + return ClippingPlane(-clp.get_normal(), clp.get_data()[3]); + } } bool GLGizmosManager::wants_reslice_supports_on_undo() const @@ -410,6 +439,7 @@ void GLGizmosManager::render_current_gizmo() const void GLGizmosManager::render_current_gizmo_for_picking_pass() const { if (! m_enabled || m_current == Undefined) + return; m_gizmos[m_current]->render_for_picking(); @@ -439,7 +469,7 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt) { bool processed = false; - if (m_current == SlaSupports || m_current == Hollow) { + if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) { float rot = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) processed = true; @@ -529,8 +559,8 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) processed = true; m_mouse_capture.right = false; } - else - return false; +// else +// return false; } #if ENABLE_GIZMO_TOOLBAR_DRAGGING_FIX else if (evt.Dragging() && !is_dragging()) @@ -618,7 +648,8 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) if (evt.LeftDown()) { - if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) + if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) + && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) // the gizmo got the event and took some action, there is no need to do anything more processed = true; else if (!selection.is_empty() && grabber_contains_mouse()) { @@ -636,17 +667,25 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) processed = true; } } - else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::RightDown)) + else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == SlaSupports || m_current == Hollow) + && gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) { // we need to set the following right up as processed to avoid showing the context menu if the user release the mouse over the object pending_right_up = true; // event was taken care of by the SlaSupports gizmo processed = true; } + else if (evt.RightDown() && (selected_object_idx != -1) && m_current == FdmSupports + && gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) + { + // event was taken care of by the FdmSupports gizmo + processed = true; + } else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1) && (m_current == SlaSupports || m_current == Hollow)) // don't allow dragging objects with the Sla gizmo on processed = true; - else if (evt.Dragging() && (m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) + else if (evt.Dragging() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports ) + && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) { // the gizmo got the event and took some action, no need to do anything more here m_parent.set_as_dirty(); @@ -723,9 +762,9 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) processed = true; } #endif // !ENABLE_CANVAS_TOOLTIP_USING_IMGUI - else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow) && !m_parent.is_mouse_dragging()) + else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) && !m_parent.is_mouse_dragging()) { - // in case SLA gizmo is selected, we just pass the LeftUp event and stop processing - neither + // in case SLA/FDM gizmo is selected, we just pass the LeftUp event and stop processing - neither // object moving or selecting is suppressed in that case gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()); processed = true; @@ -735,6 +774,11 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) // to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active processed = true; } + else if (evt.RightUp() && m_current == FdmSupports && !m_parent.is_mouse_dragging()) + { + gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()); + processed = true; + } } else { @@ -824,7 +868,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case 'r' : case 'R' : { - if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) + if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) processed = true; break; @@ -1188,10 +1232,13 @@ void GLGizmosManager::activate_gizmo(EType type) return; // gizmo refused to be turned off, do nothing. } + m_current = type; + m_common_gizmos_data->update(get_current() + ? get_current()->get_requirements() + : CommonGizmosDataID(0)); + if (type != Undefined) m_gizmos[type]->set_state(GLGizmoBase::On); - - m_current = type; } @@ -1204,135 +1251,5 @@ bool GLGizmosManager::grabber_contains_mouse() const return (curr != nullptr) ? (curr->get_hover_id() != -1) : false; } - - -CommonGizmosData::CommonGizmosData() -{ - m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); -} - - - -bool CommonGizmosData::update_from_backend(GLCanvas3D& canvas, ModelObject* model_object) -{ - recent_update = false; - bool object_changed = false; - - if (m_model_object != model_object - || (model_object && m_model_object_id != model_object->id())) { - m_model_object = model_object; - m_print_object_idx = -1; - m_mesh_raycaster.reset(); - m_object_clipper.reset(); - m_supports_clipper.reset(); - m_old_mesh = nullptr; - m_mesh = nullptr; - m_backend_mesh_transformed.clear(); - - object_changed = true; - recent_update = true; - } - - if (m_model_object) { - int active_inst = canvas.get_selection().get_instance_idx(); - if (m_active_instance != active_inst) { - m_active_instance = active_inst; - m_active_instance_bb_radius = m_model_object->instance_bounding_box(m_active_instance).radius(); - recent_update = true; - } - } - - - if (! m_model_object || ! canvas.get_selection().is_from_single_instance()) - return false; - - int old_po_idx = m_print_object_idx; - - // First we need a pointer to the respective SLAPrintObject. The index into objects vector is - // cached so we don't have todo it on each render. We only search for the po if needed: - if (m_print_object_idx < 0 || (int)canvas.sla_print()->objects().size() != m_print_objects_count) { - m_print_objects_count = canvas.sla_print()->objects().size(); - m_print_object_idx = -1; - for (const SLAPrintObject* po : canvas.sla_print()->objects()) { - ++m_print_object_idx; - if (po->model_object()->id() == m_model_object->id()) - break; - } - } - - bool mesh_exchanged = false; - m_mesh = nullptr; - // Load either the model_object mesh, or one provided by the backend - // This mesh does not account for the possible Z up SLA offset. - // The backend mesh needs to be transformed and because a pointer to it is - // saved, a copy is stored as a member (FIXME) - if (m_print_object_idx >=0) { - const SLAPrintObject* po = canvas.sla_print()->objects()[m_print_object_idx]; - if (po->is_step_done(slaposDrillHoles)) { - m_backend_mesh_transformed = po->get_mesh_to_print(); - m_backend_mesh_transformed.transform(canvas.sla_print()->sla_trafo(*m_model_object).inverse()); - m_mesh = &m_backend_mesh_transformed; - m_has_drilled_mesh = true; - mesh_exchanged = true; - } - } - - if (! m_mesh) { - m_mesh = &m_model_object->volumes.front()->mesh(); - m_backend_mesh_transformed.clear(); - m_has_drilled_mesh = false; - } - - m_model_object_id = m_model_object->id(); - - if (m_mesh != m_old_mesh) { - // Update clipping plane position. - float new_clp_pos = m_clipping_plane_distance; - if (object_changed) { - new_clp_pos = 0.f; - m_clipping_plane_was_moved = false; - } else { - // After we got a drilled mesh, move the cp to 25%. This only applies when - // the hollowing gizmo is active and hollowing is enabled - if (m_clipping_plane_distance == 0.f && mesh_exchanged && m_has_drilled_mesh) { - const DynamicPrintConfig& cfg = - (m_model_object && m_model_object->config.has("hollowing_enable")) - ? m_model_object->config - : wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; - - if (cfg.has("hollowing_enable") && cfg.opt_bool("hollowing_enable") - && canvas.get_gizmos_manager().get_current_type() == GLGizmosManager::Hollow) { - new_clp_pos = 0.25f; - m_clipping_plane_was_moved = false; // so it uses current camera direction - } - } - } - m_clipping_plane_distance = new_clp_pos; - m_clipping_plane_distance_stash = new_clp_pos; - - m_schedule_aabb_calculation = true; - recent_update = true; - return true; - } - if (! recent_update) - recent_update = m_print_object_idx < 0 && old_po_idx >= 0; - - return recent_update; -} - - -void CommonGizmosData::build_AABB_if_needed() -{ - if (! m_schedule_aabb_calculation) - return; - - wxBusyCursor wait; - m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh)); - m_object_clipper.reset(); - m_supports_clipper.reset(); - m_old_mesh = m_mesh; - m_schedule_aabb_calculation = false; -} - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 7ae1fa661e..478774718f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -3,8 +3,8 @@ #include "slic3r/GUI/GLTexture.hpp" #include "slic3r/GUI/GLToolbar.hpp" -#include "libslic3r/ObjectID.hpp" #include "slic3r/GUI/Gizmos/GLGizmoBase.hpp" +#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" #include @@ -19,7 +19,7 @@ namespace GUI { class GLCanvas3D; class ClippingPlane; enum class SLAGizmoEventType : unsigned char; -class CommonGizmosData; +class CommonGizmosDataPool; class Rect { @@ -64,6 +64,7 @@ public: Cut, Hollow, SlaSupports, + FdmSupports, Undefined }; @@ -115,7 +116,8 @@ private: MouseCapture m_mouse_capture; std::string m_tooltip; bool m_serializing; - std::unique_ptr m_common_gizmos_data; + //std::unique_ptr m_common_gizmos_data; + std::unique_ptr m_common_gizmos_data; public: explicit GLGizmosManager(GLCanvas3D& parent); @@ -197,8 +199,11 @@ public: void set_flattening_data(const ModelObject* model_object); void set_sla_support_data(ModelObject* model_object); + + void set_fdm_support_data(ModelObject* model_object); + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false, bool alt_down = false, bool control_down = false); - ClippingPlane get_sla_clipping_plane() const; + ClippingPlane get_clipping_plane() const; bool wants_reslice_supports_on_undo() const; void render_current_gizmo() const; @@ -231,63 +236,6 @@ private: -class MeshRaycaster; -class MeshClipper; - -// This class is only for sharing SLA related data between SLA gizmos -// and its synchronization with backend data. It should not be misused -// for anything else. -class CommonGizmosData { -public: - CommonGizmosData(); - const TriangleMesh* mesh() const { - return (! m_mesh ? nullptr : m_mesh); //(m_cavity_mesh ? m_cavity_mesh.get() : m_mesh)); - } - - bool update_from_backend(GLCanvas3D& canvas, ModelObject* model_object); - bool recent_update = false; - static constexpr float HoleStickOutLength = 1.f; - - ModelObject* m_model_object = nullptr; - const TriangleMesh* m_mesh; - std::unique_ptr m_mesh_raycaster; - std::unique_ptr m_object_clipper; - std::unique_ptr m_supports_clipper; - - //std::unique_ptr m_cavity_mesh; - //std::unique_ptr m_volume_with_cavity; - - int m_active_instance = -1; - float m_active_instance_bb_radius = 0; - ObjectID m_model_object_id = 0; - int m_print_object_idx = -1; - int m_print_objects_count = -1; - int m_old_timestamp = -1; - - float m_clipping_plane_distance = 0.f; - std::unique_ptr m_clipping_plane; - bool m_clipping_plane_was_moved = false; - - void stash_clipping_plane() { - m_clipping_plane_distance_stash = m_clipping_plane_distance; - } - - void unstash_clipping_plane() { - m_clipping_plane_distance = m_clipping_plane_distance_stash; - } - - bool has_drilled_mesh() const { return m_has_drilled_mesh; } - - void build_AABB_if_needed(); - -private: - const TriangleMesh* m_old_mesh; - TriangleMesh m_backend_mesh_transformed; - float m_clipping_plane_distance_stash = 0.f; - bool m_has_drilled_mesh = false; - bool m_schedule_aabb_calculation = false; -}; - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Job.hpp b/src/slic3r/GUI/Job.hpp deleted file mode 100644 index ac31b9bdb0..0000000000 --- a/src/slic3r/GUI/Job.hpp +++ /dev/null @@ -1,155 +0,0 @@ -#ifndef JOB_HPP -#define JOB_HPP - -#include - -#include -#include -#include - -#include - -#include - -namespace Slic3r { namespace GUI { - -// A class to handle UI jobs like arranging and optimizing rotation. -// These are not instant jobs, the user has to be informed about their -// state in the status progress indicator. On the other hand they are -// separated from the background slicing process. Ideally, these jobs should -// run when the background process is not running. -// -// TODO: A mechanism would be useful for blocking the plater interactions: -// objects would be frozen for the user. In case of arrange, an animation -// could be shown, or with the optimize orientations, partial results -// could be displayed. -class Job : public wxEvtHandler -{ - int m_range = 100; - boost::thread m_thread; - std::atomic m_running{false}, m_canceled{false}; - bool m_finalized = false; - std::shared_ptr m_progress; - - void run() - { - m_running.store(true); - process(); - m_running.store(false); - - // ensure to call the last status to finalize the job - update_status(status_range(), ""); - } - -protected: - // status range for a particular job - virtual int status_range() const { return 100; } - - // status update, to be used from the work thread (process() method) - void update_status(int st, const wxString &msg = "") - { - auto evt = new wxThreadEvent(); - evt->SetInt(st); - evt->SetString(msg); - wxQueueEvent(this, evt); - } - - bool was_canceled() const { return m_canceled.load(); } - - // Launched just before start(), a job can use it to prepare internals - virtual void prepare() {} - - // Launched when the job is finished. It refreshes the 3Dscene by def. - virtual void finalize() { m_finalized = true; } - - -public: - Job(std::shared_ptr pri) : m_progress(pri) - { - Bind(wxEVT_THREAD, [this](const wxThreadEvent &evt) { - auto msg = evt.GetString(); - if (!msg.empty()) - m_progress->set_status_text(msg.ToUTF8().data()); - - if (m_finalized) return; - - m_progress->set_progress(evt.GetInt()); - if (evt.GetInt() == status_range()) { - // set back the original range and cancel callback - m_progress->set_range(m_range); - m_progress->set_cancel_callback(); - wxEndBusyCursor(); - - finalize(); - - // dont do finalization again for the same process - m_finalized = true; - } - }); - } - - bool is_finalized() const { return m_finalized; } - - Job(const Job &) = delete; - Job(Job &&) = delete; - Job &operator=(const Job &) = delete; - Job &operator=(Job &&) = delete; - - virtual void process() = 0; - - void start() - { // Start the job. No effect if the job is already running - if (!m_running.load()) { - prepare(); - - // Save the current status indicatior range and push the new one - m_range = m_progress->get_range(); - m_progress->set_range(status_range()); - - // init cancellation flag and set the cancel callback - m_canceled.store(false); - m_progress->set_cancel_callback( - [this]() { m_canceled.store(true); }); - - m_finalized = false; - - // Changing cursor to busy - wxBeginBusyCursor(); - - try { // Execute the job - m_thread = create_thread([this] { this->run(); }); - } catch (std::exception &) { - update_status(status_range(), - _(L("ERROR: not enough resources to " - "execute a new job."))); - } - - // The state changes will be undone when the process hits the - // last status value, in the status update handler (see ctor) - } - } - - // To wait for the running job and join the threads. False is - // returned if the timeout has been reached and the job is still - // running. Call cancel() before this fn if you want to explicitly - // end the job. - bool join(int timeout_ms = 0) - { - if (!m_thread.joinable()) return true; - - if (timeout_ms <= 0) - m_thread.join(); - else if (!m_thread.try_join_for(boost::chrono::milliseconds(timeout_ms))) - return false; - - return true; - } - - bool is_running() const { return m_running.load(); } - void cancel() { m_canceled.store(true); } -}; - -} -} - -#endif // JOB_HPP diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp new file mode 100644 index 0000000000..00b9fb654d --- /dev/null +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -0,0 +1,223 @@ +#include "ArrangeJob.hpp" + +#include "libslic3r/MTUtils.hpp" + +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI.hpp" + +namespace Slic3r { namespace GUI { + +// Cache the wti info +class WipeTower: public GLCanvas3D::WipeTowerInfo { + using ArrangePolygon = arrangement::ArrangePolygon; +public: + explicit WipeTower(const GLCanvas3D::WipeTowerInfo &wti) + : GLCanvas3D::WipeTowerInfo(wti) + {} + + explicit WipeTower(GLCanvas3D::WipeTowerInfo &&wti) + : GLCanvas3D::WipeTowerInfo(std::move(wti)) + {} + + void apply_arrange_result(const Vec2d& tr, double rotation) + { + m_pos = unscaled(tr); m_rotation = rotation; + apply_wipe_tower(); + } + + ArrangePolygon get_arrange_polygon() const + { + Polygon ap({ + {coord_t(0), coord_t(0)}, + {scaled(m_bb_size(X)), coord_t(0)}, + {scaled(m_bb_size)}, + {coord_t(0), scaled(m_bb_size(Y))}, + {coord_t(0), coord_t(0)}, + }); + + ArrangePolygon ret; + ret.poly.contour = std::move(ap); + ret.translation = scaled(m_pos); + ret.rotation = m_rotation; + ret.priority++; + return ret; + } +}; + +static WipeTower get_wipe_tower(Plater &plater) +{ + return WipeTower{plater.canvas3D()->get_wipe_tower_info()}; +} + +void ArrangeJob::clear_input() +{ + const Model &model = m_plater->model(); + + size_t count = 0, cunprint = 0; // To know how much space to reserve + for (auto obj : model.objects) + for (auto mi : obj->instances) + mi->printable ? count++ : cunprint++; + + m_selected.clear(); + m_unselected.clear(); + m_unprintable.clear(); + m_selected.reserve(count + 1 /* for optional wti */); + m_unselected.reserve(count + 1 /* for optional wti */); + m_unprintable.reserve(cunprint /* for optional wti */); +} + +double ArrangeJob::bed_stride() const { + double bedwidth = m_plater->bed_shape_bb().size().x(); + return scaled((1. + LOGICAL_BED_GAP) * bedwidth); +} + +void ArrangeJob::prepare_all() { + clear_input(); + + for (ModelObject *obj: m_plater->model().objects) + for (ModelInstance *mi : obj->instances) { + ArrangePolygons & cont = mi->printable ? m_selected : m_unprintable; + cont.emplace_back(get_arrange_poly(mi)); + } + + if (auto wti = get_wipe_tower(*m_plater)) + m_selected.emplace_back(wti.get_arrange_polygon()); +} + +void ArrangeJob::prepare_selected() { + clear_input(); + + Model &model = m_plater->model(); + double stride = bed_stride(); + + std::vector + obj_sel(model.objects.size(), nullptr); + + for (auto &s : m_plater->get_selection().get_content()) + if (s.first < int(obj_sel.size())) + obj_sel[size_t(s.first)] = &s.second; + + // Go through the objects and check if inside the selection + for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) { + const Selection::InstanceIdxsList * instlist = obj_sel[oidx]; + ModelObject *mo = model.objects[oidx]; + + std::vector inst_sel(mo->instances.size(), false); + + if (instlist) + for (auto inst_id : *instlist) + inst_sel[size_t(inst_id)] = true; + + for (size_t i = 0; i < inst_sel.size(); ++i) { + ArrangePolygon &&ap = get_arrange_poly(mo->instances[i]); + + ArrangePolygons &cont = mo->instances[i]->printable ? + (inst_sel[i] ? m_selected : + m_unselected) : + m_unprintable; + + cont.emplace_back(std::move(ap)); + } + } + + if (auto wti = get_wipe_tower(*m_plater)) { + ArrangePolygon &&ap = get_arrange_poly(&wti); + + m_plater->get_selection().is_wipe_tower() ? + m_selected.emplace_back(std::move(ap)) : + m_unselected.emplace_back(std::move(ap)); + } + + // If the selection was empty arrange everything + if (m_selected.empty()) m_selected.swap(m_unselected); + + // The strides have to be removed from the fixed items. For the + // arrangeable (selected) items bed_idx is ignored and the + // translation is irrelevant. + for (auto &p : m_unselected) p.translation(X) -= p.bed_idx * stride; +} + +void ArrangeJob::prepare() +{ + wxGetKeyState(WXK_SHIFT) ? prepare_selected() : prepare_all(); +} + +void ArrangeJob::process() +{ + static const auto arrangestr = _(L("Arranging")); + + double dist = min_object_distance(*m_plater->config()); + + arrangement::ArrangeParams params; + params.min_obj_distance = scaled(dist); + + auto count = unsigned(m_selected.size() + m_unprintable.size()); + Points bedpts = get_bed_shape(*m_plater->config()); + + params.stopcondition = [this]() { return was_canceled(); }; + + try { + params.progressind = [this, count](unsigned st) { + st += m_unprintable.size(); + if (st > 0) update_status(int(count - st), arrangestr); + }; + + arrangement::arrange(m_selected, m_unselected, bedpts, params); + + params.progressind = [this, count](unsigned st) { + if (st > 0) update_status(int(count - st), arrangestr); + }; + + arrangement::arrange(m_unprintable, {}, bedpts, params); + } catch (std::exception & /*e*/) { + GUI::show_error(m_plater, + _(L("Could not arrange model objects! " + "Some geometries may be invalid."))); + } + + // finalize just here. + update_status(int(count), + was_canceled() ? _(L("Arranging canceled.")) + : _(L("Arranging done."))); +} + +void ArrangeJob::finalize() { + // Ignore the arrange result if aborted. + if (was_canceled()) return; + + // Unprintable items go to the last virtual bed + int beds = 0; + + // Apply the arrange result to all selected objects + for (ArrangePolygon &ap : m_selected) { + beds = std::max(ap.bed_idx, beds); + ap.apply(); + } + + // Get the virtual beds from the unselected items + for (ArrangePolygon &ap : m_unselected) + beds = std::max(ap.bed_idx, beds); + + // Move the unprintable items to the last virtual bed. + for (ArrangePolygon &ap : m_unprintable) { + ap.bed_idx += beds + 1; + ap.apply(); + } + + m_plater->update(); + + Job::finalize(); +} + +arrangement::ArrangePolygon get_wipe_tower_arrangepoly(Plater &plater) +{ + return WipeTower{plater.canvas3D()->get_wipe_tower_info()}.get_arrange_polygon(); +} + +void apply_wipe_tower_arrangepoly(Plater &plater, const arrangement::ArrangePolygon &ap) +{ + WipeTower{plater.canvas3D()->get_wipe_tower_info()}.apply_arrange_result(ap.translation.cast(), ap.rotation); +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.hpp b/src/slic3r/GUI/Jobs/ArrangeJob.hpp new file mode 100644 index 0000000000..bd097af6be --- /dev/null +++ b/src/slic3r/GUI/Jobs/ArrangeJob.hpp @@ -0,0 +1,77 @@ +#ifndef ARRANGEJOB_HPP +#define ARRANGEJOB_HPP + +#include "Job.hpp" +#include "libslic3r/Arrange.hpp" + +namespace Slic3r { namespace GUI { + +class Plater; + +class ArrangeJob : public Job +{ + Plater *m_plater; + + using ArrangePolygon = arrangement::ArrangePolygon; + using ArrangePolygons = arrangement::ArrangePolygons; + + // The gap between logical beds in the x axis expressed in ratio of + // the current bed width. + static const constexpr double LOGICAL_BED_GAP = 1. / 5.; + + ArrangePolygons m_selected, m_unselected, m_unprintable; + + // clear m_selected and m_unselected, reserve space for next usage + void clear_input(); + + // Stride between logical beds + double bed_stride() const; + + // Set up arrange polygon for a ModelInstance and Wipe tower + template ArrangePolygon get_arrange_poly(T *obj) const + { + ArrangePolygon ap = obj->get_arrange_polygon(); + ap.priority = 0; + ap.bed_idx = ap.translation.x() / bed_stride(); + ap.setter = [obj, this](const ArrangePolygon &p) { + if (p.is_arranged()) { + Vec2d t = p.translation.cast(); + t.x() += p.bed_idx * bed_stride(); + obj->apply_arrange_result(t, p.rotation); + } + }; + return ap; + } + + // Prepare all objects on the bed regardless of the selection + void prepare_all(); + + // Prepare the selected and unselected items separately. If nothing is + // selected, behaves as if everything would be selected. + void prepare_selected(); + +protected: + + void prepare() override; + +public: + ArrangeJob(std::shared_ptr pri, Plater *plater) + : Job{std::move(pri)}, m_plater{plater} + {} + + int status_range() const override + { + return int(m_selected.size() + m_unprintable.size()); + } + + void process() override; + + void finalize() override; +}; + +arrangement::ArrangePolygon get_wipe_tower_arrangepoly(Plater &); +void apply_wipe_tower_arrangepoly(Plater &plater, const arrangement::ArrangePolygon &ap); + +}} // namespace Slic3r::GUI + +#endif // ARRANGEJOB_HPP diff --git a/src/slic3r/GUI/Jobs/Job.cpp b/src/slic3r/GUI/Jobs/Job.cpp new file mode 100644 index 0000000000..cc2cb75f13 --- /dev/null +++ b/src/slic3r/GUI/Jobs/Job.cpp @@ -0,0 +1,121 @@ +#include + +#include "Job.hpp" +#include + +namespace Slic3r { + +void GUI::Job::run() +{ + m_running.store(true); + process(); + m_running.store(false); + + // ensure to call the last status to finalize the job + update_status(status_range(), ""); +} + +void GUI::Job::update_status(int st, const wxString &msg) +{ + auto evt = new wxThreadEvent(); + evt->SetInt(st); + evt->SetString(msg); + wxQueueEvent(this, evt); +} + +GUI::Job::Job(std::shared_ptr pri) + : m_progress(std::move(pri)) +{ + Bind(wxEVT_THREAD, [this](const wxThreadEvent &evt) { + auto msg = evt.GetString(); + if (!msg.empty()) + m_progress->set_status_text(msg.ToUTF8().data()); + + if (m_finalized) return; + + m_progress->set_progress(evt.GetInt()); + if (evt.GetInt() == status_range()) { + // set back the original range and cancel callback + m_progress->set_range(m_range); + m_progress->set_cancel_callback(); + wxEndBusyCursor(); + + finalize(); + + // dont do finalization again for the same process + m_finalized = true; + } + }); +} + +void GUI::Job::start() +{ // Start the job. No effect if the job is already running + if (!m_running.load()) { + prepare(); + + // Save the current status indicatior range and push the new one + m_range = m_progress->get_range(); + m_progress->set_range(status_range()); + + // init cancellation flag and set the cancel callback + m_canceled.store(false); + m_progress->set_cancel_callback( + [this]() { m_canceled.store(true); }); + + m_finalized = false; + + // Changing cursor to busy + wxBeginBusyCursor(); + + try { // Execute the job + m_thread = create_thread([this] { this->run(); }); + } catch (std::exception &) { + update_status(status_range(), + _(L("ERROR: not enough resources to " + "execute a new job."))); + } + + // The state changes will be undone when the process hits the + // last status value, in the status update handler (see ctor) + } +} + +bool GUI::Job::join(int timeout_ms) +{ + if (!m_thread.joinable()) return true; + + if (timeout_ms <= 0) + m_thread.join(); + else if (!m_thread.try_join_for(boost::chrono::milliseconds(timeout_ms))) + return false; + + return true; +} + +void GUI::ExclusiveJobGroup::start(size_t jid) { + assert(jid < m_jobs.size()); + stop_all(); + m_jobs[jid]->start(); +} + +void GUI::ExclusiveJobGroup::join_all(int wait_ms) +{ + std::vector aborted(m_jobs.size(), false); + + for (size_t jid = 0; jid < m_jobs.size(); ++jid) + aborted[jid] = m_jobs[jid]->join(wait_ms); + + if (!std::all_of(aborted.begin(), aborted.end(), [](bool t) { return t; })) + BOOST_LOG_TRIVIAL(error) << "Could not abort a job!"; +} + +bool GUI::ExclusiveJobGroup::is_any_running() const +{ + return std::any_of(m_jobs.begin(), m_jobs.end(), + [](const std::unique_ptr &j) { + return j->is_running(); + }); +} + +} + diff --git a/src/slic3r/GUI/Jobs/Job.hpp b/src/slic3r/GUI/Jobs/Job.hpp new file mode 100644 index 0000000000..130ca2ed9a --- /dev/null +++ b/src/slic3r/GUI/Jobs/Job.hpp @@ -0,0 +1,110 @@ +#ifndef JOB_HPP +#define JOB_HPP + +#include + +#include +#include + +#include "ProgressIndicator.hpp" + +#include + +#include + +namespace Slic3r { namespace GUI { + +// A class to handle UI jobs like arranging and optimizing rotation. +// These are not instant jobs, the user has to be informed about their +// state in the status progress indicator. On the other hand they are +// separated from the background slicing process. Ideally, these jobs should +// run when the background process is not running. +// +// TODO: A mechanism would be useful for blocking the plater interactions: +// objects would be frozen for the user. In case of arrange, an animation +// could be shown, or with the optimize orientations, partial results +// could be displayed. +class Job : public wxEvtHandler +{ + int m_range = 100; + boost::thread m_thread; + std::atomic m_running{false}, m_canceled{false}; + bool m_finalized = false; + std::shared_ptr m_progress; + + void run(); + +protected: + // status range for a particular job + virtual int status_range() const { return 100; } + + // status update, to be used from the work thread (process() method) + void update_status(int st, const wxString &msg = ""); + + bool was_canceled() const { return m_canceled.load(); } + + // Launched just before start(), a job can use it to prepare internals + virtual void prepare() {} + + // Launched when the job is finished. It refreshes the 3Dscene by def. + virtual void finalize() { m_finalized = true; } + +public: + Job(std::shared_ptr pri); + + bool is_finalized() const { return m_finalized; } + + Job(const Job &) = delete; + Job(Job &&) = delete; + Job &operator=(const Job &) = delete; + Job &operator=(Job &&) = delete; + + virtual void process() = 0; + + void start(); + + // To wait for the running job and join the threads. False is + // returned if the timeout has been reached and the job is still + // running. Call cancel() before this fn if you want to explicitly + // end the job. + bool join(int timeout_ms = 0); + + bool is_running() const { return m_running.load(); } + void cancel() { m_canceled.store(true); } +}; + +// Jobs defined inside the group class will be managed so that only one can +// run at a time. Also, the background process will be stopped if a job is +// started. +class ExclusiveJobGroup +{ + static const int ABORT_WAIT_MAX_MS = 10000; + + std::vector> m_jobs; + +protected: + virtual void before_start() {} + +public: + virtual ~ExclusiveJobGroup() = default; + + size_t add_job(std::unique_ptr &&job) + { + m_jobs.emplace_back(std::move(job)); + return m_jobs.size() - 1; + } + + void start(size_t jid); + + void cancel_all() { for (auto& j : m_jobs) j->cancel(); } + + void join_all(int wait_ms = 0); + + void stop_all() { cancel_all(); join_all(ABORT_WAIT_MAX_MS); } + + bool is_any_running() const; +}; + +}} // namespace Slic3r::GUI + +#endif // JOB_HPP diff --git a/src/slic3r/GUI/ProgressIndicator.hpp b/src/slic3r/GUI/Jobs/ProgressIndicator.hpp similarity index 100% rename from src/slic3r/GUI/ProgressIndicator.hpp rename to src/slic3r/GUI/Jobs/ProgressIndicator.hpp diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp new file mode 100644 index 0000000000..4cc34e71c2 --- /dev/null +++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.cpp @@ -0,0 +1,68 @@ +#include "RotoptimizeJob.hpp" + +#include "libslic3r/MTUtils.hpp" +#include "libslic3r/SLA/Rotfinder.hpp" +#include "libslic3r/MinAreaBoundingBox.hpp" + +#include "slic3r/GUI/Plater.hpp" + +namespace Slic3r { namespace GUI { + +void RotoptimizeJob::process() +{ + int obj_idx = m_plater->get_selected_object_idx(); + if (obj_idx < 0) { return; } + + ModelObject *o = m_plater->model().objects[size_t(obj_idx)]; + + auto r = sla::find_best_rotation( + *o, + .005f, + [this](unsigned s) { + if (s < 100) + update_status(int(s), + _(L("Searching for optimal orientation"))); + }, + [this]() { return was_canceled(); }); + + + double mindist = 6.0; // FIXME + + if (!was_canceled()) { + for(ModelInstance * oi : o->instances) { + oi->set_rotation({r[X], r[Y], r[Z]}); + + auto trmatrix = oi->get_transformation().get_matrix(); + Polygon trchull = o->convex_hull_2d(trmatrix); + + MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex); + double phi = rotbb.angle_to_X(); + + // The box should be landscape + if(rotbb.width() < rotbb.height()) phi += PI / 2; + + Vec3d rt = oi->get_rotation(); rt(Z) += phi; + + oi->set_rotation(rt); + } + + m_plater->find_new_position(o->instances, scaled(mindist)); + + // Correct the z offset of the object which was corrupted be + // the rotation + o->ensure_on_bed(); + } + + update_status(100, was_canceled() ? _(L("Orientation search canceled.")) : + _(L("Orientation found."))); +} + +void RotoptimizeJob::finalize() +{ + if (!was_canceled()) + m_plater->update(); + + Job::finalize(); +} + +}} diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp new file mode 100644 index 0000000000..983c43c684 --- /dev/null +++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp @@ -0,0 +1,24 @@ +#ifndef ROTOPTIMIZEJOB_HPP +#define ROTOPTIMIZEJOB_HPP + +#include "Job.hpp" + +namespace Slic3r { namespace GUI { + +class Plater; + +class RotoptimizeJob : public Job +{ + Plater *m_plater; +public: + RotoptimizeJob(std::shared_ptr pri, Plater *plater) + : Job{std::move(pri)}, m_plater{plater} + {} + + void process() override; + void finalize() override; +}; + +}} // namespace Slic3r::GUI + +#endif // ROTOPTIMIZEJOB_HPP diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp new file mode 100644 index 0000000000..e791bf94e1 --- /dev/null +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -0,0 +1,226 @@ +#include "SLAImportJob.hpp" + +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/AppConfig.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/PresetBundle.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/Utils/SLAImport.hpp" + +#include +#include +#include +#include +#include + +namespace Slic3r { namespace GUI { + +enum class Sel { modelAndProfile, profileOnly, modelOnly}; + +class ImportDlg: public wxDialog { + wxFilePickerCtrl *m_filepicker; + wxComboBox *m_import_dropdown, *m_quality_dropdown; + +public: + ImportDlg(Plater *plater) + : wxDialog{plater, wxID_ANY, "Import SLA archive"} + { + auto szvert = new wxBoxSizer{wxVERTICAL}; + auto szfilepck = new wxBoxSizer{wxHORIZONTAL}; + + m_filepicker = new wxFilePickerCtrl(this, wxID_ANY, + from_u8(wxGetApp().app_config->get_last_dir()), _(L("Choose SLA archive:")), + "SL1 archive files (*.sl1, *.zip)|*.sl1;*.SL1;*.zip;*.ZIP", + wxDefaultPosition, wxDefaultSize, wxFLP_DEFAULT_STYLE | wxFD_OPEN | wxFD_FILE_MUST_EXIST); + + szfilepck->Add(new wxStaticText(this, wxID_ANY, _(L("Import file: "))), 0, wxALIGN_CENTER); + szfilepck->Add(m_filepicker, 1); + szvert->Add(szfilepck, 0, wxALL | wxEXPAND, 5); + + auto szchoices = new wxBoxSizer{wxHORIZONTAL}; + + static const std::vector inp_choices = { + _(L("Import model and profile")), + _(L("Import profile only")), + _(L("Import model only")) + }; + + m_import_dropdown = new wxComboBox( + this, wxID_ANY, inp_choices[0], wxDefaultPosition, wxDefaultSize, + inp_choices.size(), inp_choices.data(), wxCB_READONLY | wxCB_DROPDOWN); + + szchoices->Add(m_import_dropdown); + szchoices->Add(new wxStaticText(this, wxID_ANY, _(L("Quality: "))), 0, wxALIGN_CENTER | wxALL, 5); + + static const std::vector qual_choices = { + _(L("Accurate")), + _(L("Balanced")), + _(L("Quick")) + }; + + m_quality_dropdown = new wxComboBox( + this, wxID_ANY, qual_choices[0], wxDefaultPosition, wxDefaultSize, + qual_choices.size(), qual_choices.data(), wxCB_READONLY | wxCB_DROPDOWN); + szchoices->Add(m_quality_dropdown); + + m_import_dropdown->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) { + if (get_selection() == Sel::profileOnly) + m_quality_dropdown->Disable(); + else m_quality_dropdown->Enable(); + }); + + szvert->Add(szchoices, 0, wxALL, 5); + szvert->AddStretchSpacer(1); + auto szbtn = new wxBoxSizer(wxHORIZONTAL); + szbtn->Add(new wxButton{this, wxID_CANCEL}); + szbtn->Add(new wxButton{this, wxID_OK}); + szvert->Add(szbtn, 0, wxALIGN_RIGHT | wxALL, 5); + + SetSizerAndFit(szvert); + } + + Sel get_selection() const + { + int sel = m_import_dropdown->GetSelection(); + return Sel(std::min(int(Sel::modelOnly), std::max(0, sel))); + } + + Vec2i get_marchsq_windowsize() const + { + enum { Accurate, Balanced, Fast}; + + switch(m_quality_dropdown->GetSelection()) + { + case Fast: return {8, 8}; + case Balanced: return {4, 4}; + default: + case Accurate: + return {2, 2}; + } + } + + wxString get_path() const + { + return m_filepicker->GetPath(); + } +}; + +class SLAImportJob::priv { +public: + Plater *plater; + + Sel sel = Sel::modelAndProfile; + + TriangleMesh mesh; + DynamicPrintConfig profile; + wxString path; + Vec2i win = {2, 2}; + std::string err; + + priv(Plater *plt): plater{plt} {} +}; + +SLAImportJob::SLAImportJob(std::shared_ptr pri, Plater *plater) + : Job{std::move(pri)}, p{std::make_unique(plater)} +{} + +SLAImportJob::~SLAImportJob() = default; + +void SLAImportJob::process() +{ + auto progr = [this](int s) { + if (s < 100) update_status(int(s), _(L("Importing SLA archive"))); + return !was_canceled(); + }; + + if (p->path.empty()) return; + + std::string path = p->path.ToUTF8().data(); + try { + switch (p->sel) { + case Sel::modelAndProfile: + import_sla_archive(path, p->win, p->mesh, p->profile, progr); + break; + case Sel::modelOnly: + import_sla_archive(path, p->win, p->mesh, progr); + break; + case Sel::profileOnly: + import_sla_archive(path, p->profile); + break; + } + + } catch (std::exception &ex) { + p->err = ex.what(); + } + + update_status(100, was_canceled() ? _(L("Importing canceled.")) : + _(L("Importing done."))); +} + +void SLAImportJob::reset() +{ + p->sel = Sel::modelAndProfile; + p->mesh = {}; + p->profile = {}; + p->win = {2, 2}; + p->path.Clear(); +} + +void SLAImportJob::prepare() +{ + reset(); + + ImportDlg dlg{p->plater}; + + if (dlg.ShowModal() == wxID_OK) { + auto path = dlg.get_path(); + auto nm = wxFileName(path); + p->path = !nm.Exists(wxFILE_EXISTS_REGULAR) ? "" : path.ToUTF8(); + p->sel = dlg.get_selection(); + p->win = dlg.get_marchsq_windowsize(); + } else { + p->path = ""; + } +} + +void SLAImportJob::finalize() +{ + // Ignore the arrange result if aborted. + if (was_canceled()) return; + + if (!p->err.empty()) { + show_error(p->plater, p->err); + p->err = ""; + return; + } + + std::string name = wxFileName(p->path).GetName().ToUTF8().data(); + + if (!p->profile.empty()) { + const ModelObjectPtrs& objects = p->plater->model().objects; + for (auto object : objects) + if (object->volumes.size() > 1) + { + Slic3r::GUI::show_info(nullptr, + _(L("You cannot load SLA project with a multi-part object on the bed")) + "\n\n" + + _(L("Please check your object list before preset changing.")), + _(L("Attention!")) ); + return; + } + + DynamicPrintConfig config = {}; + config.apply(SLAFullPrintConfig::defaults()); + config += std::move(p->profile); + + wxGetApp().preset_bundle->load_config_model(name, std::move(config)); + wxGetApp().load_current_presets(); + } + + if (!p->mesh.empty()) + p->plater->sidebar().obj_list()->load_mesh_object(p->mesh, name); + + reset(); +} + +}} diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.hpp b/src/slic3r/GUI/Jobs/SLAImportJob.hpp new file mode 100644 index 0000000000..cff6cc899b --- /dev/null +++ b/src/slic3r/GUI/Jobs/SLAImportJob.hpp @@ -0,0 +1,31 @@ +#ifndef SLAIMPORTJOB_HPP +#define SLAIMPORTJOB_HPP + +#include "Job.hpp" + +namespace Slic3r { namespace GUI { + +class Plater; + +class SLAImportJob : public Job { + class priv; + + std::unique_ptr p; + +public: + SLAImportJob(std::shared_ptr pri, Plater *plater); + ~SLAImportJob(); + + void process() override; + + void reset(); + +protected: + void prepare() override; + + void finalize() override; +}; + +}} // namespace Slic3r::GUI + +#endif // SLAIMPORTJOB_HPP diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 6a1964506c..f7d7a6cac0 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -589,6 +589,11 @@ void MainFrame::init_menubar() append_menu_item(import_menu, wxID_ANY, _(L("Import STL/OBJ/AM&F/3MF")) + dots + "\tCtrl+I", _(L("Load a model")), [this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, "import_plater", nullptr, [this](){return m_plater != nullptr; }, this); + + append_menu_item(import_menu, wxID_ANY, _(L("Import SL1 archive")) + dots, _(L("Load an SL1 output archive")), + [this](wxCommandEvent&) { if (m_plater) m_plater->import_sl1_archive(); }, "import_plater", nullptr, + [this](){return m_plater != nullptr; }, this); + import_menu->AppendSeparator(); append_menu_item(import_menu, wxID_ANY, _(L("Import &Config")) + dots + "\tCtrl+L", _(L("Load exported configuration file")), [this](wxCommandEvent&) { load_config_file(); }, "import_config", nullptr, diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 37b6efd87e..6c4f5e49d5 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -85,19 +85,25 @@ void MeshClipper::recalculate_triangles() tr = m_trafo.get_matrix().cast() * tr; m_triangles3d.clear(); - m_triangles3d.reserve(m_triangles2d.size()); - for (const Vec2f& pt : m_triangles2d) { - m_triangles3d.push_back(Vec3f(pt(0), pt(1), height_mesh+0.001f)); - m_triangles3d.back() = tr * m_triangles3d.back(); - } + m_triangles3d.reserve(m_triangles2d.size()); + for (const Vec2f& pt : m_triangles2d) { + m_triangles3d.push_back(Vec3f(pt(0), pt(1), height_mesh+0.001f)); + m_triangles3d.back() = tr * m_triangles3d.back(); + } m_triangles_valid = true; } +Vec3f MeshRaycaster::get_triangle_normal(const indexed_triangle_set& its, size_t facet_idx) +{ + Vec3f a(its.vertices[its.indices[facet_idx](1)] - its.vertices[its.indices[facet_idx](0)]); + Vec3f b(its.vertices[its.indices[facet_idx](2)] - its.vertices[its.indices[facet_idx](0)]); + return Vec3f(a.cross(b)).normalized(); +} -bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, - Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane) const +void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, + Vec3d& point, Vec3d& direction) const { const std::array& viewport = camera.get_viewport(); const Transform3d& model_mat = camera.get_view_matrix(); @@ -112,7 +118,21 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& pt1 = inv * pt1; pt2 = inv * pt2; - std::vector hits = m_emesh.query_ray_hits(pt1, pt2-pt1); + point = pt1; + direction = pt2-pt1; +} + + +bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, + Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane, + size_t* facet_idx) const +{ + Vec3d point; + Vec3d direction; + line_from_mouse_pos(mouse_pos, trafo, camera, point, direction); + + std::vector hits = m_emesh.query_ray_hits(point, direction); + if (hits.empty()) return false; // no intersection found @@ -134,6 +154,10 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& // Now stuff the points in the provided vector and calculate normals if asked about them: position = hits[i].position().cast(); normal = hits[i].normal().cast(); + + if (facet_idx) + *facet_idx = hits[i].face(); + return true; } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index b4ad030114..92f444f55b 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -4,6 +4,8 @@ #include "libslic3r/Point.hpp" #include "libslic3r/Geometry.hpp" #include "libslic3r/SLA/EigenMesh3D.hpp" +#include "admesh/stl.h" + #include @@ -26,10 +28,7 @@ class ClippingPlane public: ClippingPlane() { - m_data[0] = 0.0; - m_data[1] = 0.0; - m_data[2] = 1.0; - m_data[3] = 0.0; + *this = ClipsNothing(); } ClippingPlane(const Vec3d& direction, double offset) @@ -111,6 +110,9 @@ public: : m_emesh(mesh) {} + void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, + Vec3d& point, Vec3d& direction) const; + // Given a mouse position, this returns true in case it is on the mesh. bool unproject_on_mesh( const Vec2d& mouse_pos, @@ -118,7 +120,8 @@ public: const Camera& camera, // current camera position Vec3f& position, // where to save the positibon of the hit (mesh coords) Vec3f& normal, // normal of the triangle that was hit - const ClippingPlane* clipping_plane = nullptr // clipping plane (if active) + const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active) + size_t* facet_idx = nullptr // index of the facet hit ) const; // Given a vector of points in woorld coordinates, this returns vector @@ -134,8 +137,11 @@ public: // Given a point in world coords, the method returns closest point on the mesh. // The output is in mesh coords. // normal* can be used to also get normal of the respective triangle. + Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const; + static Vec3f get_triangle_normal(const indexed_triangle_set& its, size_t facet_idx); + private: sla::EigenMesh3D m_emesh; }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c7c5798cee..db9e7e59a5 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -36,7 +36,6 @@ #include "libslic3r/GCode/ThumbnailData.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/SLA/Hollowing.hpp" -#include "libslic3r/SLA/Rotfinder.hpp" #include "libslic3r/SLA/SupportPoint.hpp" #include "libslic3r/Polygon.hpp" #include "libslic3r/Print.hpp" @@ -44,13 +43,6 @@ #include "libslic3r/SLAPrint.hpp" #include "libslic3r/Utils.hpp" -//#include "libslic3r/ClipperUtils.hpp" - -// #include "libnest2d/optimizers/nlopt/genetic.hpp" -// #include "libnest2d/backends/clipper/geometries.hpp" -// #include "libnest2d/utils/rotcalipers.hpp" -#include "libslic3r/MinAreaBoundingBox.hpp" - #include "GUI.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" @@ -69,7 +61,9 @@ #include "Camera.hpp" #include "Mouse3DController.hpp" #include "Tab.hpp" -#include "Job.hpp" +#include "Jobs/ArrangeJob.hpp" +#include "Jobs/RotoptimizeJob.hpp" +#include "Jobs/SLAImportJob.hpp" #include "PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" #include "ProgressStatusBar.hpp" @@ -1485,311 +1479,44 @@ struct Plater::priv BackgroundSlicingProcess background_process; bool suppressed_backround_processing_update { false }; - // Cache the wti info - class WipeTower: public GLCanvas3D::WipeTowerInfo { - using ArrangePolygon = arrangement::ArrangePolygon; - friend priv; - public: - - void apply_arrange_result(const Vec2d& tr, double rotation) - { - m_pos = unscaled(tr); m_rotation = rotation; - apply_wipe_tower(); - } - - ArrangePolygon get_arrange_polygon() const - { - Polygon p({ - {coord_t(0), coord_t(0)}, - {scaled(m_bb_size(X)), coord_t(0)}, - {scaled(m_bb_size)}, - {coord_t(0), scaled(m_bb_size(Y))}, - {coord_t(0), coord_t(0)}, - }); - - ArrangePolygon ret; - ret.poly.contour = std::move(p); - ret.translation = scaled(m_pos); - ret.rotation = m_rotation; - ret.priority++; - return ret; - } - } wipetower; - - WipeTower& updated_wipe_tower() { - auto wti = view3D->get_canvas3d()->get_wipe_tower_info(); - wipetower.m_pos = wti.pos(); - wipetower.m_rotation = wti.rotation(); - wipetower.m_bb_size = wti.bb_size(); - return wipetower; - } - - // A class to handle UI jobs like arranging and optimizing rotation. - // These are not instant jobs, the user has to be informed about their - // state in the status progress indicator. On the other hand they are - // separated from the background slicing process. Ideally, these jobs should - // run when the background process is not running. - // - // TODO: A mechanism would be useful for blocking the plater interactions: - // objects would be frozen for the user. In case of arrange, an animation - // could be shown, or with the optimize orientations, partial results - // could be displayed. - class PlaterJob: public Job - { - priv *m_plater; - protected: - - priv & plater() { return *m_plater; } - const priv &plater() const { return *m_plater; } - - // Launched when the job is finished. It refreshes the 3Dscene by def. - void finalize() override - { - // Do a full refresh of scene tree, including regenerating - // all the GLVolumes. FIXME The update function shall just - // reload the modified matrices. - if (!Job::was_canceled()) - plater().update(unsigned(UpdateParams::FORCE_FULL_SCREEN_REFRESH)); - - Job::finalize(); - } - - public: - PlaterJob(priv *_plater) - : Job(_plater->statusbar()), m_plater(_plater) - {} - }; - - enum class Jobs : size_t { - Arrange, - Rotoptimize - }; - - class ArrangeJob : public PlaterJob - { - using ArrangePolygon = arrangement::ArrangePolygon; - using ArrangePolygons = arrangement::ArrangePolygons; - - // The gap between logical beds in the x axis expressed in ratio of - // the current bed width. - static const constexpr double LOGICAL_BED_GAP = 1. / 5.; - - ArrangePolygons m_selected, m_unselected, m_unprintable; - - // clear m_selected and m_unselected, reserve space for next usage - void clear_input() { - const Model &model = plater().model; - - size_t count = 0, cunprint = 0; // To know how much space to reserve - for (auto obj : model.objects) - for (auto mi : obj->instances) - mi->printable ? count++ : cunprint++; - - m_selected.clear(); - m_unselected.clear(); - m_unprintable.clear(); - m_selected.reserve(count + 1 /* for optional wti */); - m_unselected.reserve(count + 1 /* for optional wti */); - m_unprintable.reserve(cunprint /* for optional wti */); - } - - // Stride between logical beds - double bed_stride() const { - double bedwidth = plater().bed_shape_bb().size().x(); - return scaled((1. + LOGICAL_BED_GAP) * bedwidth); - } - - // Set up arrange polygon for a ModelInstance and Wipe tower - template ArrangePolygon get_arrange_poly(T *obj) const { - ArrangePolygon ap = obj->get_arrange_polygon(); - ap.priority = 0; - ap.bed_idx = ap.translation.x() / bed_stride(); - ap.setter = [obj, this](const ArrangePolygon &p) { - if (p.is_arranged()) { - Vec2d t = p.translation.cast(); - t.x() += p.bed_idx * bed_stride(); - obj->apply_arrange_result(t, p.rotation); - } - }; - return ap; - } - - // Prepare all objects on the bed regardless of the selection - void prepare_all() { - clear_input(); - - for (ModelObject *obj: plater().model.objects) - for (ModelInstance *mi : obj->instances) { - ArrangePolygons & cont = mi->printable ? m_selected : m_unprintable; - cont.emplace_back(get_arrange_poly(mi)); - } - - auto& wti = plater().updated_wipe_tower(); - if (wti) m_selected.emplace_back(get_arrange_poly(&wti)); - } - - // Prepare the selected and unselected items separately. If nothing is - // selected, behaves as if everything would be selected. - void prepare_selected() { - clear_input(); - - Model &model = plater().model; - coord_t stride = bed_stride(); - - std::vector - obj_sel(model.objects.size(), nullptr); - - for (auto &s : plater().get_selection().get_content()) - if (s.first < int(obj_sel.size())) - obj_sel[size_t(s.first)] = &s.second; - - // Go through the objects and check if inside the selection - for (size_t oidx = 0; oidx < model.objects.size(); ++oidx) { - const Selection::InstanceIdxsList * instlist = obj_sel[oidx]; - ModelObject *mo = model.objects[oidx]; - - std::vector inst_sel(mo->instances.size(), false); - - if (instlist) - for (auto inst_id : *instlist) - inst_sel[size_t(inst_id)] = true; - - for (size_t i = 0; i < inst_sel.size(); ++i) { - ArrangePolygon &&ap = get_arrange_poly(mo->instances[i]); - - ArrangePolygons &cont = mo->instances[i]->printable ? - (inst_sel[i] ? m_selected : - m_unselected) : - m_unprintable; - - cont.emplace_back(std::move(ap)); - } - } - - auto& wti = plater().updated_wipe_tower(); - if (wti) { - ArrangePolygon &&ap = get_arrange_poly(&wti); - - plater().get_selection().is_wipe_tower() ? - m_selected.emplace_back(std::move(ap)) : - m_unselected.emplace_back(std::move(ap)); - } - - // If the selection was empty arrange everything - if (m_selected.empty()) m_selected.swap(m_unselected); - - // The strides have to be removed from the fixed items. For the - // arrangeable (selected) items bed_idx is ignored and the - // translation is irrelevant. - for (auto &p : m_unselected) p.translation(X) -= p.bed_idx * stride; - } - - protected: - - void prepare() override - { - wxGetKeyState(WXK_SHIFT) ? prepare_selected() : prepare_all(); - } - - public: - using PlaterJob::PlaterJob; - - int status_range() const override - { - return int(m_selected.size() + m_unprintable.size()); - } - - void process() override; - - void finalize() override { - // Ignore the arrange result if aborted. - if (was_canceled()) return; - - // Unprintable items go to the last virtual bed - int beds = 0; - - // Apply the arrange result to all selected objects - for (ArrangePolygon &ap : m_selected) { - beds = std::max(ap.bed_idx, beds); - ap.apply(); - } - - // Get the virtual beds from the unselected items - for (ArrangePolygon &ap : m_unselected) - beds = std::max(ap.bed_idx, beds); - - // Move the unprintable items to the last virtual bed. - for (ArrangePolygon &ap : m_unprintable) { - ap.bed_idx += beds + 1; - ap.apply(); - } - - plater().update(); - } - }; - - class RotoptimizeJob : public PlaterJob - { - public: - using PlaterJob::PlaterJob; - void process() override; - }; - - // Jobs defined inside the group class will be managed so that only one can // run at a time. Also, the background process will be stopped if a job is - // started. - class ExclusiveJobGroup { - - static const int ABORT_WAIT_MAX_MS = 10000; - - priv * m_plater; - - ArrangeJob arrange_job{m_plater}; - RotoptimizeJob rotoptimize_job{m_plater}; - - // To create a new job, just define a new subclass of Job, implement - // the process and the optional prepare() and finalize() methods - // Register the instance of the class in the m_jobs container - // if it cannot run concurrently with other jobs in this group - - std::vector> m_jobs{arrange_job, - rotoptimize_job}; - + // started. It is up the the plater to ensure that the background slicing + // can't be restarted while a ui job is still running. + class Jobs: public ExclusiveJobGroup + { + priv *m; + size_t m_arrange_id, m_rotoptimize_id, m_sla_import_id; + + void before_start() override { m->background_process.stop(); } + public: - ExclusiveJobGroup(priv *_plater) : m_plater(_plater) {} - - void start(Jobs jid) { - m_plater->background_process.stop(); - stop_all(); - m_jobs[size_t(jid)].get().start(); - } - - void cancel_all() { for (Job& j : m_jobs) j.cancel(); } - - void join_all(int wait_ms = 0) + Jobs(priv *_m) : m(_m) { - std::vector aborted(m_jobs.size(), false); - - for (size_t jid = 0; jid < m_jobs.size(); ++jid) - aborted[jid] = m_jobs[jid].get().join(wait_ms); - - if (!all_of(aborted)) - BOOST_LOG_TRIVIAL(error) << "Could not abort a job!"; + m_arrange_id = add_job(std::make_unique(m->statusbar(), m->q)); + m_rotoptimize_id = add_job(std::make_unique(m->statusbar(), m->q)); + m_sla_import_id = add_job(std::make_unique(m->statusbar(), m->q)); } - - void stop_all() { cancel_all(); join_all(ABORT_WAIT_MAX_MS); } - - const Job& get(Jobs jobid) const { return m_jobs[size_t(jobid)]; } - - bool is_any_running() const + + void arrange() { - return std::any_of(m_jobs.begin(), - m_jobs.end(), - [](const Job &j) { return j.is_running(); }); + m->take_snapshot(_(L("Arrange"))); + start(m_arrange_id); } - - } m_ui_jobs{this}; + + void optimize_rotation() + { + m->take_snapshot(_(L("Optimize Rotation"))); + start(m_rotoptimize_id); + } + + void import_sla_arch() + { + m->take_snapshot(_(L("Import SLA archive"))); + start(m_sla_import_id); + } + + } m_ui_jobs; bool delayed_scene_refresh; std::string delayed_error_message; @@ -1808,10 +1535,10 @@ struct Plater::priv priv(Plater *q, MainFrame *main_frame); ~priv(); - enum class UpdateParams { - FORCE_FULL_SCREEN_REFRESH = 1, - FORCE_BACKGROUND_PROCESSING_UPDATE = 2, - POSTPONE_VALIDATION_ERROR_MESSAGE = 4, + enum class UpdateParams { + FORCE_FULL_SCREEN_REFRESH = 1, + FORCE_BACKGROUND_PROCESSING_UPDATE = 2, + POSTPONE_VALIDATION_ERROR_MESSAGE = 4, }; void update(unsigned int flags = 0); void select_view(const std::string& direction); @@ -1847,9 +1574,7 @@ struct Plater::priv std::string get_config(const std::string &key) const; BoundingBoxf bed_shape_bb() const; BoundingBox scaled_bed_shape_bb() const; - arrangement::BedShapeHint get_bed_shape_hint() const; - void find_new_position(const ModelInstancePtrs &instances, coord_t min_d); std::vector load_files(const std::vector& input_files, bool load_model, bool load_config); std::vector load_model_objects(const ModelObjectPtrs &model_objects); wxString get_export_file(GUI::FileType file_type); @@ -1867,8 +1592,6 @@ struct Plater::priv void delete_object_from_model(size_t obj_idx); void reset(); void mirror(Axis axis); - void arrange(); - void sla_optimize_rotation(); void split_object(); void split_volume(); void scale_selection_to_fit_print_volume(); @@ -2035,6 +1758,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) "support_material", "support_material_extruder", "support_material_interface_extruder", "support_material_contact_distance", "raft_layers" })) , sidebar(new Sidebar(q)) + , m_ui_jobs(this) , delayed_scene_refresh(false) , view_toolbar(GLToolbar::Radio, "View") , m_project_filename(wxEmptyString) @@ -2110,14 +1834,15 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) sidebar->Bind(EVT_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); }); wxGLCanvas* view3D_canvas = view3D->get_wxglcanvas(); + // 3DScene events: view3D_canvas->Bind(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); }); view3D_canvas->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this); view3D_canvas->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this); view3D_canvas->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); }); - view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); }); + view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { this->q->arrange(); }); view3D_canvas->Bind(EVT_GLCANVAS_SELECT_ALL, [this](SimpleEvent&) { this->q->select_all(); }); - view3D_canvas->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); + view3D_canvas->Bind(EVT_GLCANVAS_QUESTION_MARK, [](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event &evt) { if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); }); view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); @@ -2142,7 +1867,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); }); view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE_ALL, [q](SimpleEvent&) { q->reset_with_confirm(); }); - view3D_canvas->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { arrange(); }); + view3D_canvas->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { this->q->arrange(); }); view3D_canvas->Bind(EVT_GLTOOLBAR_COPY, [q](SimpleEvent&) { q->copy_selection_to_clipboard(); }); view3D_canvas->Bind(EVT_GLTOOLBAR_PASTE, [q](SimpleEvent&) { q->paste_from_clipboard(); }); view3D_canvas->Bind(EVT_GLTOOLBAR_MORE, [q](SimpleEvent&) { q->increase_instances(); }); @@ -2810,40 +2535,12 @@ void Plater::priv::mirror(Axis axis) view3D->mirror_selection(axis); } -void Plater::priv::arrange() -{ - this->take_snapshot(_L("Arrange")); - m_ui_jobs.start(Jobs::Arrange); -} - - -// This method will find an optimal orientation for the currently selected item -// Very similar in nature to the arrange method above... -void Plater::priv::sla_optimize_rotation() { - this->take_snapshot(_L("Optimize Rotation")); - m_ui_jobs.start(Jobs::Rotoptimize); -} - -arrangement::BedShapeHint Plater::priv::get_bed_shape_hint() const { - - const auto *bed_shape_opt = config->opt("bed_shape"); - assert(bed_shape_opt); - - if (!bed_shape_opt) return {}; - - auto &bedpoints = bed_shape_opt->values; - Polyline bedpoly; bedpoly.points.reserve(bedpoints.size()); - for (auto &v : bedpoints) bedpoly.append(scaled(v)); - - return arrangement::BedShapeHint(bedpoly); -} - -void Plater::priv::find_new_position(const ModelInstancePtrs &instances, +void Plater::find_new_position(const ModelInstancePtrs &instances, coord_t min_d) { arrangement::ArrangePolygons movable, fixed; - - for (const ModelObject *mo : model.objects) + + for (const ModelObject *mo : p->model.objects) for (const ModelInstance *inst : mo->instances) { auto it = std::find(instances.begin(), instances.end(), inst); auto arrpoly = inst->get_arrange_polygon(); @@ -2853,11 +2550,12 @@ void Plater::priv::find_new_position(const ModelInstancePtrs &instances, else movable.emplace_back(std::move(arrpoly)); } - - if (updated_wipe_tower()) - fixed.emplace_back(wipetower.get_arrange_polygon()); - - arrangement::arrange(movable, fixed, min_d, get_bed_shape_hint()); + + if (p->view3D->get_canvas3d()->get_wipe_tower_info()) + fixed.emplace_back(get_wipe_tower_arrangepoly(*this)); + + arrangement::arrange(movable, fixed, get_bed_shape(*config()), + arrangement::ArrangeParams{min_d}); for (size_t i = 0; i < instances.size(); ++i) if (movable[i].bed_idx == 0) @@ -2865,95 +2563,6 @@ void Plater::priv::find_new_position(const ModelInstancePtrs &instances, movable[i].rotation); } -void Plater::priv::ArrangeJob::process() { - static const auto arrangestr = _L("Arranging"); - - // FIXME: I don't know how to obtain the minimum distance, it depends - // on printer technology. I guess the following should work but it crashes. - double dist = 6; // PrintConfig::min_object_distance(config); - if (plater().printer_technology == ptFFF) { - dist = PrintConfig::min_object_distance(plater().config); - } - - coord_t min_d = scaled(dist); - auto count = unsigned(m_selected.size() + m_unprintable.size()); - arrangement::BedShapeHint bedshape = plater().get_bed_shape_hint(); - - auto stopfn = [this]() { return was_canceled(); }; - - try { - arrangement::arrange(m_selected, m_unselected, min_d, bedshape, - [this, count](unsigned st) { - st += m_unprintable.size(); - if (st > 0) update_status(int(count - st), arrangestr); - }, stopfn); - arrangement::arrange(m_unprintable, {}, min_d, bedshape, - [this, count](unsigned st) { - if (st > 0) update_status(int(count - st), arrangestr); - }, stopfn); - } catch (std::exception & /*e*/) { - GUI::show_error(plater().q, - _L("Could not arrange model objects! " - "Some geometries may be invalid.")); - } - - // finalize just here. - update_status(int(count), - was_canceled() ? _L("Arranging canceled.") - : _L("Arranging done.")); -} - -void Plater::priv::RotoptimizeJob::process() -{ - int obj_idx = plater().get_selected_object_idx(); - if (obj_idx < 0) { return; } - - ModelObject *o = plater().model.objects[size_t(obj_idx)]; - - auto r = sla::find_best_rotation( - *o, - .005f, - [this](unsigned s) { - if (s < 100) - update_status(int(s), - _L("Searching for optimal orientation")); - }, - [this]() { return was_canceled(); }); - - - double mindist = 6.0; // FIXME - - if (!was_canceled()) { - for(ModelInstance * oi : o->instances) { - oi->set_rotation({r[X], r[Y], r[Z]}); - - auto trmatrix = oi->get_transformation().get_matrix(); - Polygon trchull = o->convex_hull_2d(trmatrix); - - MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex); - double r = rotbb.angle_to_X(); - - // The box should be landscape - if(rotbb.width() < rotbb.height()) r += PI / 2; - - Vec3d rt = oi->get_rotation(); rt(Z) += r; - - oi->set_rotation(rt); - } - - plater().find_new_position(o->instances, scaled(mindist)); - - // Correct the z offset of the object which was corrupted be - // the rotation - o->ensure_on_bed(); - } - - update_status(100, - was_canceled() ? _L("Orientation search canceled.") - : _L("Orientation found.")); -} - - void Plater::priv::split_object() { int obj_idx = get_selected_object_idx(); @@ -3594,7 +3203,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) } // update plater with new config - wxGetApp().plater()->on_config_change(wxGetApp().preset_bundle->full_config()); + q->on_config_change(wxGetApp().preset_bundle->full_config()); /* Settings list can be changed after printer preset changing, so * update all settings items for all item had it. * Furthermore, Layers editing is implemented only for FFF printers @@ -4041,8 +3650,12 @@ bool Plater::priv::complit_init_sla_object_menu() sla_object_menu.AppendSeparator(); // Add the automatic rotation sub-menu - append_menu_item(&sla_object_menu, wxID_ANY, _L("Optimize orientation"), _L("Optimize the rotation of the object for better print results."), - [this](wxCommandEvent&) { sla_optimize_rotation(); }); + append_menu_item( + &sla_object_menu, wxID_ANY, _(L("Optimize orientation")), + _(L("Optimize the rotation of the object for better print results.")), + [this](wxCommandEvent &) { + m_ui_jobs.optimize_rotation(); + }); return true; } @@ -4646,6 +4259,11 @@ void Plater::add_model() load_files(paths, true, false); } +void Plater::import_sl1_archive() +{ + p->m_ui_jobs.import_sla_arch(); +} + void Plater::extract_config_from_project() { wxString input_file; @@ -4738,7 +4356,7 @@ void Plater::increase_instances(size_t num) sidebar().obj_list()->increase_object_instances(obj_idx, was_one_instance ? num + 1 : num); if (p->get_config("autocenter") == "1") - p->arrange(); + arrange(); p->update(); @@ -5472,6 +5090,11 @@ bool Plater::is_export_gcode_scheduled() const return p->background_process.is_export_scheduled(); } +const Selection &Plater::get_selection() const +{ + return p->get_selection(); +} + int Plater::get_selected_object_idx() { return p->get_selected_object_idx(); @@ -5497,6 +5120,11 @@ BoundingBoxf Plater::bed_shape_bb() const return p->bed_shape_bb(); } +void Plater::arrange() +{ + p->m_ui_jobs.arrange(); +} + void Plater::set_current_canvas_as_dirty() { p->set_current_canvas_as_dirty(); @@ -5519,6 +5147,8 @@ PrinterTechnology Plater::printer_technology() const return p->printer_technology; } +const DynamicPrintConfig * Plater::config() const { return p->config; } + void Plater::set_printer_technology(PrinterTechnology printer_technology) { p->printer_technology = printer_technology; diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index efdaa75ccd..2ac4f23c18 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -9,8 +9,10 @@ #include #include "Preset.hpp" +#include "Selection.hpp" #include "libslic3r/BoundingBox.hpp" +#include "Jobs/Job.hpp" #include "wxExtensions.hpp" class wxButton; @@ -157,6 +159,7 @@ public: void load_project(); void load_project(const wxString& filename); void add_model(); + void import_sl1_archive(); void extract_config_from_project(); std::vector load_files(const std::vector& input_files, bool load_model = true, bool load_config = true); @@ -252,12 +255,16 @@ public: void set_project_filename(const wxString& filename); bool is_export_gcode_scheduled() const; - + + const Selection& get_selection() const; int get_selected_object_idx(); bool is_single_full_object_selection() const; GLCanvas3D* canvas3D(); GLCanvas3D* get_current_canvas3D(); BoundingBoxf bed_shape_bb() const; + + void arrange(); + void find_new_position(const ModelInstancePtrs &instances, coord_t min_d); void set_current_canvas_as_dirty(); #if ENABLE_NON_STATIC_CANVAS_MANAGER @@ -266,6 +273,7 @@ public: #endif // ENABLE_NON_STATIC_CANVAS_MANAGER PrinterTechnology printer_technology() const; + const DynamicPrintConfig * config() const; void set_printer_technology(PrinterTechnology printer_technology); void copy_selection_to_clipboard(); @@ -371,6 +379,7 @@ private: bool m_was_scheduled; }; -}} +} // namespace GUI +} // namespace Slic3r #endif diff --git a/src/slic3r/GUI/ProgressStatusBar.hpp b/src/slic3r/GUI/ProgressStatusBar.hpp index faeb7a34ef..15b10deeb2 100644 --- a/src/slic3r/GUI/ProgressStatusBar.hpp +++ b/src/slic3r/GUI/ProgressStatusBar.hpp @@ -6,7 +6,7 @@ #include #include -#include "ProgressIndicator.hpp" +#include "Jobs/ProgressIndicator.hpp" class wxTimer; class wxGauge; diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index c9f1de1684..b3f2791961 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -754,7 +754,8 @@ ModeSizer::ModeSizer(wxWindow *parent, int hgap/* = 0*/) : std::vector < std::pair < wxString, std::string >> buttons = { {_(L("Simple")), "mode_simple"}, - {_(L("Advanced")), "mode_advanced"}, +// {_(L("Advanced")), "mode_advanced"}, + {_CTX(L_CONTEXT("Advanced", "Mode"), "Mode"), "mode_advanced"}, {_(L("Expert")), "mode_expert"}, }; diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index 30e25abe27..101654c562 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -18,6 +18,8 @@ #include #endif +#define L(s) s + #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" @@ -32,7 +34,8 @@ namespace Slic3r { struct CurlGlobalInit { static std::unique_ptr instance; - + std::string message; + CurlGlobalInit() { #ifdef OPENSSL_CERT_OVERRIDE // defined if SLIC3R_STATIC=ON @@ -57,21 +60,39 @@ struct CurlGlobalInit ssl_cafile = X509_get_default_cert_file(); int replace = true; - - if (!ssl_cafile || !fs::exists(fs::path(ssl_cafile))) - for (const char * bundle : CA_BUNDLES) { - if (fs::exists(fs::path(bundle))) { - ::setenv(SSL_CA_FILE, bundle, replace); + if (!ssl_cafile || !fs::exists(fs::path(ssl_cafile))) { + const char * bundle = nullptr; + for (const char * b : CA_BUNDLES) { + if (fs::exists(fs::path(b))) { + ::setenv(SSL_CA_FILE, bundle = b, replace); break; } } - BOOST_LOG_TRIVIAL(info) - << "Detected OpenSSL root CA store: " << ::getenv(SSL_CA_FILE); + if (!bundle) + message = L("Could not detect system SSL certificate store. " + "PrusaSlicer will be unable to establish secure " + "network connections."); + else + message = string_printf( + L("PrusaSlicer detected system SSL certificate store in: %s"), + bundle); -#endif + message += string_printf( + L("\nTo specify the system certificate store manually, please " + "set the %s environment variable to the correct CA bundle " + "and restart the application."), + SSL_CA_FILE); + } + +#endif // OPENSSL_CERT_OVERRIDE - ::curl_global_init(CURL_GLOBAL_DEFAULT); + if (CURLcode ec = ::curl_global_init(CURL_GLOBAL_DEFAULT)) { + message = L("CURL init has failed. PrusaSlicer will be unable to establish " + "network connections. See logs for additional details."); + + BOOST_LOG_TRIVIAL(error) << ::curl_easy_strerror(ec); + } } ~CurlGlobalInit() { ::curl_global_cleanup(); } @@ -132,8 +153,7 @@ Http::priv::priv(const std::string &url) , limit(0) , cancel(false) { - if (!CurlGlobalInit::instance) - CurlGlobalInit::instance = std::make_unique(); + Http::tls_global_init(); if (curl == nullptr) { throw std::runtime_error(std::string("Could not construct Curl object")); @@ -494,7 +514,26 @@ bool Http::ca_file_supported() ::CURL *curl = ::curl_easy_init(); bool res = priv::ca_file_supported(curl); if (curl != nullptr) { ::curl_easy_cleanup(curl); } - return res; + return res; +} + +std::string Http::tls_global_init() +{ + if (!CurlGlobalInit::instance) + CurlGlobalInit::instance = std::make_unique(); + + return CurlGlobalInit::instance->message; +} + +std::string Http::tls_system_cert_store() +{ + std::string ret; + +#ifdef OPENSSL_CERT_OVERRIDE + ret = ::getenv(X509_get_default_cert_file_env()); +#endif + + return ret; } std::string Http::url_encode(const std::string &str) diff --git a/src/slic3r/Utils/Http.hpp b/src/slic3r/Utils/Http.hpp index 076fa4a0cc..f162362796 100644 --- a/src/slic3r/Utils/Http.hpp +++ b/src/slic3r/Utils/Http.hpp @@ -100,6 +100,10 @@ public: // Tells whether current backend supports seting up a CA file using ca_file() static bool ca_file_supported(); + + // Return empty string on success or error message on fail. + static std::string tls_global_init(); + static std::string tls_system_cert_store(); // converts the given string to an url_encoded_string static std::string url_encode(const std::string &str); diff --git a/src/slic3r/Utils/SLAImport.cpp b/src/slic3r/Utils/SLAImport.cpp new file mode 100644 index 0000000000..442025a77d --- /dev/null +++ b/src/slic3r/Utils/SLAImport.cpp @@ -0,0 +1,314 @@ +#include "SLAImport.hpp" + +#include + +#include "libslic3r/SlicesToTriangleMesh.hpp" +#include "libslic3r/MarchingSquares.hpp" +#include "libslic3r/ClipperUtils.hpp" +#include "libslic3r/MTUtils.hpp" +#include "libslic3r/PrintConfig.hpp" +#include "libslic3r/SLA/RasterBase.hpp" +#include "libslic3r/miniz_extension.hpp" + +#include +#include +#include + +#include +#include + +namespace marchsq { + +// Specialize this struct to register a raster type for the Marching squares alg +template<> struct _RasterTraits { + using Rst = wxImage; + + // The type of pixel cell in the raster + using ValueType = uint8_t; + + // Value at a given position + static uint8_t get(const Rst &rst, size_t row, size_t col) + { + return rst.GetRed(col, row); + } + + // Number of rows and cols of the raster + static size_t rows(const Rst &rst) { return rst.GetHeight(); } + static size_t cols(const Rst &rst) { return rst.GetWidth(); } +}; + +} // namespace marchsq + +namespace Slic3r { + +namespace { + +struct ArchiveData { + boost::property_tree::ptree profile, config; + std::vector images; +}; + +static const constexpr char *CONFIG_FNAME = "config.ini"; +static const constexpr char *PROFILE_FNAME = "prusaslicer.ini"; + +boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry, + MZ_Archive & zip) +{ + std::string buf(size_t(entry.m_uncomp_size), '\0'); + + if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, + buf.data(), buf.size(), 0)) + throw std::runtime_error(zip.get_errorstr()); + + boost::property_tree::ptree tree; + std::stringstream ss(buf); + boost::property_tree::read_ini(ss, tree); + return tree; +} + +sla::EncodedRaster read_png(const mz_zip_archive_file_stat &entry, + MZ_Archive & zip, + const std::string & name) +{ + std::vector buf(entry.m_uncomp_size); + + if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, + buf.data(), buf.size(), 0)) + throw std::runtime_error(zip.get_errorstr()); + + return sla::EncodedRaster(std::move(buf), + name.empty() ? entry.m_filename : name); +} + +ArchiveData extract_sla_archive(const std::string &zipfname, + const std::string &exclude) +{ + ArchiveData arch; + + // Little RAII + struct Arch: public MZ_Archive { + Arch(const std::string &fname) { + if (!open_zip_reader(&arch, fname)) + throw std::runtime_error(get_errorstr()); + } + + ~Arch() { close_zip_reader(&arch); } + } zip (zipfname); + + mz_uint num_entries = mz_zip_reader_get_num_files(&zip.arch); + + for (mz_uint i = 0; i < num_entries; ++i) + { + mz_zip_archive_file_stat entry; + + if (mz_zip_reader_file_stat(&zip.arch, i, &entry)) + { + std::string name = entry.m_filename; + boost::algorithm::to_lower(name); + + if (boost::algorithm::contains(name, exclude)) continue; + + if (name == CONFIG_FNAME) arch.config = read_ini(entry, zip); + if (name == PROFILE_FNAME) arch.profile = read_ini(entry, zip); + + if (boost::filesystem::path(name).extension().string() == ".png") { + auto it = std::lower_bound( + arch.images.begin(), arch.images.end(), sla::EncodedRaster({}, name), + [](const sla::EncodedRaster &r1, const sla::EncodedRaster &r2) { + return std::less()(r1.extension(), r2.extension()); + }); + + arch.images.insert(it, read_png(entry, zip, name)); + } + } + } + + return arch; +} + +ExPolygons rings_to_expolygons(const std::vector &rings, + double px_w, double px_h) +{ + ExPolygons polys; polys.reserve(rings.size()); + + for (const marchsq::Ring &ring : rings) { + Polygon poly; Points &pts = poly.points; + pts.reserve(ring.size()); + + for (const marchsq::Coord &crd : ring) + pts.emplace_back(scaled(crd.c * px_w), scaled(crd.r * px_h)); + + polys.emplace_back(poly); + } + + // reverse the raster transformations + return union_ex(polys); +} + +template void foreach_vertex(ExPolygon &poly, Fn &&fn) +{ + for (auto &p : poly.contour.points) fn(p); + for (auto &h : poly.holes) + for (auto &p : h.points) fn(p); +} + +void invert_raster_trafo(ExPolygons & expolys, + const sla::RasterBase::Trafo &trafo, + coord_t width, + coord_t height) +{ + for (auto &expoly : expolys) { + if (trafo.mirror_y) + foreach_vertex(expoly, [height](Point &p) {p.y() = height - p.y(); }); + + if (trafo.mirror_x) + foreach_vertex(expoly, [width](Point &p) {p.x() = width - p.x(); }); + + expoly.translate(-trafo.center_x, -trafo.center_y); + + if (trafo.flipXY) + foreach_vertex(expoly, [](Point &p) { std::swap(p.x(), p.y()); }); + + if ((trafo.mirror_x + trafo.mirror_y + trafo.flipXY) % 2) { + expoly.contour.reverse(); + for (auto &h : expoly.holes) h.reverse(); + } + } +} + +struct RasterParams { + sla::RasterBase::Trafo trafo; // Raster transformations + coord_t width, height; // scaled raster dimensions (not resolution) + double px_h, px_w; // pixel dimesions + marchsq::Coord win; // marching squares window size +}; + +RasterParams get_raster_params(const DynamicPrintConfig &cfg) +{ + auto *opt_disp_cols = cfg.option("display_pixels_x"); + auto *opt_disp_rows = cfg.option("display_pixels_y"); + auto *opt_disp_w = cfg.option("display_width"); + auto *opt_disp_h = cfg.option("display_height"); + auto *opt_mirror_x = cfg.option("display_mirror_x"); + auto *opt_mirror_y = cfg.option("display_mirror_y"); + auto *opt_orient = cfg.option>("display_orientation"); + + if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h || + !opt_mirror_x || !opt_mirror_y || !opt_orient) + throw std::runtime_error("Invalid SL1 file"); + + RasterParams rstp; + + rstp.px_w = opt_disp_w->value / (opt_disp_cols->value - 1); + rstp.px_h = opt_disp_h->value / (opt_disp_rows->value - 1); + + sla::RasterBase::Trafo trafo{opt_orient->value == sladoLandscape ? + sla::RasterBase::roLandscape : + sla::RasterBase::roPortrait, + {opt_mirror_x->value, opt_mirror_y->value}}; + + rstp.height = scaled(opt_disp_h->value); + rstp.width = scaled(opt_disp_w->value); + + return rstp; +} + +struct SliceParams { double layerh = 0., initial_layerh = 0.; }; + +SliceParams get_slice_params(const DynamicPrintConfig &cfg) +{ + auto *opt_layerh = cfg.option("layer_height"); + auto *opt_init_layerh = cfg.option("initial_layer_height"); + + if (!opt_layerh || !opt_init_layerh) + throw std::runtime_error("Invalid SL1 file"); + + return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()}; +} + +std::vector extract_slices_from_sla_archive( + ArchiveData & arch, + const RasterParams & rstp, + std::function progr) +{ + auto jobdir = arch.config.get("jobDir"); + for (auto &c : jobdir) c = std::tolower(c); + + std::vector slices(arch.images.size()); + + struct Status + { + double incr, val, prev; + bool stop = false; + tbb::spin_mutex mutex; + } st {100. / slices.size(), 0., 0.}; + + tbb::parallel_for(size_t(0), arch.images.size(), + [&arch, &slices, &st, &rstp, progr](size_t i) { + // Status indication guarded with the spinlock + { + std::lock_guard lck(st.mutex); + if (st.stop) return; + + st.val += st.incr; + double curr = std::round(st.val); + if (curr > st.prev) { + st.prev = curr; + st.stop = !progr(int(curr)); + } + } + + auto &buf = arch.images[i]; + wxMemoryInputStream stream{buf.data(), buf.size()}; + wxImage img{stream}; + + auto rings = marchsq::execute(img, 128, rstp.win); + ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w, rstp.px_h); + + // Invert the raster transformations indicated in + // the profile metadata + invert_raster_trafo(expolys, rstp.trafo, rstp.width, rstp.height); + + slices[i] = std::move(expolys); + }); + + if (st.stop) slices = {}; + + return slices; +} + +} // namespace + +void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out) +{ + ArchiveData arch = extract_sla_archive(zipfname, "png"); + out.load(arch.profile); +} + +void import_sla_archive( + const std::string & zipfname, + Vec2i windowsize, + TriangleMesh & out, + DynamicPrintConfig & profile, + std::function progr) +{ + // Ensure minimum window size for marching squares + windowsize.x() = std::max(2, windowsize.x()); + windowsize.y() = std::max(2, windowsize.y()); + + ArchiveData arch = extract_sla_archive(zipfname, "thumbnail"); + profile.load(arch.profile); + + RasterParams rstp = get_raster_params(profile); + rstp.win = {windowsize.y(), windowsize.x()}; + + SliceParams slicp = get_slice_params(profile); + + std::vector slices = + extract_slices_from_sla_archive(arch, rstp, progr); + + if (!slices.empty()) + out = slices_to_triangle_mesh(slices, 0, slicp.layerh, slicp.initial_layerh); +} + +} // namespace Slic3r diff --git a/src/slic3r/Utils/SLAImport.hpp b/src/slic3r/Utils/SLAImport.hpp new file mode 100644 index 0000000000..a819bd7e76 --- /dev/null +++ b/src/slic3r/Utils/SLAImport.hpp @@ -0,0 +1,36 @@ +#ifndef SLAIMPORT_HPP +#define SLAIMPORT_HPP + +#include + +#include +#include +#include + +namespace Slic3r { + +class TriangleMesh; +class DynamicPrintConfig; + +void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out); + +void import_sla_archive( + const std::string & zipfname, + Vec2i windowsize, + TriangleMesh & out, + DynamicPrintConfig & profile, + std::function progr = [](int) { return true; }); + +inline void import_sla_archive( + const std::string & zipfname, + Vec2i windowsize, + TriangleMesh & out, + std::function progr = [](int) { return true; }) +{ + DynamicPrintConfig profile; + import_sla_archive(zipfname, windowsize, out, profile, progr); +} + +} + +#endif // SLAIMPORT_HPP diff --git a/tests/fff_print/test_data.cpp b/tests/fff_print/test_data.cpp index c2c6a5f33c..70f82f4a50 100644 --- a/tests/fff_print/test_data.cpp +++ b/tests/fff_print/test_data.cpp @@ -12,6 +12,7 @@ #include #include +#include using namespace std; @@ -167,8 +168,7 @@ void init_print(std::vector &&meshes, Slic3r::Print &print, Slic3r object->add_volume(std::move(t)); object->add_instance(); } - model.arrange_objects(PrintConfig::min_object_distance(&config)); - model.center_instances_around_point(Slic3r::Vec2d(100, 100)); + arrange_objects(model, InfiniteBed{}, ArrangeParams{ scaled(min_object_distance(config))}); for (ModelObject *mo : model.objects) { mo->ensure_on_bed(); print.auto_assign_extruders(mo); diff --git a/tests/fff_print/test_model.cpp b/tests/fff_print/test_model.cpp index 3378a83638..6cb9266214 100644 --- a/tests/fff_print/test_model.cpp +++ b/tests/fff_print/test_model.cpp @@ -2,6 +2,7 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/Model.hpp" +#include "libslic3r/ModelArrange.hpp" #include #include @@ -41,8 +42,7 @@ SCENARIO("Model construction", "[Model]") { } } model_object->add_instance(); - model.arrange_objects(PrintConfig::min_object_distance(&config)); - model.center_instances_around_point(Slic3r::Vec2d(100, 100)); + arrange_objects(model, InfiniteBed{scaled(Vec2d(100, 100))}, ArrangeParams{scaled(min_object_distance(config))}); model_object->ensure_on_bed(); print.auto_assign_extruders(model_object); THEN("Print works?") { diff --git a/tests/libnest2d/libnest2d_tests_main.cpp b/tests/libnest2d/libnest2d_tests_main.cpp index c7259ae537..e0692f4e74 100644 --- a/tests/libnest2d/libnest2d_tests_main.cpp +++ b/tests/libnest2d/libnest2d_tests_main.cpp @@ -472,32 +472,30 @@ TEST_CASE("ArrangeRectanglesLoose", "[Nesting]") namespace { using namespace libnest2d; -template -void exportSVG(std::vector>& result, const Bin& bin, int idx = 0) { - std::string loc = "out"; +template +void exportSVG(const char *loc, It from, It to) { - static std::string svg_header = - R"raw( + static const char* svg_header = +R"raw( )raw"; - int i = idx; - auto r = result; // for(auto r : result) { - std::fstream out(loc + std::to_string(i) + ".svg", std::fstream::out); + std::fstream out(loc, std::fstream::out); if(out.is_open()) { out << svg_header; - Item rbin( RectangleItem(bin.width(), bin.height()) ); - for(unsigned j = 0; j < rbin.vertexCount(); j++) { - auto v = rbin.vertex(j); - setY(v, -getY(v)/SCALE + 500 ); - setX(v, getX(v)/SCALE); - rbin.setVertex(j, v); - } - out << shapelike::serialize(rbin.rawShape()) << std::endl; - for(Item& sh : r) { - Item tsh(sh.transformedShape()); +// Item rbin( RectangleItem(bin.width(), bin.height()) ); +// for(unsigned j = 0; j < rbin.vertexCount(); j++) { +// auto v = rbin.vertex(j); +// setY(v, -getY(v)/SCALE + 500 ); +// setX(v, getX(v)/SCALE); +// rbin.setVertex(j, v); +// } +// out << shapelike::serialize(rbin.rawShape()) << std::endl; + for(auto it = from; it != to; ++it) { + const Item &itm = *it; + Item tsh(itm.transformedShape()); for(unsigned j = 0; j < tsh.vertexCount(); j++) { auto v = tsh.vertex(j); setY(v, -getY(v)/SCALE + 500); @@ -509,10 +507,16 @@ void exportSVG(std::vector>& result, const Bin& bin out << "\n" << std::endl; } out.close(); - + // i++; // } } + +template +void exportSVG(std::vector>& result, int idx = 0) { + exportSVG((std::string("out") + std::to_string(idx) + ".svg").c_str(), + result.begin(), result.end()); +} } TEST_CASE("BottomLeftStressTest", "[Geometry]") { @@ -541,7 +545,7 @@ TEST_CASE("BottomLeftStressTest", "[Geometry]") { valid = (valid && !r1.isInside(r2) && !r2.isInside(r1)); if(!valid) { std::cout << "error index: " << i << std::endl; - exportSVG(result, bin, i); + exportSVG(result, i); } REQUIRE(valid); } else { @@ -894,7 +898,7 @@ void testNfp(const std::vector& testdata) { int TEST_CASEcase = 0; - auto& exportfun = exportSVG; + auto& exportfun = exportSVG; auto onetest = [&](Item& orbiter, Item& stationary, unsigned /*testidx*/){ TEST_CASEcase++; @@ -941,7 +945,7 @@ void testNfp(const std::vector& testdata) { std::ref(stationary), std::ref(tmp), std::ref(infp) }; - exportfun(inp, bin, TEST_CASEcase*i++); + exportfun(inp, TEST_CASEcase*i++); } REQUIRE(touching); @@ -1096,3 +1100,91 @@ TEST_CASE("MinAreaBBWithRotatingCalipers", "[Geometry]") { REQUIRE(succ); } } + +template MultiPolygon merged_pile(It from, It to, int bin_id) +{ + MultiPolygon pile; + pile.reserve(size_t(to - from)); + + for (auto it = from; it != to; ++it) { + if (it->binId() == bin_id) pile.emplace_back(it->transformedShape()); + } + + return nfp::merge(pile); +} + +TEST_CASE("Test for bed center distance optimization", "[Nesting], [NestKernels]") +{ + static const constexpr ClipperLib::cInt W = 10000000; + + // Get the input items and define the bin. + std::vector input(9, {W, W}); + + auto bin = Box::infinite(); + + NfpPlacer::Config pconfig; + + pconfig.object_function = [](const Item &item) -> double { + return pl::magnsq(item.boundingBox().center()); + }; + + size_t bins = nest(input, bin, 0, NestConfig{pconfig}); + + REQUIRE(bins == 1); + + // Gather the items into piles of arranged polygons... + MultiPolygon pile; + pile.reserve(input.size()); + + for (auto &itm : input) { + REQUIRE(itm.binId() == 0); + pile.emplace_back(itm.transformedShape()); + } + + MultiPolygon m = merged_pile(input.begin(), input.end(), 0); + + REQUIRE(m.size() == 1); + + REQUIRE(sl::area(m) == Approx(9. * W * W)); +} + +TEST_CASE("Test for biggest bounding box area", "[Nesting], [NestKernels]") +{ + static const constexpr ClipperLib::cInt W = 10000000; + static const constexpr size_t N = 100; + + // Get the input items and define the bin. + std::vector input(N, {W, W}); + + auto bin = Box::infinite(); + + NfpPlacer::Config pconfig; + pconfig.rotations = {0.}; + Box pile_box; + pconfig.before_packing = + [&pile_box](const MultiPolygon &pile, + const _ItemGroup &/*packed_items*/, + const _ItemGroup &/*remaining_items*/) { + pile_box = sl::boundingBox(pile); + }; + + pconfig.object_function = [&pile_box](const Item &item) -> double { + Box b = sl::boundingBox(item.boundingBox(), pile_box); + double area = b.area() / (W * W); + return -area; + }; + + size_t bins = nest(input, bin, 0, NestConfig{pconfig}); + + // To debug: + exportSVG<1000000>("out", input.begin(), input.end()); + + REQUIRE(bins == 1); + + MultiPolygon pile = merged_pile(input.begin(), input.end(), 0); + Box bb = sl::boundingBox(pile); + + // Here the result shall be a stairway of boxes + REQUIRE(pile.size() == N); + REQUIRE(bb.area() == N * N * W * W); +} diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 2353414f96..b41dbf8baf 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable(${_TEST_NAME}_tests test_stl.cpp test_meshsimplify.cpp test_meshboolean.cpp + test_marchingsquares.cpp test_timeutils.cpp ) diff --git a/tests/libslic3r/test_marchingsquares.cpp b/tests/libslic3r/test_marchingsquares.cpp new file mode 100644 index 0000000000..9912ff2ca7 --- /dev/null +++ b/tests/libslic3r/test_marchingsquares.cpp @@ -0,0 +1,371 @@ +#define NOMINMAX + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace Slic3r; + +static double area(const sla::RasterBase::PixelDim &pxd) +{ + return pxd.w_mm * pxd.h_mm; +} + +static Slic3r::sla::RasterGrayscaleAA create_raster( + const sla::RasterBase::Resolution &res, + double disp_w = 100., + double disp_h = 100.) +{ + sla::RasterBase::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px}; + + auto bb = BoundingBox({0, 0}, {scaled(disp_w), scaled(disp_h)}); + sla::RasterBase::Trafo trafo; + trafo.center_x = bb.center().x(); + trafo.center_y = bb.center().y(); + + return sla::RasterGrayscaleAA{res, pixdim, trafo, agg::gamma_threshold(.5)}; +} + +static ExPolygon square(double a, Point center = {0, 0}) +{ + ExPolygon poly; + coord_t V = scaled(a / 2.); + + poly.contour.points = {{-V, -V}, {V, -V}, {V, V}, {-V, V}}; + poly.translate(center.x(), center.y()); + + return poly; +} + +static ExPolygon square_with_hole(double a, Point center = {0, 0}) +{ + ExPolygon poly = square(a); + + poly.holes.emplace_back(); + coord_t V = scaled(a / 4.); + poly.holes.front().points = {{-V, V}, {V, V}, {V, -V}, {-V, -V}}; + + poly.translate(center.x(), center.y()); + + return poly; +} + +static ExPolygons circle_with_hole(double r, Point center = {0, 0}) { + + ExPolygon poly; + + std::vector pis = linspace_vector(0., 2 * PI, 100); + + coord_t rs = scaled(r); + for (double phi : pis) { + poly.contour.points.emplace_back(rs * std::cos(phi), rs * std::sin(phi)); + } + + poly.holes.emplace_back(poly.contour); + poly.holes.front().reverse(); + for (auto &p : poly.holes.front().points) p /= 2; + + poly.translate(center.x(), center.y()); + + return {poly}; +} + +static const Vec2i W4x4 = {4, 4}; +static const Vec2i W2x2 = {2, 2}; + +template +static void test_expolys(Rst && rst, + const ExPolygons & ref, + Vec2i window, + const std::string &name = "test") +{ + for (const ExPolygon &expoly : ref) rst.draw(expoly); + + std::fstream out(name + ".png", std::ios::out); + out << rst.encode(sla::PNGRasterEncoder{}); + out.close(); + + ExPolygons extracted = sla::raster_to_polygons(rst, window); + + SVG svg(name + ".svg"); + svg.draw(extracted); + svg.draw(ref, "green"); + svg.Close(); + + double max_rel_err = 0.1; + sla::RasterBase::PixelDim pxd = rst.pixel_dimensions(); + double max_abs_err = area(pxd) * scaled(1.) * scaled(1.); + + BoundingBox ref_bb; + for (auto &expoly : ref) ref_bb.merge(expoly.contour.bounding_box()); + + double max_displacement = 4. * (std::pow(pxd.h_mm, 2) + std::pow(pxd.w_mm, 2)); + max_displacement *= scaled(1.) * scaled(1.); + + REQUIRE(extracted.size() == ref.size()); + for (size_t i = 0; i < ref.size(); ++i) { + REQUIRE(extracted[i].contour.is_counter_clockwise()); + REQUIRE(extracted[i].holes.size() == ref[i].holes.size()); + + for (auto &h : extracted[i].holes) REQUIRE(h.is_clockwise()); + + double refa = ref[i].area(); + double abs_err = std::abs(extracted[i].area() - refa); + double rel_err = abs_err / refa; + + REQUIRE((rel_err <= max_rel_err || abs_err <= max_abs_err)); + + BoundingBox bb; + for (auto &expoly : extracted) bb.merge(expoly.contour.bounding_box()); + + Point d = bb.center() - ref_bb.center(); + REQUIRE(double(d.transpose() * d) <= max_displacement); + } +} + +TEST_CASE("Empty raster should result in empty polygons", "[MarchingSquares]") { + sla::RasterGrayscaleAAGammaPower rst{{}, {}, {}}; + ExPolygons extracted = sla::raster_to_polygons(rst); + REQUIRE(extracted.size() == 0); +} + +TEST_CASE("Marching squares directions", "[MarchingSquares]") { + marchsq::Coord crd{1, 1}; + + REQUIRE(step(crd, marchsq::__impl::Dir::left).r == 1); + REQUIRE(step(crd, marchsq::__impl::Dir::left).c == 0); + + REQUIRE(step(crd, marchsq::__impl::Dir::down).r == 2); + REQUIRE(step(crd, marchsq::__impl::Dir::down).c == 1); + + REQUIRE(step(crd, marchsq::__impl::Dir::right).r == 1); + REQUIRE(step(crd, marchsq::__impl::Dir::right).c == 2); + + REQUIRE(step(crd, marchsq::__impl::Dir::up).r == 0); + REQUIRE(step(crd, marchsq::__impl::Dir::up).c == 1); +} + +TEST_CASE("Fully covered raster should result in a rectangle", "[MarchingSquares]") { + auto rst = create_raster({4, 4}, 4., 4.); + + ExPolygon rect = square(4); + + SECTION("Full accuracy") { + test_expolys(rst, {rect}, W2x2, "fully_covered_full_acc"); + } + + SECTION("Half accuracy") { + test_expolys(rst, {rect}, W4x4, "fully_covered_half_acc"); + } +} + +TEST_CASE("4x4 raster with one ring", "[MarchingSquares]") { + + sla::RasterBase::PixelDim pixdim{1, 1}; + + // We need one additional row and column to detect edges + sla::RasterGrayscaleAA rst{{4, 4}, pixdim, {}, agg::gamma_threshold(.5)}; + + // Draw a triangle from individual pixels + rst.draw(square(1., {0500000, 0500000})); + rst.draw(square(1., {1500000, 0500000})); + rst.draw(square(1., {2500000, 0500000})); + + rst.draw(square(1., {1500000, 1500000})); + rst.draw(square(1., {2500000, 1500000})); + + rst.draw(square(1., {2500000, 2500000})); + + std::fstream out("4x4.png", std::ios::out); + out << rst.encode(sla::PNGRasterEncoder{}); + out.close(); + + ExPolygons extracted = sla::raster_to_polygons(rst); + + SVG svg("4x4.svg"); + svg.draw(extracted); + svg.Close(); + + REQUIRE(extracted.size() == 1); +} + +TEST_CASE("4x4 raster with two rings", "[MarchingSquares]") { + + sla::RasterBase::PixelDim pixdim{1, 1}; + + // We need one additional row and column to detect edges + sla::RasterGrayscaleAA rst{{5, 5}, pixdim, {}, agg::gamma_threshold(.5)}; + + SECTION("Ambiguous case with 'ac' square") { + + // Draw a triangle from individual pixels + rst.draw(square(1., {3500000, 2500000})); + rst.draw(square(1., {3500000, 3500000})); + rst.draw(square(1., {2500000, 3500000})); + + rst.draw(square(1., {2500000, 1500000})); + rst.draw(square(1., {1500000, 1500000})); + rst.draw(square(1., {1500000, 2500000})); + + std::fstream out("4x4_ac.png", std::ios::out); + out << rst.encode(sla::PNGRasterEncoder{}); + out.close(); + + ExPolygons extracted = sla::raster_to_polygons(rst); + + SVG svg("4x4_ac.svg"); + svg.draw(extracted); + svg.Close(); + + REQUIRE(extracted.size() == 2); + } + + SECTION("Ambiguous case with 'bd' square") { + + // Draw a triangle from individual pixels + rst.draw(square(1., {3500000, 1500000})); + rst.draw(square(1., {3500000, 2500000})); + rst.draw(square(1., {2500000, 1500000})); + + rst.draw(square(1., {1500000, 2500000})); + rst.draw(square(1., {1500000, 3500000})); + rst.draw(square(1., {2500000, 3500000})); + + std::fstream out("4x4_bd.png", std::ios::out); + out << rst.encode(sla::PNGRasterEncoder{}); + out.close(); + + ExPolygons extracted = sla::raster_to_polygons(rst); + + SVG svg("4x4_bd.svg"); + svg.draw(extracted); + svg.Close(); + + REQUIRE(extracted.size() == 2); + } +} + +TEST_CASE("Square with hole in the middle", "[MarchingSquares]") { + using namespace Slic3r; + + ExPolygons inp = {square_with_hole(50.)}; + + SECTION("Proportional raster, 1x1 mm pixel size, full accuracy") { + test_expolys(create_raster({100, 100}, 100., 100.), inp, W2x2, "square_with_hole_proportional_1x1_mm_px_full"); + } + + SECTION("Proportional raster, 1x1 mm pixel size, half accuracy") { + test_expolys(create_raster({100, 100}, 100., 100.), inp, W4x4, "square_with_hole_proportional_1x1_mm_px_half"); + } + + SECTION("Landscape raster, 1x1 mm pixel size, full accuracy") { + test_expolys(create_raster({150, 100}, 150., 100.), inp, W2x2, "square_with_hole_landsc_1x1_mm_px_full"); + } + + SECTION("Landscape raster, 1x1 mm pixel size, half accuracy") { + test_expolys(create_raster({150, 100}, 150., 100.), inp, W4x4, "square_with_hole_landsc_1x1_mm_px_half"); + } + + SECTION("Portrait raster, 1x1 mm pixel size, full accuracy") { + test_expolys(create_raster({100, 150}, 100., 150.), inp, W2x2, "square_with_hole_portrait_1x1_mm_px_full"); + } + + SECTION("Portrait raster, 1x1 mm pixel size, half accuracy") { + test_expolys(create_raster({100, 150}, 100., 150.), inp, W4x4, "square_with_hole_portrait_1x1_mm_px_half"); + } + + SECTION("Proportional raster, 2x2 mm pixel size, full accuracy") { + test_expolys(create_raster({200, 200}, 100., 100.), inp, W2x2, "square_with_hole_proportional_2x2_mm_px_full"); + } + + SECTION("Proportional raster, 2x2 mm pixel size, half accuracy") { + test_expolys(create_raster({200, 200}, 100., 100.), inp, W4x4, "square_with_hole_proportional_2x2_mm_px_half"); + } + + SECTION("Proportional raster, 0.5x0.5 mm pixel size, full accuracy") { + test_expolys(create_raster({50, 50}, 100., 100.), inp, W2x2, "square_with_hole_proportional_0.5x0.5_mm_px_full"); + } + + SECTION("Proportional raster, 0.5x0.5 mm pixel size, half accuracy") { + test_expolys(create_raster({50, 50}, 100., 100.), inp, W4x4, "square_with_hole_proportional_0.5x0.5_mm_px_half"); + } +} + +TEST_CASE("Circle with hole in the middle", "[MarchingSquares]") { + using namespace Slic3r; + + test_expolys(create_raster({1000, 1000}), circle_with_hole(25.), W2x2, "circle_with_hole"); +} + +static void recreate_object_from_rasters(const std::string &objname, float lh) { + TriangleMesh mesh = load_model(objname); + + auto bb = mesh.bounding_box(); + Vec3f tr = -bb.center().cast(); + mesh.translate(tr.x(), tr.y(), tr.z()); + bb = mesh.bounding_box(); + + std::vector layers; + slice_mesh(mesh, grid(float(bb.min.z()) + lh, float(bb.max.z()), lh), layers, 0.f, []{}); + + sla::RasterBase::Resolution res{2560, 1440}; + double disp_w = 120.96; + double disp_h = 68.04; + + size_t cntr = 0; + for (ExPolygons &layer : layers) { + auto rst = create_raster(res, disp_w, disp_h); + + for (ExPolygon &island : layer) { + rst.draw(island); + } + + std::fstream out(objname + std::to_string(cntr) + ".png", std::ios::out); + out << rst.encode(sla::PNGRasterEncoder{}); + out.close(); + + ExPolygons layer_ = sla::raster_to_polygons(rst); +// float delta = scaled(std::min(rst.pixel_dimensions().h_mm, +// rst.pixel_dimensions().w_mm)) / 2; + +// layer_ = expolygons_simplify(layer_, delta); + + SVG svg(objname + std::to_string(cntr) + ".svg", BoundingBox(Point{0, 0}, Point{scaled(disp_w), scaled(disp_h)})); + svg.draw(layer_); + svg.draw(layer, "green"); + svg.Close(); + + double layera = 0., layera_ = 0.; + for (auto &p : layer) layera += p.area(); + for (auto &p : layer_) layera_ += p.area(); + + std::cout << cntr++ << std::endl; + double diff = std::abs(layera_ - layera); + REQUIRE((diff <= 0.1 * layera || diff < scaled(1.) * scaled(1.))); + + layer = std::move(layer_); + } + + TriangleMesh out = slices_to_triangle_mesh(layers, bb.min.z(), double(lh), double(lh)); + + out.require_shared_vertices(); + out.WriteOBJFile("out_from_rasters.obj"); +} + +TEST_CASE("Recreate object from rasters", "[SL1Import]") { + recreate_object_from_rasters("frog_legs.obj", 0.05f); +} diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index 10f5742d34..82df2c1a6f 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -154,19 +154,12 @@ TEST_CASE("FloorSupportsDoNotPierceModel", "[SLASupportGeneration]") { test_support_model_collision(fname, supportcfg); } -TEST_CASE("DefaultRasterShouldBeEmpty", "[SLARasterOutput]") { - sla::Raster raster; - REQUIRE(raster.empty()); -} - TEST_CASE("InitializedRasterShouldBeNONEmpty", "[SLARasterOutput]") { // Default Prusa SL1 display parameters - sla::Raster::Resolution res{2560, 1440}; - sla::Raster::PixelDim pixdim{120. / res.width_px, 68. / res.height_px}; + sla::RasterBase::Resolution res{2560, 1440}; + sla::RasterBase::PixelDim pixdim{120. / res.width_px, 68. / res.height_px}; - sla::Raster raster; - raster.reset(res, pixdim); - REQUIRE_FALSE(raster.empty()); + sla::RasterGrayscaleAAGammaPower raster(res, pixdim, {}, 1.); REQUIRE(raster.resolution().width_px == res.width_px); REQUIRE(raster.resolution().height_px == res.height_px); REQUIRE(raster.pixel_dimensions().w_mm == Approx(pixdim.w_mm)); @@ -174,13 +167,14 @@ TEST_CASE("InitializedRasterShouldBeNONEmpty", "[SLARasterOutput]") { } TEST_CASE("MirroringShouldBeCorrect", "[SLARasterOutput]") { - sla::Raster::TMirroring mirrorings[] = {sla::Raster::NoMirror, - sla::Raster::MirrorX, - sla::Raster::MirrorY, - sla::Raster::MirrorXY}; + sla::RasterBase::TMirroring mirrorings[] = {sla::RasterBase::NoMirror, + sla::RasterBase::MirrorX, + sla::RasterBase::MirrorY, + sla::RasterBase::MirrorXY}; + + sla::RasterBase::Orientation orientations[] = + {sla::RasterBase::roLandscape, sla::RasterBase::roPortrait}; - sla::Raster::Orientation orientations[] = {sla::Raster::roLandscape, - sla::Raster::roPortrait}; for (auto orientation : orientations) for (auto &mirror : mirrorings) check_raster_transformations(orientation, mirror); @@ -189,10 +183,11 @@ TEST_CASE("MirroringShouldBeCorrect", "[SLARasterOutput]") { TEST_CASE("RasterizedPolygonAreaShouldMatch", "[SLARasterOutput]") { double disp_w = 120., disp_h = 68.; - sla::Raster::Resolution res{2560, 1440}; - sla::Raster::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px}; + sla::RasterBase::Resolution res{2560, 1440}; + sla::RasterBase::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px}; - sla::Raster raster{res, pixdim}; + double gamma = 1.; + sla::RasterGrayscaleAAGammaPower raster(res, pixdim, {}, gamma); auto bb = BoundingBox({0, 0}, {scaled(disp_w), scaled(disp_h)}); ExPolygon poly = square_with_hole(10.); @@ -215,6 +210,13 @@ TEST_CASE("RasterizedPolygonAreaShouldMatch", "[SLARasterOutput]") { diff = std::abs(a - ra); REQUIRE(diff <= predict_error(poly, pixdim)); + + sla::RasterGrayscaleAA raster0(res, pixdim, {}, [](double) { return 0.; }); + REQUIRE(raster_pxsum(raster0) == 0); + + raster0.draw(poly); + ra = raster_white_area(raster); + REQUIRE(raster_pxsum(raster0) == 0); } TEST_CASE("Triangle mesh conversions should be correct", "[SLAConversions]") diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index a844b2eaed..883e4268a8 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -1,4 +1,5 @@ #include "sla_test_utils.hpp" +#include "libslic3r/SLA/AGGRaster.hpp" void test_support_model_collision(const std::string &obj_filename, const sla::SupportConfig &input_supportcfg, @@ -293,18 +294,19 @@ void check_validity(const TriangleMesh &input_mesh, int flags) } } -void check_raster_transformations(sla::Raster::Orientation o, sla::Raster::TMirroring mirroring) +void check_raster_transformations(sla::RasterBase::Orientation o, sla::RasterBase::TMirroring mirroring) { double disp_w = 120., disp_h = 68.; - sla::Raster::Resolution res{2560, 1440}; - sla::Raster::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px}; + sla::RasterBase::Resolution res{2560, 1440}; + sla::RasterBase::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px}; auto bb = BoundingBox({0, 0}, {scaled(disp_w), scaled(disp_h)}); - sla::Raster::Trafo trafo{o, mirroring}; - trafo.origin_x = bb.center().x(); - trafo.origin_y = bb.center().y(); + sla::RasterBase::Trafo trafo{o, mirroring}; + trafo.center_x = bb.center().x(); + trafo.center_y = bb.center().y(); + double gamma = 1.; - sla::Raster raster{res, pixdim, trafo}; + sla::RasterGrayscaleAAGammaPower raster{res, pixdim, trafo, gamma}; // create box of size 32x32 pixels (not 1x1 to avoid antialiasing errors) coord_t pw = 32 * coord_t(std::ceil(scaled(pixdim.w_mm))); @@ -319,7 +321,7 @@ void check_raster_transformations(sla::Raster::Orientation o, sla::Raster::TMirr // Now calculate the position of the translated box according to output // trafo. - if (o == sla::Raster::Orientation::roPortrait) expected_box.rotate(PI / 2.); + if (o == sla::RasterBase::Orientation::roPortrait) expected_box.rotate(PI / 2.); if (mirroring[X]) for (auto &p : expected_box.contour.points) p.x() = -p.x(); @@ -340,10 +342,9 @@ void check_raster_transformations(sla::Raster::Orientation o, sla::Raster::TMirr auto px = raster.read_pixel(w, h); if (px != FullWhite) { - sla::PNGImage img; std::fstream outf("out.png", std::ios::out); - outf << img.serialize(raster); + outf << raster.encode(sla::PNGRasterEncoder()); } REQUIRE(px == FullWhite); @@ -361,9 +362,21 @@ ExPolygon square_with_hole(double v) return poly; } -double raster_white_area(const sla::Raster &raster) +long raster_pxsum(const sla::RasterGrayscaleAA &raster) { - if (raster.empty()) return std::nan(""); + auto res = raster.resolution(); + long a = 0; + + for (size_t x = 0; x < res.width_px; ++x) + for (size_t y = 0; y < res.height_px; ++y) + a += raster.read_pixel(x, y); + + return a; +} + +double raster_white_area(const sla::RasterGrayscaleAA &raster) +{ + if (raster.resolution().pixels() == 0) return std::nan(""); auto res = raster.resolution(); double a = 0; @@ -377,7 +390,7 @@ double raster_white_area(const sla::Raster &raster) return a; } -double predict_error(const ExPolygon &p, const sla::Raster::PixelDim &pd) +double predict_error(const ExPolygon &p, const sla::RasterBase::PixelDim &pd) { auto lines = p.lines(); double pix_err = pixel_area(FullWhite, pd) / 2.; diff --git a/tests/sla_print/sla_test_utils.hpp b/tests/sla_print/sla_test_utils.hpp index f3727bd394..3652b1f81c 100644 --- a/tests/sla_print/sla_test_utils.hpp +++ b/tests/sla_print/sla_test_utils.hpp @@ -16,7 +16,7 @@ #include "libslic3r/SLA/SupportTreeBuilder.hpp" #include "libslic3r/SLA/SupportTreeBuildsteps.hpp" #include "libslic3r/SLA/SupportPointGenerator.hpp" -#include "libslic3r/SLA/Raster.hpp" +#include "libslic3r/SLA/AGGRaster.hpp" #include "libslic3r/SLA/ConcaveHull.hpp" #include "libslic3r/MTUtils.hpp" @@ -170,18 +170,19 @@ static constexpr const TPixel FullBlack = 0; template constexpr int arraysize(const A (&)[N]) { return N; } -void check_raster_transformations(sla::Raster::Orientation o, - sla::Raster::TMirroring mirroring); +void check_raster_transformations(sla::RasterBase::Orientation o, + sla::RasterBase::TMirroring mirroring); ExPolygon square_with_hole(double v); -inline double pixel_area(TPixel px, const sla::Raster::PixelDim &pxdim) +inline double pixel_area(TPixel px, const sla::RasterBase::PixelDim &pxdim) { return (pxdim.h_mm * pxdim.w_mm) * px * 1. / (FullWhite - FullBlack); } -double raster_white_area(const sla::Raster &raster); +double raster_white_area(const sla::RasterGrayscaleAA &raster); +long raster_pxsum(const sla::RasterGrayscaleAA &raster); -double predict_error(const ExPolygon &p, const sla::Raster::PixelDim &pd); +double predict_error(const ExPolygon &p, const sla::RasterBase::PixelDim &pd); #endif // SLA_TEST_UTILS_HPP diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 435dda5a28..63dc5b312a 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -50,7 +50,7 @@ void erase(t_config_option_key opt_key); void normalize(); %name{setenv} void setenv_(); - double min_object_distance() %code{% PrintConfig cfg; cfg.apply(*THIS, true); RETVAL = cfg.min_object_distance(); %}; + double min_object_distance() %code{% RETVAL = Slic3r::min_object_distance(*THIS); %}; static DynamicPrintConfig* load(char *path) %code%{ auto config = new DynamicPrintConfig(); @@ -114,7 +114,7 @@ } %}; %name{setenv} void setenv_(); - double min_object_distance() %code{% RETVAL = PrintConfig::min_object_distance(THIS); %}; + double min_object_distance() %code{% RETVAL = Slic3r::min_object_distance(*THIS); %}; static StaticPrintConfig* load(char *path) %code%{ auto config = new FullPrintConfig(); diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 35b1c01ce8..4fb35578db 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -3,6 +3,7 @@ %{ #include #include "libslic3r/Model.hpp" +#include "libslic3r/ModelArrange.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/Slicing.hpp" @@ -80,9 +81,9 @@ ModelObjectPtrs* objects() %code%{ RETVAL = &THIS->objects; %}; - bool arrange_objects(double dist, BoundingBoxf* bb = NULL); - void duplicate(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL); - void duplicate_objects(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL); + bool arrange_objects(double dist, BoundingBoxf* bb = NULL) %code%{ ArrangeParams ap{scaled(dist)}; if (bb) arrange_objects(*THIS, scaled(*bb), ap); else arrange_objects(*THIS, InfiniteBed{}, ap); %}; + void duplicate(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL) %code%{ ArrangeParams ap{scaled(dist)}; if (bb) duplicate(*THIS, copies_num, scaled(*bb), ap); else duplicate(*THIS, copies_num, InfiniteBed{}, ap); %}; + void duplicate_objects(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL) %code%{ ArrangeParams ap{scaled(dist)}; if (bb) duplicate_objects(*THIS, copies_num, scaled(*bb), ap); else duplicate_objects(*THIS, copies_num, InfiniteBed{}, ap); %}; void duplicate_objects_grid(unsigned int x, unsigned int y, double dist); bool looks_like_multipart_object() const;