diff --git a/CMakeLists.txt b/CMakeLists.txt index 625ebb334..781dbd01a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,24 @@ if (MSVC) add_compile_options(-bigobj -Zm316) endif () -message(STATUS "CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}") + +# Display and check CMAKE_PREFIX_PATH +message(STATUS "SLIC3R_STATIC: ${SLIC3R_STATIC}") +if (NOT "${CMAKE_PREFIX_PATH}" STREQUAL "") + message(STATUS "CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH} (from cache or command line)") + set(PREFIX_PATH_CHECK ${CMAKE_PREFIX_PATH}) +elseif (NOT "$ENV{CMAKE_PREFIX_PATH}" STREQUAL "") + message(STATUS "CMAKE_PREFIX_PATH: $ENV{CMAKE_PREFIX_PATH} (from environment)") + set(PREFIX_PATH_CHECK $ENV{CMAKE_PREFIX_PATH}) +else () + message(STATUS "CMAKE_PREFIX_PATH: (default)") +endif () + +foreach (DIR ${PREFIX_PATH_CHECK}) + if (NOT EXISTS "${DIR}") + message(WARNING "CMAKE_PREFIX_PATH element doesn't exist: ${DIR}") + endif () +endforeach () # Add our own cmake module path. list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/) diff --git a/resources/icons/Slic3r_32px.png b/resources/icons/Slic3r_32px.png new file mode 100644 index 000000000..6bf5a9cd1 Binary files /dev/null and b/resources/icons/Slic3r_32px.png differ diff --git a/resources/icons/overlay/cut_hover.png b/resources/icons/overlay/cut_hover.png index eccce0243..b026667c2 100644 Binary files a/resources/icons/overlay/cut_hover.png and b/resources/icons/overlay/cut_hover.png differ diff --git a/resources/icons/overlay/cut_off.png b/resources/icons/overlay/cut_off.png index 4d0f8c93f..c01f20554 100644 Binary files a/resources/icons/overlay/cut_off.png and b/resources/icons/overlay/cut_off.png differ diff --git a/resources/icons/overlay/cut_on.png b/resources/icons/overlay/cut_on.png index 967d15458..fac6ecb45 100644 Binary files a/resources/icons/overlay/cut_on.png and b/resources/icons/overlay/cut_on.png differ diff --git a/resources/icons/overlay/layflat_hover.png b/resources/icons/overlay/layflat_hover.png index 31ee91f46..508916e48 100644 Binary files a/resources/icons/overlay/layflat_hover.png and b/resources/icons/overlay/layflat_hover.png differ diff --git a/resources/icons/overlay/layflat_off.png b/resources/icons/overlay/layflat_off.png index 4feadbaf4..6bfadd316 100644 Binary files a/resources/icons/overlay/layflat_off.png and b/resources/icons/overlay/layflat_off.png differ diff --git a/resources/icons/overlay/layflat_on.png b/resources/icons/overlay/layflat_on.png index 93a186957..4892b57fc 100644 Binary files a/resources/icons/overlay/layflat_on.png and b/resources/icons/overlay/layflat_on.png differ diff --git a/resources/icons/overlay/move_hover.png b/resources/icons/overlay/move_hover.png index 8cd66cab9..933c34c24 100644 Binary files a/resources/icons/overlay/move_hover.png and b/resources/icons/overlay/move_hover.png differ diff --git a/resources/icons/overlay/move_off.png b/resources/icons/overlay/move_off.png index 9e44690df..6c921a042 100644 Binary files a/resources/icons/overlay/move_off.png and b/resources/icons/overlay/move_off.png differ diff --git a/resources/icons/overlay/move_on.png b/resources/icons/overlay/move_on.png index c19be461d..80204b52e 100644 Binary files a/resources/icons/overlay/move_on.png and b/resources/icons/overlay/move_on.png differ diff --git a/resources/icons/overlay/rotate_hover.png b/resources/icons/overlay/rotate_hover.png index 818abcbdc..9df377fc7 100644 Binary files a/resources/icons/overlay/rotate_hover.png and b/resources/icons/overlay/rotate_hover.png differ diff --git a/resources/icons/overlay/rotate_off.png b/resources/icons/overlay/rotate_off.png index d8037d773..f5b652979 100644 Binary files a/resources/icons/overlay/rotate_off.png and b/resources/icons/overlay/rotate_off.png differ diff --git a/resources/icons/overlay/rotate_on.png b/resources/icons/overlay/rotate_on.png index f40f28d4b..6d4c5bf31 100644 Binary files a/resources/icons/overlay/rotate_on.png and b/resources/icons/overlay/rotate_on.png differ diff --git a/resources/icons/overlay/scale_hover.png b/resources/icons/overlay/scale_hover.png index 394cb4b20..40a05fffd 100644 Binary files a/resources/icons/overlay/scale_hover.png and b/resources/icons/overlay/scale_hover.png differ diff --git a/resources/icons/overlay/scale_off.png b/resources/icons/overlay/scale_off.png index fac035099..351d5a004 100644 Binary files a/resources/icons/overlay/scale_off.png and b/resources/icons/overlay/scale_off.png differ diff --git a/resources/icons/overlay/scale_on.png b/resources/icons/overlay/scale_on.png index 4d4c075f3..f8450e105 100644 Binary files a/resources/icons/overlay/scale_on.png and b/resources/icons/overlay/scale_on.png differ diff --git a/resources/icons/overlay/sla_support_points_hover.png b/resources/icons/overlay/sla_support_points_hover.png index 6303232b2..2b385c0e7 100644 Binary files a/resources/icons/overlay/sla_support_points_hover.png and b/resources/icons/overlay/sla_support_points_hover.png differ diff --git a/resources/icons/overlay/sla_support_points_off.png b/resources/icons/overlay/sla_support_points_off.png index b654366d5..0a1e3f570 100644 Binary files a/resources/icons/overlay/sla_support_points_off.png and b/resources/icons/overlay/sla_support_points_off.png differ diff --git a/resources/icons/overlay/sla_support_points_on.png b/resources/icons/overlay/sla_support_points_on.png index 8c1e69565..e7f5d0a87 100644 Binary files a/resources/icons/overlay/sla_support_points_on.png and b/resources/icons/overlay/sla_support_points_on.png differ diff --git a/resources/icons/toolbar.png b/resources/icons/toolbar.png index 75faea658..581db339e 100644 Binary files a/resources/icons/toolbar.png and b/resources/icons/toolbar.png differ diff --git a/resources/icons/toolbar141.png b/resources/icons/toolbar141.png new file mode 100644 index 000000000..888b7b1f9 Binary files /dev/null and b/resources/icons/toolbar141.png differ diff --git a/resources/icons/toolbar_background.png b/resources/icons/toolbar_background.png index 2b5ea013b..e7ce3c146 100644 Binary files a/resources/icons/toolbar_background.png and b/resources/icons/toolbar_background.png differ diff --git a/resources/icons/view_toolbar.png b/resources/icons/view_toolbar.png index dd1f5aca4..8cfb8d091 100644 Binary files a/resources/icons/view_toolbar.png and b/resources/icons/view_toolbar.png differ diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index e440b35df..49d8ac1a5 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,6 +1,21 @@ min_slic3r_version = 1.42.0-alpha +0.4.0-alpha3 Update of SLA profiles 0.4.0-alpha2 First SLA profiles +min_slic3r_version = 1.41.1 +0.3.3 Prusament PETG released +0.3.2 New MK2.5 and MK3 FW versions +0.3.1 New MK2.5 and MK3 FW versions +0.3.0 New MK2.5 and MK3 FW version min_slic3r_version = 1.41.0-alpha +0.2.9 New MK2.5 and MK3 FW versions +0.2.8 New MK2.5 and MK3 FW version +min_slic3r_version = 1.41.1 +0.2.7 New MK2.5 and MK3 FW version +0.2.6 Added MMU2 MK2.5 settings +min_slic3r_version = 1.41.0-alpha +0.2.5 Prusament is out - added prusament settings +0.2.4 Added soluble support profiles for MMU2 +0.2.3 Added materials for MMU2 single mode, edited MK3 xy stealth feedrate limit 0.2.2 Edited MMU2 Single mode purge line 0.2.1 Added PET and BVOH settings for MMU2 0.2.0-beta5 Fixed MMU1 ramming parameters diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 4dae3e3fb..60f0a0499 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the Slic3r configuration to be downgraded. -config_version = 0.4.0-alpha2 +config_version = 0.4.0-alpha3 # Where to get the updates from? config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/PrusaResearch/ @@ -18,30 +18,37 @@ config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/ma [printer_model:MK3] name = Original Prusa i3 MK3 variants = 0.4; 0.25; 0.6 +technology = FFF [printer_model:MK2.5] name = Original Prusa i3 MK2.5 variants = 0.4; 0.25; 0.6 +technology = FFF [printer_model:MK2S] name = Original Prusa i3 MK2/S variants = 0.4; 0.25; 0.6 +technology = FFF [printer_model:MK3MMU2] name = Original Prusa i3 MK3 MMU 2.0 variants = 0.4 +technology = FFF [printer_model:MK2SMM] name = Original Prusa i3 MK2/S MMU 1.0 variants = 0.4; 0.6 +technology = FFF [printer_model:MK2.5MMU2] name = Original Prusa i3 MK2.5 MMU 2.0 variants = 0.4 +technology = FFF [printer_model:SL1] name = Original Prusa SL1 -variants = default; dummy +variants = default +technology = SLA # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. @@ -1140,19 +1147,132 @@ min_fan_speed = 100 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" temperature = 220 -[sla_material:*common*] +[sla_print:*common*] +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_SL1.*/ layer_height = 0.05 -initial_layer_height = 0.3 +output_filename_format = [input_filename_base].dwz +pad_edge_radius = 0.5 +pad_enable = 1 +pad_max_merge_distance = 50 +pad_wall_height = 3 +pad_wall_thickness = 1 +support_base_diameter = 3 +support_base_height = 0.5 +support_critical_angle = 45 +support_density_at_45 = 250 +support_density_at_horizontal = 500 +support_head_front_diameter = 0.4 +support_head_penetration = 0.4 +support_head_width = 3 +support_max_bridge_length = 10 +support_minimal_z = 0 +support_object_elevation = 5 +support_pillar_diameter = 1 +support_pillar_widening_factor = 0 +supports_enable = 1 + +[sla_print:0.025 UltraDetail] +inherits = *common* +layer_height = 0.025 +support_head_width = 2 + +[sla_print:0.035 Detail] +inherits = *common* +layer_height = 0.035 + +[sla_print:0.05 Normal] +inherits = *common* +layer_height = 0.05 + +[sla_print:0.1 Fast] +inherits = *common* +layer_height = 0.1 + +########### Materials 0.025 + +[sla_material:*common 0.05*] +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_SL1.*/ +compatible_prints_condition = layer_height == 0.05 +exposure_time = 12 +initial_exposure_time = 45 +initial_layer_height = 0.5 +material_correction_curing = 1,1,1 +material_correction_printing = 1,1,1 +material_notes = + +[sla_material:*common 0.025*] +inherits = *common 0.05* +compatible_prints_condition = layer_height == 0.025 exposure_time = 10 -initial_exposure_time = 15 -material_correction_printing = 1, 1, 1 -material_correction_curing = 1, 1, 1 +initial_exposure_time = 35 -[sla_material:Material 1] -inherits = *common* +[sla_material:*common 0.035*] +inherits = *common 0.05* +compatible_prints_condition = layer_height == 0.035 +exposure_time = 13 +initial_exposure_time = 40 -[sla_material:Material 2] -inherits = *common* +[sla_material:*common 0.1*] +inherits = *common 0.05* +compatible_prints_condition = layer_height == 0.1 +exposure_time = 20 +initial_exposure_time = 90 + +########### Materials 0.025 + +[sla_material:Jamg He Transparent Clear 0.025] +inherits = *common 0.025* + +[sla_material:Jamg He Transparent Green 0.025] +inherits = *common 0.025* + +[sla_material:Jamg He Transparent Orange 0.025] +inherits = *common 0.025* + +[sla_material:Jamg He Transparent Red 0.025] +inherits = *common 0.025* + +########### Materials 0.05 + +[sla_material:Jamg He Transparent Clear 0.05] +inherits = *common 0.05* + +[sla_material:Jamg He Transparent Green 0.05] +inherits = *common 0.05* + +[sla_material:Jamg He Transparent Orange 0.05] +inherits = *common 0.05* + +[sla_material:Jamg He Transparent Red 0.05] +inherits = *common 0.05* + +########### Materials 0.035 + +[sla_material:Jamg He Transparent Clear 0.035] +inherits = *common 0.035* + +[sla_material:Jamg He Transparent Green 0.035] +inherits = *common 0.035* + +[sla_material:Jamg He Transparent Orange 0.035] +inherits = *common 0.035* + +[sla_material:Jamg He Transparent Red 0.035] +inherits = *common 0.035* + +########### Materials 0.1 + +[sla_material:Jamg He Transparent Clear 0.1] +inherits = *common 0.1* + +[sla_material:Jamg He Transparent Green 0.1] +inherits = *common 0.1* + +[sla_material:Jamg He Transparent Orange 0.1] +inherits = *common 0.1* + +[sla_material:Jamg He Transparent Red 0.1] +inherits = *common 0.1* [printer:*common*] printer_technology = FFF @@ -1470,21 +1590,20 @@ end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG printer_technology = SLA printer_model = SL1 printer_variant = default -default_sla_material_profile = Material 1 -bed_shape = 0x0,150x0,150x100,0x100 -max_print_height = 100 -display_width = 150 -display_height = 100 -display_pixels_x = 2000 -display_pixels_y = 1000 +default_sla_material_profile = Jamg He Transparent Green 0.05 +default_sla_print_profile = 0.05 Normal +bed_shape = 0.98x1.02,119.98x1.02,119.98x68.02,0.98x68.02 +display_height = 68.04 +display_orientation = portrait +display_pixels_x = 2560 +display_pixels_y = 1440 +display_width = 120.96 +max_print_height = 150 printer_correction = 1,1,1 - -[printer:Original Prusa SL1 dummy] -inherits = Original Prusa SL1 -printer_variant = dummy -default_sla_material_profile = Material 2 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\n # The obsolete presets will be removed when upgrading from the legacy configuration structure (up to Slic3r 1.39.2) to 1.40.0 and newer. [obsolete_presets] print="0.05mm DETAIL 0.25 nozzle";"0.05mm DETAIL MK3";"0.05mm DETAIL";"0.20mm NORMAL MK3";"0.35mm FAST MK3";"print:0.15mm OPTIMAL MK3 MMU2";"print:0.20mm FAST MK3 MMU2" filament="ColorFabb Brass Bronze 1.75mm";"ColorFabb HT 1.75mm";"ColorFabb nGen 1.75mm";"ColorFabb Woodfil 1.75mm";"ColorFabb XT 1.75mm";"ColorFabb XT-CF20 1.75mm";"E3D PC-ABS 1.75mm";"Fillamentum ABS 1.75mm";"Fillamentum ASA 1.75mm";"Generic ABS 1.75mm";"Generic PET 1.75mm";"Generic PLA 1.75mm";"Prusa ABS 1.75mm";"Prusa HIPS 1.75mm";"Prusa PET 1.75mm";"Prusa PLA 1.75mm";"Taulman Bridge 1.75mm";"Taulman T-Glase 1.75mm" +printer="Original Prusa SL1 dummy" diff --git a/src/eigen/unsupported/Eigen/SparseExtra b/src/eigen/unsupported/Eigen/SparseExtra new file mode 100644 index 000000000..819cffa27 --- /dev/null +++ b/src/eigen/unsupported/Eigen/SparseExtra @@ -0,0 +1,53 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_SPARSE_EXTRA_MODULE_H +#define EIGEN_SPARSE_EXTRA_MODULE_H + +#include "../../Eigen/Sparse" + +#include "../../Eigen/src/Core/util/DisableStupidWarnings.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef EIGEN_GOOGLEHASH_SUPPORT + #include +#endif + +/** + * \defgroup SparseExtra_Module SparseExtra module + * + * This module contains some experimental features extending the sparse module. + * + * \code + * #include + * \endcode + */ + + +#include "src/SparseExtra/DynamicSparseMatrix.h" +#include "src/SparseExtra/BlockOfDynamicSparseMatrix.h" +#include "src/SparseExtra/RandomSetter.h" + +#include "src/SparseExtra/MarketIO.h" + +#if !defined(_WIN32) +#include +#include "src/SparseExtra/MatrixMarketIterator.h" +#endif + +#include "../../Eigen/src/Core/util/ReenableStupidWarnings.h" + +#endif // EIGEN_SPARSE_EXTRA_MODULE_H diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/BlockOfDynamicSparseMatrix.h b/src/eigen/unsupported/Eigen/src/SparseExtra/BlockOfDynamicSparseMatrix.h new file mode 100644 index 000000000..e9ec746e3 --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/BlockOfDynamicSparseMatrix.h @@ -0,0 +1,122 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_SPARSE_BLOCKFORDYNAMICMATRIX_H +#define EIGEN_SPARSE_BLOCKFORDYNAMICMATRIX_H + +namespace Eigen { + +#if 0 + +// NOTE Have to be reimplemented as a specialization of BlockImpl< DynamicSparseMatrix<_Scalar, _Options, _Index>, ... > +// See SparseBlock.h for an example + + +/*************************************************************************** +* specialisation for DynamicSparseMatrix +***************************************************************************/ + +template +class SparseInnerVectorSet, Size> + : public SparseMatrixBase, Size> > +{ + typedef DynamicSparseMatrix<_Scalar, _Options, _Index> MatrixType; + public: + + enum { IsRowMajor = internal::traits::IsRowMajor }; + + EIGEN_SPARSE_PUBLIC_INTERFACE(SparseInnerVectorSet) + class InnerIterator: public MatrixType::InnerIterator + { + public: + inline InnerIterator(const SparseInnerVectorSet& xpr, Index outer) + : MatrixType::InnerIterator(xpr.m_matrix, xpr.m_outerStart + outer), m_outer(outer) + {} + inline Index row() const { return IsRowMajor ? m_outer : this->index(); } + inline Index col() const { return IsRowMajor ? this->index() : m_outer; } + protected: + Index m_outer; + }; + + inline SparseInnerVectorSet(const MatrixType& matrix, Index outerStart, Index outerSize) + : m_matrix(matrix), m_outerStart(outerStart), m_outerSize(outerSize) + { + eigen_assert( (outerStart>=0) && ((outerStart+outerSize)<=matrix.outerSize()) ); + } + + inline SparseInnerVectorSet(const MatrixType& matrix, Index outer) + : m_matrix(matrix), m_outerStart(outer), m_outerSize(Size) + { + eigen_assert(Size!=Dynamic); + eigen_assert( (outer>=0) && (outer + inline SparseInnerVectorSet& operator=(const SparseMatrixBase& other) + { + if (IsRowMajor != ((OtherDerived::Flags&RowMajorBit)==RowMajorBit)) + { + // need to transpose => perform a block evaluation followed by a big swap + DynamicSparseMatrix aux(other); + *this = aux.markAsRValue(); + } + else + { + // evaluate/copy vector per vector + for (Index j=0; j aux(other.innerVector(j)); + m_matrix.const_cast_derived()._data()[m_outerStart+j].swap(aux._data()); + } + } + return *this; + } + + inline SparseInnerVectorSet& operator=(const SparseInnerVectorSet& other) + { + return operator=(other); + } + + Index nonZeros() const + { + Index count = 0; + for (Index j=0; j0); + return m_matrix.data()[m_outerStart].vale(m_matrix.data()[m_outerStart].size()-1); + } + +// template +// inline SparseInnerVectorSet& operator=(const SparseMatrixBase& other) +// { +// return *this; +// } + + EIGEN_STRONG_INLINE Index rows() const { return IsRowMajor ? m_outerSize.value() : m_matrix.rows(); } + EIGEN_STRONG_INLINE Index cols() const { return IsRowMajor ? m_matrix.cols() : m_outerSize.value(); } + + protected: + + const typename MatrixType::Nested m_matrix; + Index m_outerStart; + const internal::variable_if_dynamic m_outerSize; + +}; + +#endif + +} // end namespace Eigen + +#endif // EIGEN_SPARSE_BLOCKFORDYNAMICMATRIX_H diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/BlockSparseMatrix.h b/src/eigen/unsupported/Eigen/src/SparseExtra/BlockSparseMatrix.h new file mode 100644 index 000000000..0e8350a7d --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/BlockSparseMatrix.h @@ -0,0 +1,1079 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2013 Desire Nuentsa +// Copyright (C) 2013 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_SPARSEBLOCKMATRIX_H +#define EIGEN_SPARSEBLOCKMATRIX_H + +namespace Eigen { +/** \ingroup SparseCore_Module + * + * \class BlockSparseMatrix + * + * \brief A versatile sparse matrix representation where each element is a block + * + * This class provides routines to manipulate block sparse matrices stored in a + * BSR-like representation. There are two main types : + * + * 1. All blocks have the same number of rows and columns, called block size + * in the following. In this case, if this block size is known at compile time, + * it can be given as a template parameter like + * \code + * BlockSparseMatrix bmat(b_rows, b_cols); + * \endcode + * Here, bmat is a b_rows x b_cols block sparse matrix + * where each coefficient is a 3x3 dense matrix. + * If the block size is fixed but will be given at runtime, + * \code + * BlockSparseMatrix bmat(b_rows, b_cols); + * bmat.setBlockSize(block_size); + * \endcode + * + * 2. The second case is for variable-block sparse matrices. + * Here each block has its own dimensions. The only restriction is that all the blocks + * in a row (resp. a column) should have the same number of rows (resp. of columns). + * It is thus required in this case to describe the layout of the matrix by calling + * setBlockLayout(rowBlocks, colBlocks). + * + * In any of the previous case, the matrix can be filled by calling setFromTriplets(). + * A regular sparse matrix can be converted to a block sparse matrix and vice versa. + * It is obviously required to describe the block layout beforehand by calling either + * setBlockSize() for fixed-size blocks or setBlockLayout for variable-size blocks. + * + * \tparam _Scalar The Scalar type + * \tparam _BlockAtCompileTime The block layout option. It takes the following values + * Dynamic : block size known at runtime + * a numeric number : fixed-size block known at compile time + */ +template class BlockSparseMatrix; + +template class BlockSparseMatrixView; + +namespace internal { +template +struct traits > +{ + typedef _Scalar Scalar; + typedef _Index Index; + typedef Sparse StorageKind; // FIXME Where is it used ?? + typedef MatrixXpr XprKind; + enum { + RowsAtCompileTime = Dynamic, + ColsAtCompileTime = Dynamic, + MaxRowsAtCompileTime = Dynamic, + MaxColsAtCompileTime = Dynamic, + BlockSize = _BlockAtCompileTime, + Flags = _Options | NestByRefBit | LvalueBit, + CoeffReadCost = NumTraits::ReadCost, + SupportedAccessPatterns = InnerRandomAccessPattern + }; +}; +template +struct traits > +{ + typedef Ref > Scalar; + typedef Ref > RealScalar; + +}; + +// Function object to sort a triplet list +template +struct TripletComp +{ + typedef typename Iterator::value_type Triplet; + bool operator()(const Triplet& a, const Triplet& b) + { if(IsColMajor) + return ((a.col() == b.col() && a.row() < b.row()) || (a.col() < b.col())); + else + return ((a.row() == b.row() && a.col() < b.col()) || (a.row() < b.row())); + } +}; +} // end namespace internal + + +/* Proxy to view the block sparse matrix as a regular sparse matrix */ +template +class BlockSparseMatrixView : public SparseMatrixBase +{ + public: + typedef Ref Scalar; + typedef Ref RealScalar; + typedef typename BlockSparseMatrixT::Index Index; + typedef BlockSparseMatrixT Nested; + enum { + Flags = BlockSparseMatrixT::Options, + Options = BlockSparseMatrixT::Options, + RowsAtCompileTime = BlockSparseMatrixT::RowsAtCompileTime, + ColsAtCompileTime = BlockSparseMatrixT::ColsAtCompileTime, + MaxColsAtCompileTime = BlockSparseMatrixT::MaxColsAtCompileTime, + MaxRowsAtCompileTime = BlockSparseMatrixT::MaxRowsAtCompileTime + }; + public: + BlockSparseMatrixView(const BlockSparseMatrixT& spblockmat) + : m_spblockmat(spblockmat) + {} + + Index outerSize() const + { + return (Flags&RowMajorBit) == 1 ? this->rows() : this->cols(); + } + Index cols() const + { + return m_spblockmat.blockCols(); + } + Index rows() const + { + return m_spblockmat.blockRows(); + } + Scalar coeff(Index row, Index col) + { + return m_spblockmat.coeff(row, col); + } + Scalar coeffRef(Index row, Index col) + { + return m_spblockmat.coeffRef(row, col); + } + // Wrapper to iterate over all blocks + class InnerIterator : public BlockSparseMatrixT::BlockInnerIterator + { + public: + InnerIterator(const BlockSparseMatrixView& mat, Index outer) + : BlockSparseMatrixT::BlockInnerIterator(mat.m_spblockmat, outer) + {} + + }; + + protected: + const BlockSparseMatrixT& m_spblockmat; +}; + +// Proxy to view a regular vector as a block vector +template +class BlockVectorView +{ + public: + enum { + BlockSize = BlockSparseMatrixT::BlockSize, + ColsAtCompileTime = VectorType::ColsAtCompileTime, + RowsAtCompileTime = VectorType::RowsAtCompileTime, + Flags = VectorType::Flags + }; + typedef Ref >Scalar; + typedef typename BlockSparseMatrixT::Index Index; + public: + BlockVectorView(const BlockSparseMatrixT& spblockmat, const VectorType& vec) + : m_spblockmat(spblockmat),m_vec(vec) + { } + inline Index cols() const + { + return m_vec.cols(); + } + inline Index size() const + { + return m_spblockmat.blockRows(); + } + inline Scalar coeff(Index bi) const + { + Index startRow = m_spblockmat.blockRowsIndex(bi); + Index rowSize = m_spblockmat.blockRowsIndex(bi+1) - startRow; + return m_vec.middleRows(startRow, rowSize); + } + inline Scalar coeff(Index bi, Index j) const + { + Index startRow = m_spblockmat.blockRowsIndex(bi); + Index rowSize = m_spblockmat.blockRowsIndex(bi+1) - startRow; + return m_vec.block(startRow, j, rowSize, 1); + } + protected: + const BlockSparseMatrixT& m_spblockmat; + const VectorType& m_vec; +}; + +template class BlockVectorReturn; + + +// Proxy to view a regular vector as a block vector +template +class BlockVectorReturn +{ + public: + enum { + ColsAtCompileTime = VectorType::ColsAtCompileTime, + RowsAtCompileTime = VectorType::RowsAtCompileTime, + Flags = VectorType::Flags + }; + typedef Ref > Scalar; + typedef typename BlockSparseMatrixT::Index Index; + public: + BlockVectorReturn(const BlockSparseMatrixT& spblockmat, VectorType& vec) + : m_spblockmat(spblockmat),m_vec(vec) + { } + inline Index size() const + { + return m_spblockmat.blockRows(); + } + inline Scalar coeffRef(Index bi) + { + Index startRow = m_spblockmat.blockRowsIndex(bi); + Index rowSize = m_spblockmat.blockRowsIndex(bi+1) - startRow; + return m_vec.middleRows(startRow, rowSize); + } + inline Scalar coeffRef(Index bi, Index j) + { + Index startRow = m_spblockmat.blockRowsIndex(bi); + Index rowSize = m_spblockmat.blockRowsIndex(bi+1) - startRow; + return m_vec.block(startRow, j, rowSize, 1); + } + + protected: + const BlockSparseMatrixT& m_spblockmat; + VectorType& m_vec; +}; + +// Block version of the sparse dense product +template +class BlockSparseTimeDenseProduct; + +namespace internal { + +template +struct traits > +{ + typedef Dense StorageKind; + typedef MatrixXpr XprKind; + typedef typename BlockSparseMatrixT::Scalar Scalar; + typedef typename BlockSparseMatrixT::Index Index; + enum { + RowsAtCompileTime = Dynamic, + ColsAtCompileTime = Dynamic, + MaxRowsAtCompileTime = Dynamic, + MaxColsAtCompileTime = Dynamic, + Flags = 0, + CoeffReadCost = internal::traits::CoeffReadCost + }; +}; +} // end namespace internal + +template +class BlockSparseTimeDenseProduct + : public ProductBase, Lhs, Rhs> +{ + public: + EIGEN_PRODUCT_PUBLIC_INTERFACE(BlockSparseTimeDenseProduct) + + BlockSparseTimeDenseProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) + {} + + template void scaleAndAddTo(Dest& dest, const typename Rhs::Scalar& alpha) const + { + BlockVectorReturn tmpDest(m_lhs, dest); + internal::sparse_time_dense_product( BlockSparseMatrixView(m_lhs), BlockVectorView(m_lhs, m_rhs), tmpDest, alpha); + } + + private: + BlockSparseTimeDenseProduct& operator=(const BlockSparseTimeDenseProduct&); +}; + +template +class BlockSparseMatrix : public SparseMatrixBase > +{ + public: + typedef _Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef _StorageIndex StorageIndex; + typedef typename internal::ref_selector >::type Nested; + + enum { + Options = _Options, + Flags = Options, + BlockSize=_BlockAtCompileTime, + RowsAtCompileTime = Dynamic, + ColsAtCompileTime = Dynamic, + MaxRowsAtCompileTime = Dynamic, + MaxColsAtCompileTime = Dynamic, + IsVectorAtCompileTime = 0, + IsColMajor = Flags&RowMajorBit ? 0 : 1 + }; + typedef Matrix BlockScalar; + typedef Matrix BlockRealScalar; + typedef typename internal::conditional<_BlockAtCompileTime==Dynamic, Scalar, BlockScalar>::type BlockScalarReturnType; + typedef BlockSparseMatrix PlainObject; + public: + // Default constructor + BlockSparseMatrix() + : m_innerBSize(0),m_outerBSize(0),m_innerOffset(0),m_outerOffset(0), + m_nonzerosblocks(0),m_values(0),m_blockPtr(0),m_indices(0), + m_outerIndex(0),m_blockSize(BlockSize) + { } + + + /** + * \brief Construct and resize + * + */ + BlockSparseMatrix(Index brow, Index bcol) + : m_innerBSize(IsColMajor ? brow : bcol), + m_outerBSize(IsColMajor ? bcol : brow), + m_innerOffset(0),m_outerOffset(0),m_nonzerosblocks(0), + m_values(0),m_blockPtr(0),m_indices(0), + m_outerIndex(0),m_blockSize(BlockSize) + { } + + /** + * \brief Copy-constructor + */ + BlockSparseMatrix(const BlockSparseMatrix& other) + : m_innerBSize(other.m_innerBSize),m_outerBSize(other.m_outerBSize), + m_nonzerosblocks(other.m_nonzerosblocks),m_nonzeros(other.m_nonzeros), + m_blockPtr(0),m_blockSize(other.m_blockSize) + { + // should we allow copying between variable-size blocks and fixed-size blocks ?? + eigen_assert(m_blockSize == BlockSize && " CAN NOT COPY BETWEEN FIXED-SIZE AND VARIABLE-SIZE BLOCKS"); + + std::copy(other.m_innerOffset, other.m_innerOffset+m_innerBSize+1, m_innerOffset); + std::copy(other.m_outerOffset, other.m_outerOffset+m_outerBSize+1, m_outerOffset); + std::copy(other.m_values, other.m_values+m_nonzeros, m_values); + + if(m_blockSize != Dynamic) + std::copy(other.m_blockPtr, other.m_blockPtr+m_nonzerosblocks, m_blockPtr); + + std::copy(other.m_indices, other.m_indices+m_nonzerosblocks, m_indices); + std::copy(other.m_outerIndex, other.m_outerIndex+m_outerBSize, m_outerIndex); + } + + friend void swap(BlockSparseMatrix& first, BlockSparseMatrix& second) + { + std::swap(first.m_innerBSize, second.m_innerBSize); + std::swap(first.m_outerBSize, second.m_outerBSize); + std::swap(first.m_innerOffset, second.m_innerOffset); + std::swap(first.m_outerOffset, second.m_outerOffset); + std::swap(first.m_nonzerosblocks, second.m_nonzerosblocks); + std::swap(first.m_nonzeros, second.m_nonzeros); + std::swap(first.m_values, second.m_values); + std::swap(first.m_blockPtr, second.m_blockPtr); + std::swap(first.m_indices, second.m_indices); + std::swap(first.m_outerIndex, second.m_outerIndex); + std::swap(first.m_BlockSize, second.m_blockSize); + } + + BlockSparseMatrix& operator=(BlockSparseMatrix other) + { + //Copy-and-swap paradigm ... avoid leaked data if thrown + swap(*this, other); + return *this; + } + + // Destructor + ~BlockSparseMatrix() + { + delete[] m_outerIndex; + delete[] m_innerOffset; + delete[] m_outerOffset; + delete[] m_indices; + delete[] m_blockPtr; + delete[] m_values; + } + + + /** + * \brief Constructor from a sparse matrix + * + */ + template + inline BlockSparseMatrix(const MatrixType& spmat) : m_blockSize(BlockSize) + { + EIGEN_STATIC_ASSERT((m_blockSize != Dynamic), THIS_METHOD_IS_ONLY_FOR_FIXED_SIZE); + + *this = spmat; + } + + /** + * \brief Assignment from a sparse matrix with the same storage order + * + * Convert from a sparse matrix to block sparse matrix. + * \warning Before calling this function, tt is necessary to call + * either setBlockLayout() (matrices with variable-size blocks) + * or setBlockSize() (for fixed-size blocks). + */ + template + inline BlockSparseMatrix& operator=(const MatrixType& spmat) + { + eigen_assert((m_innerBSize != 0 && m_outerBSize != 0) + && "Trying to assign to a zero-size matrix, call resize() first"); + eigen_assert(((MatrixType::Options&RowMajorBit) != IsColMajor) && "Wrong storage order"); + typedef SparseMatrix MatrixPatternType; + MatrixPatternType blockPattern(blockRows(), blockCols()); + m_nonzeros = 0; + + // First, compute the number of nonzero blocks and their locations + for(StorageIndex bj = 0; bj < m_outerBSize; ++bj) + { + // Browse each outer block and compute the structure + std::vector nzblocksFlag(m_innerBSize,false); // Record the existing blocks + blockPattern.startVec(bj); + for(StorageIndex j = blockOuterIndex(bj); j < blockOuterIndex(bj+1); ++j) + { + typename MatrixType::InnerIterator it_spmat(spmat, j); + for(; it_spmat; ++it_spmat) + { + StorageIndex bi = innerToBlock(it_spmat.index()); // Index of the current nonzero block + if(!nzblocksFlag[bi]) + { + // Save the index of this nonzero block + nzblocksFlag[bi] = true; + blockPattern.insertBackByOuterInnerUnordered(bj, bi) = true; + // Compute the total number of nonzeros (including explicit zeros in blocks) + m_nonzeros += blockOuterSize(bj) * blockInnerSize(bi); + } + } + } // end current outer block + } + blockPattern.finalize(); + + // Allocate the internal arrays + setBlockStructure(blockPattern); + + for(StorageIndex nz = 0; nz < m_nonzeros; ++nz) m_values[nz] = Scalar(0); + for(StorageIndex bj = 0; bj < m_outerBSize; ++bj) + { + // Now copy the values + for(StorageIndex j = blockOuterIndex(bj); j < blockOuterIndex(bj+1); ++j) + { + // Browse the outer block column by column (for column-major matrices) + typename MatrixType::InnerIterator it_spmat(spmat, j); + for(; it_spmat; ++it_spmat) + { + StorageIndex idx = 0; // Position of this block in the column block + StorageIndex bi = innerToBlock(it_spmat.index()); // Index of the current nonzero block + // Go to the inner block where this element belongs to + while(bi > m_indices[m_outerIndex[bj]+idx]) ++idx; // Not expensive for ordered blocks + StorageIndex idxVal;// Get the right position in the array of values for this element + if(m_blockSize == Dynamic) + { + // Offset from all blocks before ... + idxVal = m_blockPtr[m_outerIndex[bj]+idx]; + // ... and offset inside the block + idxVal += (j - blockOuterIndex(bj)) * blockOuterSize(bj) + it_spmat.index() - m_innerOffset[bi]; + } + else + { + // All blocks before + idxVal = (m_outerIndex[bj] + idx) * m_blockSize * m_blockSize; + // inside the block + idxVal += (j - blockOuterIndex(bj)) * m_blockSize + (it_spmat.index()%m_blockSize); + } + // Insert the value + m_values[idxVal] = it_spmat.value(); + } // end of this column + } // end of this block + } // end of this outer block + + return *this; + } + + /** + * \brief Set the nonzero block pattern of the matrix + * + * Given a sparse matrix describing the nonzero block pattern, + * this function prepares the internal pointers for values. + * After calling this function, any *nonzero* block (bi, bj) can be set + * with a simple call to coeffRef(bi,bj). + * + * + * \warning Before calling this function, tt is necessary to call + * either setBlockLayout() (matrices with variable-size blocks) + * or setBlockSize() (for fixed-size blocks). + * + * \param blockPattern Sparse matrix of boolean elements describing the block structure + * + * \sa setBlockLayout() \sa setBlockSize() + */ + template + void setBlockStructure(const MatrixType& blockPattern) + { + resize(blockPattern.rows(), blockPattern.cols()); + reserve(blockPattern.nonZeros()); + + // Browse the block pattern and set up the various pointers + m_outerIndex[0] = 0; + if(m_blockSize == Dynamic) m_blockPtr[0] = 0; + for(StorageIndex nz = 0; nz < m_nonzeros; ++nz) m_values[nz] = Scalar(0); + for(StorageIndex bj = 0; bj < m_outerBSize; ++bj) + { + //Browse each outer block + + //First, copy and save the indices of nonzero blocks + //FIXME : find a way to avoid this ... + std::vector nzBlockIdx; + typename MatrixType::InnerIterator it(blockPattern, bj); + for(; it; ++it) + { + nzBlockIdx.push_back(it.index()); + } + std::sort(nzBlockIdx.begin(), nzBlockIdx.end()); + + // Now, fill block indices and (eventually) pointers to blocks + for(StorageIndex idx = 0; idx < nzBlockIdx.size(); ++idx) + { + StorageIndex offset = m_outerIndex[bj]+idx; // offset in m_indices + m_indices[offset] = nzBlockIdx[idx]; + if(m_blockSize == Dynamic) + m_blockPtr[offset] = m_blockPtr[offset-1] + blockInnerSize(nzBlockIdx[idx]) * blockOuterSize(bj); + // There is no blockPtr for fixed-size blocks... not needed !??? + } + // Save the pointer to the next outer block + m_outerIndex[bj+1] = m_outerIndex[bj] + nzBlockIdx.size(); + } + } + + /** + * \brief Set the number of rows and columns blocks + */ + inline void resize(Index brow, Index bcol) + { + m_innerBSize = IsColMajor ? brow : bcol; + m_outerBSize = IsColMajor ? bcol : brow; + } + + /** + * \brief set the block size at runtime for fixed-size block layout + * + * Call this only for fixed-size blocks + */ + inline void setBlockSize(Index blockSize) + { + m_blockSize = blockSize; + } + + /** + * \brief Set the row and column block layouts, + * + * This function set the size of each row and column block. + * So this function should be used only for blocks with variable size. + * \param rowBlocks : Number of rows per row block + * \param colBlocks : Number of columns per column block + * \sa resize(), setBlockSize() + */ + inline void setBlockLayout(const VectorXi& rowBlocks, const VectorXi& colBlocks) + { + const VectorXi& innerBlocks = IsColMajor ? rowBlocks : colBlocks; + const VectorXi& outerBlocks = IsColMajor ? colBlocks : rowBlocks; + eigen_assert(m_innerBSize == innerBlocks.size() && "CHECK THE NUMBER OF ROW OR COLUMN BLOCKS"); + eigen_assert(m_outerBSize == outerBlocks.size() && "CHECK THE NUMBER OF ROW OR COLUMN BLOCKS"); + m_outerBSize = outerBlocks.size(); + // starting index of blocks... cumulative sums + m_innerOffset = new StorageIndex[m_innerBSize+1]; + m_outerOffset = new StorageIndex[m_outerBSize+1]; + m_innerOffset[0] = 0; + m_outerOffset[0] = 0; + std::partial_sum(&innerBlocks[0], &innerBlocks[m_innerBSize-1]+1, &m_innerOffset[1]); + std::partial_sum(&outerBlocks[0], &outerBlocks[m_outerBSize-1]+1, &m_outerOffset[1]); + + // Compute the total number of nonzeros + m_nonzeros = 0; + for(StorageIndex bj = 0; bj < m_outerBSize; ++bj) + for(StorageIndex bi = 0; bi < m_innerBSize; ++bi) + m_nonzeros += outerBlocks[bj] * innerBlocks[bi]; + + } + + /** + * \brief Allocate the internal array of pointers to blocks and their inner indices + * + * \note For fixed-size blocks, call setBlockSize() to set the block. + * And For variable-size blocks, call setBlockLayout() before using this function + * + * \param nonzerosblocks Number of nonzero blocks. The total number of nonzeros is + * is computed in setBlockLayout() for variable-size blocks + * \sa setBlockSize() + */ + inline void reserve(const Index nonzerosblocks) + { + eigen_assert((m_innerBSize != 0 && m_outerBSize != 0) && + "TRYING TO RESERVE ZERO-SIZE MATRICES, CALL resize() first"); + + //FIXME Should free if already allocated + m_outerIndex = new StorageIndex[m_outerBSize+1]; + + m_nonzerosblocks = nonzerosblocks; + if(m_blockSize != Dynamic) + { + m_nonzeros = nonzerosblocks * (m_blockSize * m_blockSize); + m_blockPtr = 0; + } + else + { + // m_nonzeros is already computed in setBlockLayout() + m_blockPtr = new StorageIndex[m_nonzerosblocks+1]; + } + m_indices = new StorageIndex[m_nonzerosblocks+1]; + m_values = new Scalar[m_nonzeros]; + } + + + /** + * \brief Fill values in a matrix from a triplet list. + * + * Each triplet item has a block stored in an Eigen dense matrix. + * The InputIterator class should provide the functions row(), col() and value() + * + * \note For fixed-size blocks, call setBlockSize() before this function. + * + * FIXME Do not accept duplicates + */ + template + void setFromTriplets(const InputIterator& begin, const InputIterator& end) + { + eigen_assert((m_innerBSize!=0 && m_outerBSize !=0) && "ZERO BLOCKS, PLEASE CALL resize() before"); + + /* First, sort the triplet list + * FIXME This can be unnecessarily expensive since only the inner indices have to be sorted + * The best approach is like in SparseMatrix::setFromTriplets() + */ + internal::TripletComp tripletcomp; + std::sort(begin, end, tripletcomp); + + /* Count the number of rows and column blocks, + * and the number of nonzero blocks per outer dimension + */ + VectorXi rowBlocks(m_innerBSize); // Size of each block row + VectorXi colBlocks(m_outerBSize); // Size of each block column + rowBlocks.setZero(); colBlocks.setZero(); + VectorXi nzblock_outer(m_outerBSize); // Number of nz blocks per outer vector + VectorXi nz_outer(m_outerBSize); // Number of nz per outer vector...for variable-size blocks + nzblock_outer.setZero(); + nz_outer.setZero(); + for(InputIterator it(begin); it !=end; ++it) + { + eigen_assert(it->row() >= 0 && it->row() < this->blockRows() && it->col() >= 0 && it->col() < this->blockCols()); + eigen_assert((it->value().rows() == it->value().cols() && (it->value().rows() == m_blockSize)) + || (m_blockSize == Dynamic)); + + if(m_blockSize == Dynamic) + { + eigen_assert((rowBlocks[it->row()] == 0 || rowBlocks[it->row()] == it->value().rows()) && + "NON CORRESPONDING SIZES FOR ROW BLOCKS"); + eigen_assert((colBlocks[it->col()] == 0 || colBlocks[it->col()] == it->value().cols()) && + "NON CORRESPONDING SIZES FOR COLUMN BLOCKS"); + rowBlocks[it->row()] =it->value().rows(); + colBlocks[it->col()] = it->value().cols(); + } + nz_outer(IsColMajor ? it->col() : it->row()) += it->value().rows() * it->value().cols(); + nzblock_outer(IsColMajor ? it->col() : it->row())++; + } + // Allocate member arrays + if(m_blockSize == Dynamic) setBlockLayout(rowBlocks, colBlocks); + StorageIndex nzblocks = nzblock_outer.sum(); + reserve(nzblocks); + + // Temporary markers + VectorXi block_id(m_outerBSize); // To be used as a block marker during insertion + + // Setup outer index pointers and markers + m_outerIndex[0] = 0; + if (m_blockSize == Dynamic) m_blockPtr[0] = 0; + for(StorageIndex bj = 0; bj < m_outerBSize; ++bj) + { + m_outerIndex[bj+1] = m_outerIndex[bj] + nzblock_outer(bj); + block_id(bj) = m_outerIndex[bj]; + if(m_blockSize==Dynamic) + { + m_blockPtr[m_outerIndex[bj+1]] = m_blockPtr[m_outerIndex[bj]] + nz_outer(bj); + } + } + + // Fill the matrix + for(InputIterator it(begin); it!=end; ++it) + { + StorageIndex outer = IsColMajor ? it->col() : it->row(); + StorageIndex inner = IsColMajor ? it->row() : it->col(); + m_indices[block_id(outer)] = inner; + StorageIndex block_size = it->value().rows()*it->value().cols(); + StorageIndex nz_marker = blockPtr(block_id[outer]); + memcpy(&(m_values[nz_marker]), it->value().data(), block_size * sizeof(Scalar)); + if(m_blockSize == Dynamic) + { + m_blockPtr[block_id(outer)+1] = m_blockPtr[block_id(outer)] + block_size; + } + block_id(outer)++; + } + + // An alternative when the outer indices are sorted...no need to use an array of markers +// for(Index bcol = 0; bcol < m_outerBSize; ++bcol) +// { +// Index id = 0, id_nz = 0, id_nzblock = 0; +// for(InputIterator it(begin); it!=end; ++it) +// { +// while (idvalue().rows()*it->value().cols(); +// m_blockPtr[id_nzblock+1] = m_blockPtr[id_nzblock] + block_size; +// id_nzblock++; +// memcpy(&(m_values[id_nz]),it->value().data(), block_size*sizeof(Scalar)); +// id_nz += block_size; +// } +// while(id < m_outerBSize-1) // Empty columns at the end +// { +// id++; +// m_outerIndex[id+1]=m_outerIndex[id]; +// } +// } + } + + + /** + * \returns the number of rows + */ + inline Index rows() const + { +// return blockRows(); + return (IsColMajor ? innerSize() : outerSize()); + } + + /** + * \returns the number of cols + */ + inline Index cols() const + { +// return blockCols(); + return (IsColMajor ? outerSize() : innerSize()); + } + + inline Index innerSize() const + { + if(m_blockSize == Dynamic) return m_innerOffset[m_innerBSize]; + else return (m_innerBSize * m_blockSize) ; + } + + inline Index outerSize() const + { + if(m_blockSize == Dynamic) return m_outerOffset[m_outerBSize]; + else return (m_outerBSize * m_blockSize) ; + } + /** \returns the number of rows grouped by blocks */ + inline Index blockRows() const + { + return (IsColMajor ? m_innerBSize : m_outerBSize); + } + /** \returns the number of columns grouped by blocks */ + inline Index blockCols() const + { + return (IsColMajor ? m_outerBSize : m_innerBSize); + } + + inline Index outerBlocks() const { return m_outerBSize; } + inline Index innerBlocks() const { return m_innerBSize; } + + /** \returns the block index where outer belongs to */ + inline Index outerToBlock(Index outer) const + { + eigen_assert(outer < outerSize() && "OUTER INDEX OUT OF BOUNDS"); + + if(m_blockSize != Dynamic) + return (outer / m_blockSize); // Integer division + + StorageIndex b_outer = 0; + while(m_outerOffset[b_outer] <= outer) ++b_outer; + return b_outer - 1; + } + /** \returns the block index where inner belongs to */ + inline Index innerToBlock(Index inner) const + { + eigen_assert(inner < innerSize() && "OUTER INDEX OUT OF BOUNDS"); + + if(m_blockSize != Dynamic) + return (inner / m_blockSize); // Integer division + + StorageIndex b_inner = 0; + while(m_innerOffset[b_inner] <= inner) ++b_inner; + return b_inner - 1; + } + + /** + *\returns a reference to the (i,j) block as an Eigen Dense Matrix + */ + Ref coeffRef(Index brow, Index bcol) + { + eigen_assert(brow < blockRows() && "BLOCK ROW INDEX OUT OF BOUNDS"); + eigen_assert(bcol < blockCols() && "BLOCK nzblocksFlagCOLUMN OUT OF BOUNDS"); + + StorageIndex rsize = IsColMajor ? blockInnerSize(brow): blockOuterSize(bcol); + StorageIndex csize = IsColMajor ? blockOuterSize(bcol) : blockInnerSize(brow); + StorageIndex inner = IsColMajor ? brow : bcol; + StorageIndex outer = IsColMajor ? bcol : brow; + StorageIndex offset = m_outerIndex[outer]; + while(offset < m_outerIndex[outer+1] && m_indices[offset] != inner) + offset++; + if(m_indices[offset] == inner) + { + return Map(&(m_values[blockPtr(offset)]), rsize, csize); + } + else + { + //FIXME the block does not exist, Insert it !!!!!!!!! + eigen_assert("DYNAMIC INSERTION IS NOT YET SUPPORTED"); + } + } + + /** + * \returns the value of the (i,j) block as an Eigen Dense Matrix + */ + Map coeff(Index brow, Index bcol) const + { + eigen_assert(brow < blockRows() && "BLOCK ROW INDEX OUT OF BOUNDS"); + eigen_assert(bcol < blockCols() && "BLOCK COLUMN OUT OF BOUNDS"); + + StorageIndex rsize = IsColMajor ? blockInnerSize(brow): blockOuterSize(bcol); + StorageIndex csize = IsColMajor ? blockOuterSize(bcol) : blockInnerSize(brow); + StorageIndex inner = IsColMajor ? brow : bcol; + StorageIndex outer = IsColMajor ? bcol : brow; + StorageIndex offset = m_outerIndex[outer]; + while(offset < m_outerIndex[outer+1] && m_indices[offset] != inner) offset++; + if(m_indices[offset] == inner) + { + return Map (&(m_values[blockPtr(offset)]), rsize, csize); + } + else +// return BlockScalar::Zero(rsize, csize); + eigen_assert("NOT YET SUPPORTED"); + } + + // Block Matrix times vector product + template + BlockSparseTimeDenseProduct operator*(const VecType& lhs) const + { + return BlockSparseTimeDenseProduct(*this, lhs); + } + + /** \returns the number of nonzero blocks */ + inline Index nonZerosBlocks() const { return m_nonzerosblocks; } + /** \returns the total number of nonzero elements, including eventual explicit zeros in blocks */ + inline Index nonZeros() const { return m_nonzeros; } + + inline BlockScalarReturnType *valuePtr() {return static_cast(m_values);} +// inline Scalar *valuePtr(){ return m_values; } + inline StorageIndex *innerIndexPtr() {return m_indices; } + inline const StorageIndex *innerIndexPtr() const {return m_indices; } + inline StorageIndex *outerIndexPtr() {return m_outerIndex; } + inline const StorageIndex* outerIndexPtr() const {return m_outerIndex; } + + /** \brief for compatibility purposes with the SparseMatrix class */ + inline bool isCompressed() const {return true;} + /** + * \returns the starting index of the bi row block + */ + inline Index blockRowsIndex(Index bi) const + { + return IsColMajor ? blockInnerIndex(bi) : blockOuterIndex(bi); + } + + /** + * \returns the starting index of the bj col block + */ + inline Index blockColsIndex(Index bj) const + { + return IsColMajor ? blockOuterIndex(bj) : blockInnerIndex(bj); + } + + inline Index blockOuterIndex(Index bj) const + { + return (m_blockSize == Dynamic) ? m_outerOffset[bj] : (bj * m_blockSize); + } + inline Index blockInnerIndex(Index bi) const + { + return (m_blockSize == Dynamic) ? m_innerOffset[bi] : (bi * m_blockSize); + } + + // Not needed ??? + inline Index blockInnerSize(Index bi) const + { + return (m_blockSize == Dynamic) ? (m_innerOffset[bi+1] - m_innerOffset[bi]) : m_blockSize; + } + inline Index blockOuterSize(Index bj) const + { + return (m_blockSize == Dynamic) ? (m_outerOffset[bj+1]- m_outerOffset[bj]) : m_blockSize; + } + + /** + * \brief Browse the matrix by outer index + */ + class InnerIterator; // Browse column by column + + /** + * \brief Browse the matrix by block outer index + */ + class BlockInnerIterator; // Browse block by block + + friend std::ostream & operator << (std::ostream & s, const BlockSparseMatrix& m) + { + for (StorageIndex j = 0; j < m.outerBlocks(); ++j) + { + BlockInnerIterator itb(m, j); + for(; itb; ++itb) + { + s << "("< in the array of values + */ + Index blockPtr(Index id) const + { + if(m_blockSize == Dynamic) return m_blockPtr[id]; + else return id * m_blockSize * m_blockSize; + //return blockDynIdx(id, typename internal::conditional<(BlockSize==Dynamic), internal::true_type, internal::false_type>::type()); + } + + + protected: +// inline Index blockDynIdx(Index id, internal::true_type) const +// { +// return m_blockPtr[id]; +// } +// inline Index blockDynIdx(Index id, internal::false_type) const +// { +// return id * BlockSize * BlockSize; +// } + + // To be implemented + // Insert a block at a particular location... need to make a room for that + Map insert(Index brow, Index bcol); + + Index m_innerBSize; // Number of block rows + Index m_outerBSize; // Number of block columns + StorageIndex *m_innerOffset; // Starting index of each inner block (size m_innerBSize+1) + StorageIndex *m_outerOffset; // Starting index of each outer block (size m_outerBSize+1) + Index m_nonzerosblocks; // Total nonzeros blocks (lower than m_innerBSize x m_outerBSize) + Index m_nonzeros; // Total nonzeros elements + Scalar *m_values; //Values stored block column after block column (size m_nonzeros) + StorageIndex *m_blockPtr; // Pointer to the beginning of each block in m_values, size m_nonzeroblocks ... null for fixed-size blocks + StorageIndex *m_indices; //Inner block indices, size m_nonzerosblocks ... OK + StorageIndex *m_outerIndex; // Starting pointer of each block column in m_indices (size m_outerBSize)... OK + Index m_blockSize; // Size of a block for fixed-size blocks, otherwise -1 +}; + +template +class BlockSparseMatrix<_Scalar, _BlockAtCompileTime, _Options, _StorageIndex>::BlockInnerIterator +{ + public: + + enum{ + Flags = _Options + }; + + BlockInnerIterator(const BlockSparseMatrix& mat, const Index outer) + : m_mat(mat),m_outer(outer), + m_id(mat.m_outerIndex[outer]), + m_end(mat.m_outerIndex[outer+1]) + { + } + + inline BlockInnerIterator& operator++() {m_id++; return *this; } + + inline const Map value() const + { + return Map(&(m_mat.m_values[m_mat.blockPtr(m_id)]), + rows(),cols()); + } + inline Map valueRef() + { + return Map(&(m_mat.m_values[m_mat.blockPtr(m_id)]), + rows(),cols()); + } + // Block inner index + inline Index index() const {return m_mat.m_indices[m_id]; } + inline Index outer() const { return m_outer; } + // block row index + inline Index row() const {return index(); } + // block column index + inline Index col() const {return outer(); } + // FIXME Number of rows in the current block + inline Index rows() const { return (m_mat.m_blockSize==Dynamic) ? (m_mat.m_innerOffset[index()+1] - m_mat.m_innerOffset[index()]) : m_mat.m_blockSize; } + // Number of columns in the current block ... + inline Index cols() const { return (m_mat.m_blockSize==Dynamic) ? (m_mat.m_outerOffset[m_outer+1]-m_mat.m_outerOffset[m_outer]) : m_mat.m_blockSize;} + inline operator bool() const { return (m_id < m_end); } + + protected: + const BlockSparseMatrix<_Scalar, _BlockAtCompileTime, _Options, StorageIndex>& m_mat; + const Index m_outer; + Index m_id; + Index m_end; +}; + +template +class BlockSparseMatrix<_Scalar, _BlockAtCompileTime, _Options, _StorageIndex>::InnerIterator +{ + public: + InnerIterator(const BlockSparseMatrix& mat, Index outer) + : m_mat(mat),m_outerB(mat.outerToBlock(outer)),m_outer(outer), + itb(mat, mat.outerToBlock(outer)), + m_offset(outer - mat.blockOuterIndex(m_outerB)) + { + if (itb) + { + m_id = m_mat.blockInnerIndex(itb.index()); + m_start = m_id; + m_end = m_mat.blockInnerIndex(itb.index()+1); + } + } + inline InnerIterator& operator++() + { + m_id++; + if (m_id >= m_end) + { + ++itb; + if (itb) + { + m_id = m_mat.blockInnerIndex(itb.index()); + m_start = m_id; + m_end = m_mat.blockInnerIndex(itb.index()+1); + } + } + return *this; + } + inline const Scalar& value() const + { + return itb.value().coeff(m_id - m_start, m_offset); + } + inline Scalar& valueRef() + { + return itb.valueRef().coeff(m_id - m_start, m_offset); + } + inline Index index() const { return m_id; } + inline Index outer() const {return m_outer; } + inline Index col() const {return outer(); } + inline Index row() const { return index();} + inline operator bool() const + { + return itb; + } + protected: + const BlockSparseMatrix& m_mat; + const Index m_outer; + const Index m_outerB; + BlockInnerIterator itb; // Iterator through the blocks + const Index m_offset; // Position of this column in the block + Index m_start; // starting inner index of this block + Index m_id; // current inner index in the block + Index m_end; // starting inner index of the next block + +}; +} // end namespace Eigen + +#endif // EIGEN_SPARSEBLOCKMATRIX_H diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/DynamicSparseMatrix.h b/src/eigen/unsupported/Eigen/src/SparseExtra/DynamicSparseMatrix.h new file mode 100644 index 000000000..037a13f86 --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/DynamicSparseMatrix.h @@ -0,0 +1,392 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_DYNAMIC_SPARSEMATRIX_H +#define EIGEN_DYNAMIC_SPARSEMATRIX_H + +namespace Eigen { + +/** \deprecated use a SparseMatrix in an uncompressed mode + * + * \class DynamicSparseMatrix + * + * \brief A sparse matrix class designed for matrix assembly purpose + * + * \param _Scalar the scalar type, i.e. the type of the coefficients + * + * Unlike SparseMatrix, this class provides a much higher degree of flexibility. In particular, it allows + * random read/write accesses in log(rho*outer_size) where \c rho is the probability that a coefficient is + * nonzero and outer_size is the number of columns if the matrix is column-major and the number of rows + * otherwise. + * + * Internally, the data are stored as a std::vector of compressed vector. The performances of random writes might + * decrease as the number of nonzeros per inner-vector increase. In practice, we observed very good performance + * till about 100 nonzeros/vector, and the performance remains relatively good till 500 nonzeros/vectors. + * + * \see SparseMatrix + */ + +namespace internal { +template +struct traits > +{ + typedef _Scalar Scalar; + typedef _StorageIndex StorageIndex; + typedef Sparse StorageKind; + typedef MatrixXpr XprKind; + enum { + RowsAtCompileTime = Dynamic, + ColsAtCompileTime = Dynamic, + MaxRowsAtCompileTime = Dynamic, + MaxColsAtCompileTime = Dynamic, + Flags = _Options | NestByRefBit | LvalueBit, + CoeffReadCost = NumTraits::ReadCost, + SupportedAccessPatterns = OuterRandomAccessPattern + }; +}; +} + +template + class DynamicSparseMatrix + : public SparseMatrixBase > +{ + typedef SparseMatrixBase Base; + using Base::convert_index; + public: + EIGEN_SPARSE_PUBLIC_INTERFACE(DynamicSparseMatrix) + // FIXME: why are these operator already alvailable ??? + // EIGEN_SPARSE_INHERIT_ASSIGNMENT_OPERATOR(DynamicSparseMatrix, +=) + // EIGEN_SPARSE_INHERIT_ASSIGNMENT_OPERATOR(DynamicSparseMatrix, -=) + typedef MappedSparseMatrix Map; + using Base::IsRowMajor; + using Base::operator=; + enum { + Options = _Options + }; + + protected: + + typedef DynamicSparseMatrix TransposedSparseMatrix; + + Index m_innerSize; + std::vector > m_data; + + public: + + inline Index rows() const { return IsRowMajor ? outerSize() : m_innerSize; } + inline Index cols() const { return IsRowMajor ? m_innerSize : outerSize(); } + inline Index innerSize() const { return m_innerSize; } + inline Index outerSize() const { return convert_index(m_data.size()); } + inline Index innerNonZeros(Index j) const { return m_data[j].size(); } + + std::vector >& _data() { return m_data; } + const std::vector >& _data() const { return m_data; } + + /** \returns the coefficient value at given position \a row, \a col + * This operation involes a log(rho*outer_size) binary search. + */ + inline Scalar coeff(Index row, Index col) const + { + const Index outer = IsRowMajor ? row : col; + const Index inner = IsRowMajor ? col : row; + return m_data[outer].at(inner); + } + + /** \returns a reference to the coefficient value at given position \a row, \a col + * This operation involes a log(rho*outer_size) binary search. If the coefficient does not + * exist yet, then a sorted insertion into a sequential buffer is performed. + */ + inline Scalar& coeffRef(Index row, Index col) + { + const Index outer = IsRowMajor ? row : col; + const Index inner = IsRowMajor ? col : row; + return m_data[outer].atWithInsertion(inner); + } + + class InnerIterator; + class ReverseInnerIterator; + + void setZero() + { + for (Index j=0; j0) + { + Index reserveSizePerVector = (std::max)(reserveSize/outerSize(),Index(4)); + for (Index j=0; j(m_data[outer].size()) - 1; + m_data[outer].resize(id+2,1); + + while ( (id >= startId) && (m_data[outer].index(id) > inner) ) + { + m_data[outer].index(id+1) = m_data[outer].index(id); + m_data[outer].value(id+1) = m_data[outer].value(id); + --id; + } + m_data[outer].index(id+1) = inner; + m_data[outer].value(id+1) = 0; + return m_data[outer].value(id+1); + } + + /** Does nothing: provided for compatibility with SparseMatrix */ + inline void finalize() {} + + /** Suppress all nonzeros which are smaller than \a reference under the tolerence \a epsilon */ + void prune(Scalar reference, RealScalar epsilon = NumTraits::dummy_precision()) + { + for (Index j=0; jinnerSize) + { + // remove all coefficients with innerCoord>=innerSize + // TODO + //std::cerr << "not implemented yet\n"; + exit(2); + } + if (m_data.size() != outerSize) + { + m_data.resize(outerSize); + } + } + + /** The class DynamicSparseMatrix is deprectaed */ + EIGEN_DEPRECATED inline DynamicSparseMatrix() + : m_innerSize(0), m_data(0) + { + eigen_assert(innerSize()==0 && outerSize()==0); + } + + /** The class DynamicSparseMatrix is deprectaed */ + EIGEN_DEPRECATED inline DynamicSparseMatrix(Index rows, Index cols) + : m_innerSize(0) + { + resize(rows, cols); + } + + /** The class DynamicSparseMatrix is deprectaed */ + template + EIGEN_DEPRECATED explicit inline DynamicSparseMatrix(const SparseMatrixBase& other) + : m_innerSize(0) + { + Base::operator=(other.derived()); + } + + inline DynamicSparseMatrix(const DynamicSparseMatrix& other) + : Base(), m_innerSize(0) + { + *this = other.derived(); + } + + inline void swap(DynamicSparseMatrix& other) + { + //EIGEN_DBG_SPARSE(std::cout << "SparseMatrix:: swap\n"); + std::swap(m_innerSize, other.m_innerSize); + //std::swap(m_outerSize, other.m_outerSize); + m_data.swap(other.m_data); + } + + inline DynamicSparseMatrix& operator=(const DynamicSparseMatrix& other) + { + if (other.isRValue()) + { + swap(other.const_cast_derived()); + } + else + { + resize(other.rows(), other.cols()); + m_data = other.m_data; + } + return *this; + } + + /** Destructor */ + inline ~DynamicSparseMatrix() {} + + public: + + /** \deprecated + * Set the matrix to zero and reserve the memory for \a reserveSize nonzero coefficients. */ + EIGEN_DEPRECATED void startFill(Index reserveSize = 1000) + { + setZero(); + reserve(reserveSize); + } + + /** \deprecated use insert() + * inserts a nonzero coefficient at given coordinates \a row, \a col and returns its reference assuming that: + * 1 - the coefficient does not exist yet + * 2 - this the coefficient with greater inner coordinate for the given outer coordinate. + * In other words, assuming \c *this is column-major, then there must not exists any nonzero coefficient of coordinates + * \c i \c x \a col such that \c i >= \a row. Otherwise the matrix is invalid. + * + * \see fillrand(), coeffRef() + */ + EIGEN_DEPRECATED Scalar& fill(Index row, Index col) + { + const Index outer = IsRowMajor ? row : col; + const Index inner = IsRowMajor ? col : row; + return insertBack(outer,inner); + } + + /** \deprecated use insert() + * Like fill() but with random inner coordinates. + * Compared to the generic coeffRef(), the unique limitation is that we assume + * the coefficient does not exist yet. + */ + EIGEN_DEPRECATED Scalar& fillrand(Index row, Index col) + { + return insert(row,col); + } + + /** \deprecated use finalize() + * Does nothing. Provided for compatibility with SparseMatrix. */ + EIGEN_DEPRECATED void endFill() {} + +# ifdef EIGEN_DYNAMICSPARSEMATRIX_PLUGIN +# include EIGEN_DYNAMICSPARSEMATRIX_PLUGIN +# endif + }; + +template +class DynamicSparseMatrix::InnerIterator : public SparseVector::InnerIterator +{ + typedef typename SparseVector::InnerIterator Base; + public: + InnerIterator(const DynamicSparseMatrix& mat, Index outer) + : Base(mat.m_data[outer]), m_outer(outer) + {} + + inline Index row() const { return IsRowMajor ? m_outer : Base::index(); } + inline Index col() const { return IsRowMajor ? Base::index() : m_outer; } + inline Index outer() const { return m_outer; } + + protected: + const Index m_outer; +}; + +template +class DynamicSparseMatrix::ReverseInnerIterator : public SparseVector::ReverseInnerIterator +{ + typedef typename SparseVector::ReverseInnerIterator Base; + public: + ReverseInnerIterator(const DynamicSparseMatrix& mat, Index outer) + : Base(mat.m_data[outer]), m_outer(outer) + {} + + inline Index row() const { return IsRowMajor ? m_outer : Base::index(); } + inline Index col() const { return IsRowMajor ? Base::index() : m_outer; } + inline Index outer() const { return m_outer; } + + protected: + const Index m_outer; +}; + +namespace internal { + +template +struct evaluator > + : evaluator_base > +{ + typedef _Scalar Scalar; + typedef DynamicSparseMatrix<_Scalar,_Options,_StorageIndex> SparseMatrixType; + typedef typename SparseMatrixType::InnerIterator InnerIterator; + typedef typename SparseMatrixType::ReverseInnerIterator ReverseInnerIterator; + + enum { + CoeffReadCost = NumTraits<_Scalar>::ReadCost, + Flags = SparseMatrixType::Flags + }; + + evaluator() : m_matrix(0) {} + evaluator(const SparseMatrixType &mat) : m_matrix(&mat) {} + + operator SparseMatrixType&() { return m_matrix->const_cast_derived(); } + operator const SparseMatrixType&() const { return *m_matrix; } + + Scalar coeff(Index row, Index col) const { return m_matrix->coeff(row,col); } + + Index nonZerosEstimate() const { return m_matrix->nonZeros(); } + + const SparseMatrixType *m_matrix; +}; + +} + +} // end namespace Eigen + +#endif // EIGEN_DYNAMIC_SPARSEMATRIX_H diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/MarketIO.h b/src/eigen/unsupported/Eigen/src/SparseExtra/MarketIO.h new file mode 100644 index 000000000..41e4af4a4 --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/MarketIO.h @@ -0,0 +1,275 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Gael Guennebaud +// Copyright (C) 2012 Desire NUENTSA WAKAM +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_SPARSE_MARKET_IO_H +#define EIGEN_SPARSE_MARKET_IO_H + +#include + +namespace Eigen { + +namespace internal +{ + template + inline bool GetMarketLine (std::stringstream& line, Index& M, Index& N, Index& i, Index& j, Scalar& value) + { + line >> i >> j >> value; + i--; + j--; + if(i>=0 && j>=0 && i + inline bool GetMarketLine (std::stringstream& line, Index& M, Index& N, Index& i, Index& j, std::complex& value) + { + Scalar valR, valI; + line >> i >> j >> valR >> valI; + i--; + j--; + if(i>=0 && j>=0 && i(valR, valI); + return true; + } + else + return false; + } + + template + inline void GetVectorElt (const std::string& line, RealScalar& val) + { + std::istringstream newline(line); + newline >> val; + } + + template + inline void GetVectorElt (const std::string& line, std::complex& val) + { + RealScalar valR, valI; + std::istringstream newline(line); + newline >> valR >> valI; + val = std::complex(valR, valI); + } + + template + inline void putMarketHeader(std::string& header,int sym) + { + header= "%%MatrixMarket matrix coordinate "; + if(internal::is_same >::value || internal::is_same >::value) + { + header += " complex"; + if(sym == Symmetric) header += " symmetric"; + else if (sym == SelfAdjoint) header += " Hermitian"; + else header += " general"; + } + else + { + header += " real"; + if(sym == Symmetric) header += " symmetric"; + else header += " general"; + } + } + + template + inline void PutMatrixElt(Scalar value, int row, int col, std::ofstream& out) + { + out << row << " "<< col << " " << value << "\n"; + } + template + inline void PutMatrixElt(std::complex value, int row, int col, std::ofstream& out) + { + out << row << " " << col << " " << value.real() << " " << value.imag() << "\n"; + } + + + template + inline void putVectorElt(Scalar value, std::ofstream& out) + { + out << value << "\n"; + } + template + inline void putVectorElt(std::complex value, std::ofstream& out) + { + out << value.real << " " << value.imag()<< "\n"; + } + +} // end namepsace internal + +inline bool getMarketHeader(const std::string& filename, int& sym, bool& iscomplex, bool& isvector) +{ + sym = 0; + iscomplex = false; + isvector = false; + std::ifstream in(filename.c_str(),std::ios::in); + if(!in) + return false; + + std::string line; + // The matrix header is always the first line in the file + std::getline(in, line); eigen_assert(in.good()); + + std::stringstream fmtline(line); + std::string substr[5]; + fmtline>> substr[0] >> substr[1] >> substr[2] >> substr[3] >> substr[4]; + if(substr[2].compare("array") == 0) isvector = true; + if(substr[3].compare("complex") == 0) iscomplex = true; + if(substr[4].compare("symmetric") == 0) sym = Symmetric; + else if (substr[4].compare("Hermitian") == 0) sym = SelfAdjoint; + + return true; +} + +template +bool loadMarket(SparseMatrixType& mat, const std::string& filename) +{ + typedef typename SparseMatrixType::Scalar Scalar; + typedef typename SparseMatrixType::Index Index; + std::ifstream input(filename.c_str(),std::ios::in); + if(!input) + return false; + + const int maxBuffersize = 2048; + char buffer[maxBuffersize]; + + bool readsizes = false; + + typedef Triplet T; + std::vector elements; + + Index M(-1), N(-1), NNZ(-1); + Index count = 0; + while(input.getline(buffer, maxBuffersize)) + { + // skip comments + //NOTE An appropriate test should be done on the header to get the symmetry + if(buffer[0]=='%') + continue; + + std::stringstream line(buffer); + + if(!readsizes) + { + line >> M >> N >> NNZ; + if(M > 0 && N > 0 && NNZ > 0) + { + readsizes = true; + //std::cout << "sizes: " << M << "," << N << "," << NNZ << "\n"; + mat.resize(M,N); + mat.reserve(NNZ); + } + } + else + { + Index i(-1), j(-1); + Scalar value; + if( internal::GetMarketLine(line, M, N, i, j, value) ) + { + ++ count; + elements.push_back(T(i,j,value)); + } + else + std::cerr << "Invalid read: " << i << "," << j << "\n"; + } + } + mat.setFromTriplets(elements.begin(), elements.end()); + if(count!=NNZ) + std::cerr << count << "!=" << NNZ << "\n"; + + input.close(); + return true; +} + +template +bool loadMarketVector(VectorType& vec, const std::string& filename) +{ + typedef typename VectorType::Scalar Scalar; + std::ifstream in(filename.c_str(), std::ios::in); + if(!in) + return false; + + std::string line; + int n(0), col(0); + do + { // Skip comments + std::getline(in, line); eigen_assert(in.good()); + } while (line[0] == '%'); + std::istringstream newline(line); + newline >> n >> col; + eigen_assert(n>0 && col>0); + vec.resize(n); + int i = 0; + Scalar value; + while ( std::getline(in, line) && (i < n) ){ + internal::GetVectorElt(line, value); + vec(i++) = value; + } + in.close(); + if (i!=n){ + std::cerr<< "Unable to read all elements from file " << filename << "\n"; + return false; + } + return true; +} + +template +bool saveMarket(const SparseMatrixType& mat, const std::string& filename, int sym = 0) +{ + typedef typename SparseMatrixType::Scalar Scalar; + std::ofstream out(filename.c_str(),std::ios::out); + if(!out) + return false; + + out.flags(std::ios_base::scientific); + out.precision(64); + std::string header; + internal::putMarketHeader(header, sym); + out << header << std::endl; + out << mat.rows() << " " << mat.cols() << " " << mat.nonZeros() << "\n"; + int count = 0; + for(int j=0; j +bool saveMarketVector (const VectorType& vec, const std::string& filename) +{ + typedef typename VectorType::Scalar Scalar; + std::ofstream out(filename.c_str(),std::ios::out); + if(!out) + return false; + + out.flags(std::ios_base::scientific); + out.precision(64); + if(internal::is_same >::value || internal::is_same >::value) + out << "%%MatrixMarket matrix array complex general\n"; + else + out << "%%MatrixMarket matrix array real general\n"; + out << vec.size() << " "<< 1 << "\n"; + for (int i=0; i < vec.size(); i++){ + internal::putVectorElt(vec(i), out); + } + out.close(); + return true; +} + +} // end namespace Eigen + +#endif // EIGEN_SPARSE_MARKET_IO_H diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/MatrixMarketIterator.h b/src/eigen/unsupported/Eigen/src/SparseExtra/MatrixMarketIterator.h new file mode 100644 index 000000000..02916ea6f --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/MatrixMarketIterator.h @@ -0,0 +1,247 @@ + +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2012 Desire NUENTSA WAKAM +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_BROWSE_MATRICES_H +#define EIGEN_BROWSE_MATRICES_H + +namespace Eigen { + +enum { + SPD = 0x100, + NonSymmetric = 0x0 +}; + +/** + * @brief Iterator to browse matrices from a specified folder + * + * This is used to load all the matrices from a folder. + * The matrices should be in Matrix Market format + * It is assumed that the matrices are named as matname.mtx + * and matname_SPD.mtx if the matrix is Symmetric and positive definite (or Hermitian) + * The right hand side vectors are loaded as well, if they exist. + * They should be named as matname_b.mtx. + * Note that the right hand side for a SPD matrix is named as matname_SPD_b.mtx + * + * Sometimes a reference solution is available. In this case, it should be named as matname_x.mtx + * + * Sample code + * \code + * + * \endcode + * + * \tparam Scalar The scalar type + */ +template +class MatrixMarketIterator +{ + typedef typename NumTraits::Real RealScalar; + public: + typedef Matrix VectorType; + typedef SparseMatrix MatrixType; + + public: + MatrixMarketIterator(const std::string &folder) + : m_sym(0), m_isvalid(false), m_matIsLoaded(false), m_hasRhs(false), m_hasrefX(false), m_folder(folder) + { + m_folder_id = opendir(folder.c_str()); + if(m_folder_id) + Getnextvalidmatrix(); + } + + ~MatrixMarketIterator() + { + if (m_folder_id) closedir(m_folder_id); + } + + inline MatrixMarketIterator& operator++() + { + m_matIsLoaded = false; + m_hasrefX = false; + m_hasRhs = false; + Getnextvalidmatrix(); + return *this; + } + inline operator bool() const { return m_isvalid;} + + /** Return the sparse matrix corresponding to the current file */ + inline MatrixType& matrix() + { + // Read the matrix + if (m_matIsLoaded) return m_mat; + + std::string matrix_file = m_folder + "/" + m_matname + ".mtx"; + if ( !loadMarket(m_mat, matrix_file)) + { + std::cerr << "Warning loadMarket failed when loading \"" << matrix_file << "\"" << std::endl; + m_matIsLoaded = false; + return m_mat; + } + m_matIsLoaded = true; + + if (m_sym != NonSymmetric) + { + // Check whether we need to restore a full matrix: + RealScalar diag_norm = m_mat.diagonal().norm(); + RealScalar lower_norm = m_mat.template triangularView().norm(); + RealScalar upper_norm = m_mat.template triangularView().norm(); + if(lower_norm>diag_norm && upper_norm==diag_norm) + { + // only the lower part is stored + MatrixType tmp(m_mat); + m_mat = tmp.template selfadjointView(); + } + else if(upper_norm>diag_norm && lower_norm==diag_norm) + { + // only the upper part is stored + MatrixType tmp(m_mat); + m_mat = tmp.template selfadjointView(); + } + } + return m_mat; + } + + /** Return the right hand side corresponding to the current matrix. + * If the rhs file is not provided, a random rhs is generated + */ + inline VectorType& rhs() + { + // Get the right hand side + if (m_hasRhs) return m_rhs; + + std::string rhs_file; + rhs_file = m_folder + "/" + m_matname + "_b.mtx"; // The pattern is matname_b.mtx + m_hasRhs = Fileexists(rhs_file); + if (m_hasRhs) + { + m_rhs.resize(m_mat.cols()); + m_hasRhs = loadMarketVector(m_rhs, rhs_file); + } + if (!m_hasRhs) + { + // Generate a random right hand side + if (!m_matIsLoaded) this->matrix(); + m_refX.resize(m_mat.cols()); + m_refX.setRandom(); + m_rhs = m_mat * m_refX; + m_hasrefX = true; + m_hasRhs = true; + } + return m_rhs; + } + + /** Return a reference solution + * If it is not provided and if the right hand side is not available + * then refX is randomly generated such that A*refX = b + * where A and b are the matrix and the rhs. + * Note that when a rhs is provided, refX is not available + */ + inline VectorType& refX() + { + // Check if a reference solution is provided + if (m_hasrefX) return m_refX; + + std::string lhs_file; + lhs_file = m_folder + "/" + m_matname + "_x.mtx"; + m_hasrefX = Fileexists(lhs_file); + if (m_hasrefX) + { + m_refX.resize(m_mat.cols()); + m_hasrefX = loadMarketVector(m_refX, lhs_file); + } + else + m_refX.resize(0); + return m_refX; + } + + inline std::string& matname() { return m_matname; } + + inline int sym() { return m_sym; } + + bool hasRhs() {return m_hasRhs; } + bool hasrefX() {return m_hasrefX; } + bool isFolderValid() { return bool(m_folder_id); } + + protected: + + inline bool Fileexists(std::string file) + { + std::ifstream file_id(file.c_str()); + if (!file_id.good() ) + { + return false; + } + else + { + file_id.close(); + return true; + } + } + + void Getnextvalidmatrix( ) + { + m_isvalid = false; + // Here, we return with the next valid matrix in the folder + while ( (m_curs_id = readdir(m_folder_id)) != NULL) { + m_isvalid = false; + std::string curfile; + curfile = m_folder + "/" + m_curs_id->d_name; + // Discard if it is a folder + if (m_curs_id->d_type == DT_DIR) continue; //FIXME This may not be available on non BSD systems +// struct stat st_buf; +// stat (curfile.c_str(), &st_buf); +// if (S_ISDIR(st_buf.st_mode)) continue; + + // Determine from the header if it is a matrix or a right hand side + bool isvector,iscomplex=false; + if(!getMarketHeader(curfile,m_sym,iscomplex,isvector)) continue; + if(isvector) continue; + if (!iscomplex) + { + if(internal::is_same >::value || internal::is_same >::value) + continue; + } + if (iscomplex) + { + if(internal::is_same::value || internal::is_same::value) + continue; + } + + + // Get the matrix name + std::string filename = m_curs_id->d_name; + m_matname = filename.substr(0, filename.length()-4); + + // Find if the matrix is SPD + size_t found = m_matname.find("SPD"); + if( (found!=std::string::npos) && (m_sym != NonSymmetric) ) + m_sym = SPD; + + m_isvalid = true; + break; + } + } + int m_sym; // Symmetry of the matrix + MatrixType m_mat; // Current matrix + VectorType m_rhs; // Current vector + VectorType m_refX; // The reference solution, if exists + std::string m_matname; // Matrix Name + bool m_isvalid; + bool m_matIsLoaded; // Determine if the matrix has already been loaded from the file + bool m_hasRhs; // The right hand side exists + bool m_hasrefX; // A reference solution is provided + std::string m_folder; + DIR * m_folder_id; + struct dirent *m_curs_id; + +}; + +} // end namespace Eigen + +#endif diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/RandomSetter.h b/src/eigen/unsupported/Eigen/src/SparseExtra/RandomSetter.h new file mode 100644 index 000000000..ee97299af --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/RandomSetter.h @@ -0,0 +1,327 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_RANDOMSETTER_H +#define EIGEN_RANDOMSETTER_H + +namespace Eigen { + +/** Represents a std::map + * + * \see RandomSetter + */ +template struct StdMapTraits +{ + typedef int KeyType; + typedef std::map Type; + enum { + IsSorted = 1 + }; + + static void setInvalidKey(Type&, const KeyType&) {} +}; + +#ifdef EIGEN_UNORDERED_MAP_SUPPORT +/** Represents a std::unordered_map + * + * To use it you need to both define EIGEN_UNORDERED_MAP_SUPPORT and include the unordered_map header file + * yourself making sure that unordered_map is defined in the std namespace. + * + * For instance, with current version of gcc you can either enable C++0x standard (-std=c++0x) or do: + * \code + * #include + * #define EIGEN_UNORDERED_MAP_SUPPORT + * namespace std { + * using std::tr1::unordered_map; + * } + * \endcode + * + * \see RandomSetter + */ +template struct StdUnorderedMapTraits +{ + typedef int KeyType; + typedef std::unordered_map Type; + enum { + IsSorted = 0 + }; + + static void setInvalidKey(Type&, const KeyType&) {} +}; +#endif // EIGEN_UNORDERED_MAP_SUPPORT + +#ifdef _DENSE_HASH_MAP_H_ +/** Represents a google::dense_hash_map + * + * \see RandomSetter + */ +template struct GoogleDenseHashMapTraits +{ + typedef int KeyType; + typedef google::dense_hash_map Type; + enum { + IsSorted = 0 + }; + + static void setInvalidKey(Type& map, const KeyType& k) + { map.set_empty_key(k); } +}; +#endif + +#ifdef _SPARSE_HASH_MAP_H_ +/** Represents a google::sparse_hash_map + * + * \see RandomSetter + */ +template struct GoogleSparseHashMapTraits +{ + typedef int KeyType; + typedef google::sparse_hash_map Type; + enum { + IsSorted = 0 + }; + + static void setInvalidKey(Type&, const KeyType&) {} +}; +#endif + +/** \class RandomSetter + * + * \brief The RandomSetter is a wrapper object allowing to set/update a sparse matrix with random access + * + * \tparam SparseMatrixType the type of the sparse matrix we are updating + * \tparam MapTraits a traits class representing the map implementation used for the temporary sparse storage. + * Its default value depends on the system. + * \tparam OuterPacketBits defines the number of rows (or columns) manage by a single map object + * as a power of two exponent. + * + * This class temporarily represents a sparse matrix object using a generic map implementation allowing for + * efficient random access. The conversion from the compressed representation to a hash_map object is performed + * in the RandomSetter constructor, while the sparse matrix is updated back at destruction time. This strategy + * suggest the use of nested blocks as in this example: + * + * \code + * SparseMatrix m(rows,cols); + * { + * RandomSetter > w(m); + * // don't use m but w instead with read/write random access to the coefficients: + * for(;;) + * w(rand(),rand()) = rand; + * } + * // when w is deleted, the data are copied back to m + * // and m is ready to use. + * \endcode + * + * Since hash_map objects are not fully sorted, representing a full matrix as a single hash_map would + * involve a big and costly sort to update the compressed matrix back. To overcome this issue, a RandomSetter + * use multiple hash_map, each representing 2^OuterPacketBits columns or rows according to the storage order. + * To reach optimal performance, this value should be adjusted according to the average number of nonzeros + * per rows/columns. + * + * The possible values for the template parameter MapTraits are: + * - \b StdMapTraits: corresponds to std::map. (does not perform very well) + * - \b GnuHashMapTraits: corresponds to __gnu_cxx::hash_map (available only with GCC) + * - \b GoogleDenseHashMapTraits: corresponds to google::dense_hash_map (best efficiency, reasonable memory consumption) + * - \b GoogleSparseHashMapTraits: corresponds to google::sparse_hash_map (best memory consumption, relatively good performance) + * + * The default map implementation depends on the availability, and the preferred order is: + * GoogleSparseHashMapTraits, GnuHashMapTraits, and finally StdMapTraits. + * + * For performance and memory consumption reasons it is highly recommended to use one of + * the Google's hash_map implementation. To enable the support for them, you have two options: + * - \#include yourself \b before Eigen/Sparse header + * - define EIGEN_GOOGLEHASH_SUPPORT + * In the later case the inclusion of is made for you. + * + * \see http://code.google.com/p/google-sparsehash/ + */ +template class MapTraits = +#if defined _DENSE_HASH_MAP_H_ + GoogleDenseHashMapTraits +#elif defined _HASH_MAP + GnuHashMapTraits +#else + StdMapTraits +#endif + ,int OuterPacketBits = 6> +class RandomSetter +{ + typedef typename SparseMatrixType::Scalar Scalar; + typedef typename SparseMatrixType::StorageIndex StorageIndex; + + struct ScalarWrapper + { + ScalarWrapper() : value(0) {} + Scalar value; + }; + typedef typename MapTraits::KeyType KeyType; + typedef typename MapTraits::Type HashMapType; + static const int OuterPacketMask = (1 << OuterPacketBits) - 1; + enum { + SwapStorage = 1 - MapTraits::IsSorted, + TargetRowMajor = (SparseMatrixType::Flags & RowMajorBit) ? 1 : 0, + SetterRowMajor = SwapStorage ? 1-TargetRowMajor : TargetRowMajor + }; + + public: + + /** Constructs a random setter object from the sparse matrix \a target + * + * Note that the initial value of \a target are imported. If you want to re-set + * a sparse matrix from scratch, then you must set it to zero first using the + * setZero() function. + */ + inline RandomSetter(SparseMatrixType& target) + : mp_target(&target) + { + const Index outerSize = SwapStorage ? target.innerSize() : target.outerSize(); + const Index innerSize = SwapStorage ? target.outerSize() : target.innerSize(); + m_outerPackets = outerSize >> OuterPacketBits; + if (outerSize&OuterPacketMask) + m_outerPackets += 1; + m_hashmaps = new HashMapType[m_outerPackets]; + // compute number of bits needed to store inner indices + Index aux = innerSize - 1; + m_keyBitsOffset = 0; + while (aux) + { + ++m_keyBitsOffset; + aux = aux >> 1; + } + KeyType ik = (1<<(OuterPacketBits+m_keyBitsOffset)); + for (Index k=0; k::setInvalidKey(m_hashmaps[k],ik); + + // insert current coeffs + for (Index j=0; jouterSize(); ++j) + for (typename SparseMatrixType::InnerIterator it(*mp_target,j); it; ++it) + (*this)(TargetRowMajor?j:it.index(), TargetRowMajor?it.index():j) = it.value(); + } + + /** Destructor updating back the sparse matrix target */ + ~RandomSetter() + { + KeyType keyBitsMask = (1<setZero(); + mp_target->makeCompressed(); + mp_target->reserve(nonZeros()); + Index prevOuter = -1; + for (Index k=0; kfirst >> m_keyBitsOffset) + outerOffset; + const Index inner = it->first & keyBitsMask; + if (prevOuter!=outer) + { + for (Index j=prevOuter+1;j<=outer;++j) + mp_target->startVec(j); + prevOuter = outer; + } + mp_target->insertBackByOuterInner(outer, inner) = it->second.value; + } + } + mp_target->finalize(); + } + else + { + VectorXi positions(mp_target->outerSize()); + positions.setZero(); + // pass 1 + for (Index k=0; kfirst & keyBitsMask; + ++positions[outer]; + } + } + // prefix sum + Index count = 0; + for (Index j=0; jouterSize(); ++j) + { + Index tmp = positions[j]; + mp_target->outerIndexPtr()[j] = count; + positions[j] = count; + count += tmp; + } + mp_target->makeCompressed(); + mp_target->outerIndexPtr()[mp_target->outerSize()] = count; + mp_target->resizeNonZeros(count); + // pass 2 + for (Index k=0; kfirst >> m_keyBitsOffset) + outerOffset; + const Index outer = it->first & keyBitsMask; + // sorted insertion + // Note that we have to deal with at most 2^OuterPacketBits unsorted coefficients, + // moreover those 2^OuterPacketBits coeffs are likely to be sparse, an so only a + // small fraction of them have to be sorted, whence the following simple procedure: + Index posStart = mp_target->outerIndexPtr()[outer]; + Index i = (positions[outer]++) - 1; + while ( (i >= posStart) && (mp_target->innerIndexPtr()[i] > inner) ) + { + mp_target->valuePtr()[i+1] = mp_target->valuePtr()[i]; + mp_target->innerIndexPtr()[i+1] = mp_target->innerIndexPtr()[i]; + --i; + } + mp_target->innerIndexPtr()[i+1] = inner; + mp_target->valuePtr()[i+1] = it->second.value; + } + } + } + delete[] m_hashmaps; + } + + /** \returns a reference to the coefficient at given coordinates \a row, \a col */ + Scalar& operator() (Index row, Index col) + { + const Index outer = SetterRowMajor ? row : col; + const Index inner = SetterRowMajor ? col : row; + const Index outerMajor = outer >> OuterPacketBits; // index of the packet/map + const Index outerMinor = outer & OuterPacketMask; // index of the inner vector in the packet + const KeyType key = internal::convert_index((outerMinor<(m_hashmaps[k].size()); + return nz; + } + + + protected: + + HashMapType* m_hashmaps; + SparseMatrixType* mp_target; + Index m_outerPackets; + unsigned char m_keyBitsOffset; +}; + +} // end namespace Eigen + +#endif // EIGEN_RANDOMSETTER_H diff --git a/src/igl/EPS.h b/src/igl/EPS.h index 17f3b8c25..d65007a64 100644 --- a/src/igl/EPS.h +++ b/src/igl/EPS.h @@ -13,8 +13,8 @@ namespace igl // Define a standard value for double epsilon const double DOUBLE_EPS = 1.0e-14; const double DOUBLE_EPS_SQ = 1.0e-28; - const float FLOAT_EPS = 1.0e-7; - const float FLOAT_EPS_SQ = 1.0e-14; + const float FLOAT_EPS = 1.0e-7f; + const float FLOAT_EPS_SQ = 1.0e-14f; // Function returning EPS for corresponding type template IGL_INLINE S_type EPS(); template IGL_INLINE S_type EPS_SQ(); diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index 14ed3d22c..28659c512 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -545,8 +545,8 @@ public: _NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default; #ifndef BP2D_COMPILER_MSVC12 // MSVC2013 does not support default move ctors - _NofitPolyPlacer(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default; - _NofitPolyPlacer& operator=(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default; + _NofitPolyPlacer(_NofitPolyPlacer&&) /*BP2D_NOEXCEPT*/ = default; + _NofitPolyPlacer& operator=(_NofitPolyPlacer&&) /*BP2D_NOEXCEPT*/ = default; #endif static inline double overfit(const Box& bb, const RawShape& bin) { diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index adbfb9de2..5324b19a4 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -198,6 +198,10 @@ target_link_libraries(libslic3r tbb ) +if(WIN32) + target_link_libraries(libslic3r Psapi.lib) +endif() + if(SLIC3R_PROFILE) target_link_libraries(slic3r Shiny) endif() diff --git a/src/libslic3r/Channel.hpp b/src/libslic3r/Channel.hpp index 8d1a07d35..68369af63 100644 --- a/src/libslic3r/Channel.hpp +++ b/src/libslic3r/Channel.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_Channel_hpp_ #define slic3r_Channel_hpp_ +#include #include #include #include @@ -13,32 +14,26 @@ namespace Slic3r { template class Channel { -private: - using UniqueLock = std::unique_lock; - using Queue = std::deque; public: - class Guard + using UniqueLock = std::unique_lock; + + template class Unlocker { public: - Guard(UniqueLock lock, const Queue &queue) : m_lock(std::move(lock)), m_queue(queue) {} - Guard(const Guard &other) = delete; - Guard(Guard &&other) = delete; - ~Guard() {} + Unlocker(UniqueLock lock) : m_lock(std::move(lock)) {} + Unlocker(const Unlocker &other) noexcept : m_lock(std::move(other.m_lock)) {} // XXX: done beacuse of MSVC 2013 not supporting init of deleter by move + Unlocker(Unlocker &&other) noexcept : m_lock(std::move(other.m_lock)) {} + Unlocker& operator=(const Unlocker &other) = delete; + Unlocker& operator=(Unlocker &&other) { m_lock = std::move(other.m_lock); } - // Access trampolines - size_t size() const noexcept { return m_queue.size(); } - bool empty() const noexcept { return m_queue.empty(); } - typename Queue::const_iterator begin() const noexcept { return m_queue.begin(); } - typename Queue::const_iterator end() const noexcept { return m_queue.end(); } - typename Queue::const_reference operator[](size_t i) const { return m_queue[i]; } - - Guard& operator=(const Guard &other) = delete; - Guard& operator=(Guard &&other) = delete; + void operator()(Ptr*) { m_lock.unlock(); } private: - UniqueLock m_lock; - const Queue &m_queue; + mutable UniqueLock m_lock; // XXX: mutable: see above }; + using Queue = std::deque; + using LockedConstPtr = std::unique_ptr>; + using LockedPtr = std::unique_ptr>; Channel() {} ~Channel() {} @@ -56,7 +51,7 @@ public: { { UniqueLock lock(m_mutex); - m_queue.push_back(std::forward(item)); + m_queue.push_back(std::forward(item)); } if (! silent) { m_condition.notify_one(); } } @@ -82,19 +77,21 @@ public: } } - // Unlocked observers - // Thread unsafe! Keep in mind you need to re-verify the result after acquiring lock! - size_t size() const noexcept { return m_queue.size(); } - bool empty() const noexcept { return m_queue.empty(); } + // Unlocked observer/hint. Thread unsafe! Keep in mind you need to re-verify the result after locking. + size_t size_hint() const noexcept { return m_queue.size(); } - Guard read() const + LockedConstPtr lock_read() const { - return Guard(UniqueLock(m_mutex), m_queue); + return LockedConstPtr(&m_queue, Unlocker(UniqueLock(m_mutex))); } + LockedPtr lock_rw() + { + return LockedPtr(&m_queue, Unlocker(UniqueLock(m_mutex))); + } private: Queue m_queue; - std::mutex m_mutex; + mutable std::mutex m_mutex; std::condition_variable m_condition; }; diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 88e0c7664..2f61cc008 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -483,8 +483,9 @@ bool DynamicConfig::operator==(const DynamicConfig &rhs) const t_options_map::const_iterator it2 = rhs.options.begin(); t_options_map::const_iterator it2_end = rhs.options.end(); for (; it1 != it1_end && it2 != it2_end; ++ it1, ++ it2) - if (*it1->second != *it2->second) - return false; + if (it1->first != it2->first || *it1->second != *it2->second) + // key or value differ + return false; return it1 == it1_end && it2 == it2_end; } diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index f5750162f..3b99ccd82 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1336,49 +1336,12 @@ namespace Slic3r { void _3MF_Importer::_apply_transform(ModelInstance& instance, const Transform3d& transform) { -#if ENABLE_MODELVOLUME_TRANSFORM Slic3r::Geometry::Transformation t(transform); // invalid scale value, return if (!t.get_scaling_factor().all()) return; instance.set_transformation(t); -#else - // translation - Vec3d offset = transform.matrix().block(0, 3, 3, 1); - - Eigen::Matrix m3x3 = transform.matrix().block(0, 0, 3, 3); - // mirror - // it is impossible to reconstruct the original mirroring factors from a matrix, - // we can only detect if the matrix contains a left handed reference system - // in which case we reorient it back to right handed by mirroring the x axis - Vec3d mirror = Vec3d::Ones(); - if (m3x3.col(0).dot(m3x3.col(1).cross(m3x3.col(2))) < 0.0) - { - mirror(0) = -1.0; - // remove mirror - m3x3.col(0) *= -1.0; - } - - // scale - Vec3d scale(m3x3.col(0).norm(), m3x3.col(1).norm(), m3x3.col(2).norm()); - - // invalid scale value, return - if ((scale(0) == 0.0) || (scale(1) == 0.0) || (scale(2) == 0.0)) - return; - - // remove scale - m3x3.col(0).normalize(); - m3x3.col(1).normalize(); - m3x3.col(2).normalize(); - - Vec3d rotation = Slic3r::Geometry::extract_euler_angles(m3x3); - - instance.set_offset(offset); - instance.set_scaling_factor(scale); - instance.set_rotation(rotation); - instance.set_mirror(mirror); -#endif // ENABLE_MODELVOLUME_TRANSFORM } bool _3MF_Importer::_handle_start_config(const char** attributes, unsigned int num_attributes) @@ -1875,23 +1838,15 @@ namespace Slic3r { vertices_count += stl.stats.shared_vertices; -#if ENABLE_MODELVOLUME_TRANSFORM const Transform3d& matrix = volume->get_matrix(); -#endif // ENABLE_MODELVOLUME_TRANSFORM for (int i = 0; i < stl.stats.shared_vertices; ++i) { stream << " <" << VERTEX_TAG << " "; -#if ENABLE_MODELVOLUME_TRANSFORM Vec3d v = matrix * stl.v_shared[i].cast(); stream << "x=\"" << v(0) << "\" "; stream << "y=\"" << v(1) << "\" "; stream << "z=\"" << v(2) << "\" />\n"; -#else - stream << "x=\"" << stl.v_shared[i](0) << "\" "; - stream << "y=\"" << stl.v_shared[i](1) << "\" "; - stream << "z=\"" << stl.v_shared[i](2) << "\" />\n"; -#endif // ENABLE_MODELVOLUME_TRANSFORM } } diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp index 6c0b055d6..80aae75cf 100644 --- a/src/libslic3r/Format/PRUS.cpp +++ b/src/libslic3r/Format/PRUS.cpp @@ -96,7 +96,6 @@ static void extract_model_from_archive( const char *model_xml = strstr(scene_xml_data.data(), model_name_tag); const char *zero_tag = ""; const char *zero_xml = strstr(scene_xml_data.data(), zero_tag); - float trafo[3][4] = { 0 }; Vec3d instance_rotation = Vec3d::Zero(); Vec3d instance_scaling_factor = Vec3d::Ones(); Vec3d instance_offset = Vec3d::Zero(); @@ -124,19 +123,7 @@ static void extract_model_from_archive( "[%f, %f, %f]", zero, zero+1, zero+2) == 3) { instance_scaling_factor = Vec3d((double)scale[0], (double)scale[1], (double)scale[2]); instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]); - Eigen::Matrix3f mat_rot, mat_scale, mat_trafo; - mat_rot = Eigen::AngleAxisf(-rotation[2], Eigen::Vector3f::UnitZ()) * - Eigen::AngleAxisf(-rotation[1], Eigen::Vector3f::UnitY()) * - Eigen::AngleAxisf(-rotation[0], Eigen::Vector3f::UnitX()); - mat_scale = Eigen::Scaling(scale[0], scale[1], scale[2]); - mat_trafo = mat_rot * mat_scale; - for (size_t r = 0; r < 3; ++ r) { - for (size_t c = 0; c < 3; ++ c) - trafo[r][c] += mat_trafo(r, c); - } instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2])); - // CHECK_ME -> Is the following correct ? - trafo[2][3] = position[2] / (float)instance_scaling_factor(2); trafo_set = true; } const char *group_tag = ""; @@ -189,8 +176,6 @@ static void extract_model_from_archive( // All the faces have been read. stl_get_size(&stl); mesh.repair(); - // Transform the model. - stl_transform(&stl, &trafo[0][0]); if (std::abs(stl.stats.min(2)) < EPSILON) stl.stats.min(2) = 0.; // Add a mesh to a model. @@ -274,8 +259,6 @@ static void extract_model_from_archive( memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50); stl_get_size(&stl); mesh.repair(); - // Transform the model. - stl_transform(&stl, &trafo[0][0]); // Add a mesh to a model. if (mesh.facets_count() > 0) mesh_valid = true; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 9c55fe1e1..13d439740 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -423,7 +423,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ print->set_started(psGCodeExport); - BOOST_LOG_TRIVIAL(info) << "Exporting G-code..."; + BOOST_LOG_TRIVIAL(info) << "Exporting G-code..." << log_memory_info(); // Remove the old g-code if it exists. boost::nowide::remove(path); @@ -435,9 +435,11 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ if (file == nullptr) throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); + m_enable_analyzer = preview_data != nullptr; + try { m_placeholder_parser_failed_templates.clear(); - this->_do_export(*print, file, preview_data); + this->_do_export(*print, file); fflush(file); if (ferror(file)) { fclose(file); @@ -453,15 +455,6 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ } fclose(file); - if (print->config().remaining_times.value) { - BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode"; - m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f); - if (m_silent_time_estimator_enabled) { - BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode"; - m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f); - } - } - if (! m_placeholder_parser_failed_templates.empty()) { // G-code export proceeded, but some of the PlaceholderParser substitutions failed. std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n"; @@ -475,12 +468,30 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ throw std::runtime_error(msg); } + if (print->config().remaining_times.value) { + BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode"; + m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f); + m_normal_time_estimator.reset(); + if (m_silent_time_estimator_enabled) { + BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode"; + m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f); + m_silent_time_estimator.reset(); + } + } + + // starts analyzer calculations + if (m_enable_analyzer) { + BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data"; + m_analyzer.calc_gcode_preview_data(*preview_data); + m_analyzer.reset(); + } + if (rename_file(path_tmp, path) != 0) throw std::runtime_error( std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + "Is " + path_tmp + " locked?" + '\n'); - BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished"; + BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished" << log_memory_info(); print->set_done(psGCodeExport); // Write the profiler measurements to file @@ -488,7 +499,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ PROFILE_OUTPUT(debug_out_path("gcode-export-profile.txt").c_str()); } -void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) +void GCode::_do_export(Print &print, FILE *file) { PROFILE_FUNC(); @@ -558,7 +569,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // resets analyzer m_analyzer.reset(); - m_enable_analyzer = preview_data != nullptr; // resets analyzer's tracking data m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm; @@ -1034,12 +1044,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _write(file, full_config); } print.throw_if_canceled(); - - // starts analyzer calculations - if (preview_data != nullptr) { - BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data"; - m_analyzer.calc_gcode_preview_data(*preview_data); - } } std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) @@ -1231,7 +1235,7 @@ void GCode::process_layer( const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. const std::vector &layers, - const LayerTools &layer_tools, + const LayerTools &layer_tools, // If set to size_t(-1), then print all copies of all objects. // Otherwise print a single copy of a single object. const size_t single_object_idx) @@ -1650,6 +1654,11 @@ void GCode::process_layer( // printf("G-code after filter:\n%s\n", out.c_str()); _write(file, gcode); + BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << + ", time estimator memory: " << + format_memsize_MB(m_normal_time_estimator.memory_used() + m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0) << + ", analyzer memory: " << + format_memsize_MB(m_analyzer.memory_used()); } void GCode::apply_print_config(const PrintConfig &print_config) diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 2dd1e3571..8deb40382 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -180,7 +180,7 @@ public: static void append_full_config(const Print& print, std::string& str); protected: - void _do_export(Print &print, FILE *file, GCodePreviewData *preview_data); + void _do_export(Print &print, FILE *file); // Object and support extrusions of the same PrintObject at the same print_z. struct LayerToPrint diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index c56f02753..c32acd4e9 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -4,6 +4,7 @@ #include "../libslic3r.h" #include "../PrintConfig.hpp" +#include "../Utils.hpp" #include "Print.hpp" #include "Analyzer.hpp" @@ -667,6 +668,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ { static GCodePreviewData::Extrusion::Layer& get_layer_at_z(GCodePreviewData::Extrusion::LayersList& layers, float z) { + //FIXME this has a terrible time complexity for (GCodePreviewData::Extrusion::Layer& layer : layers) { // if layer found, return it @@ -852,20 +854,14 @@ void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_ } } -GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2) +// Return an estimate of the memory consumed by the time estimator. +size_t GCodeAnalyzer::memory_used() const { - return GCodePreviewData::Color(clamp(0.0f, 1.0f, c1.rgba[0] + c2.rgba[0]), - clamp(0.0f, 1.0f, c1.rgba[1] + c2.rgba[1]), - clamp(0.0f, 1.0f, c1.rgba[2] + c2.rgba[2]), - clamp(0.0f, 1.0f, c1.rgba[3] + c2.rgba[3])); -} - -GCodePreviewData::Color operator * (float f, const GCodePreviewData::Color& color) -{ - return GCodePreviewData::Color(clamp(0.0f, 1.0f, f * color.rgba[0]), - clamp(0.0f, 1.0f, f * color.rgba[1]), - clamp(0.0f, 1.0f, f * color.rgba[2]), - clamp(0.0f, 1.0f, f * color.rgba[3])); + size_t out = sizeof(*this); + for (const std::pair &kvp : m_moves_map) + out += sizeof(kvp) + SLIC3R_STDVEC_MEMSIZE(kvp.second, GCodeMove); + out += m_process_output.size(); + return out; } } // namespace Slic3r diff --git a/src/libslic3r/GCode/Analyzer.hpp b/src/libslic3r/GCode/Analyzer.hpp index f50138b56..389c11cec 100644 --- a/src/libslic3r/GCode/Analyzer.hpp +++ b/src/libslic3r/GCode/Analyzer.hpp @@ -120,6 +120,9 @@ public: // Calculates all data needed for gcode visualization void calc_gcode_preview_data(GCodePreviewData& preview_data); + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; + static bool is_valid_extrusion_role(ExtrusionRole role); private: diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp index 50d441e5e..232bbc249 100644 --- a/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -2,6 +2,7 @@ #include "PreviewData.hpp" #include #include +#include "Utils.hpp" #include @@ -205,6 +206,18 @@ bool GCodePreviewData::Extrusion::is_role_flag_set(unsigned int flags, Extrusion return GCodeAnalyzer::is_valid_extrusion_role(role) && (flags & (1 << (role - erPerimeter))) != 0; } +size_t GCodePreviewData::Extrusion::memory_used() const +{ + size_t out = sizeof(*this); + out += SLIC3R_STDVEC_MEMSIZE(this->layers, Layer); + for (const Layer &layer : this->layers) { + out += SLIC3R_STDVEC_MEMSIZE(layer.paths, ExtrusionPath); + for (const ExtrusionPath &path : layer.paths) + out += SLIC3R_STDVEC_MEMSIZE(path.polyline.points, Point); + } + return out; +} + const float GCodePreviewData::Travel::Default_Width = 0.075f; const float GCodePreviewData::Travel::Default_Height = 0.075f; const GCodePreviewData::Color GCodePreviewData::Travel::Default_Type_Colors[Num_Types] = @@ -224,6 +237,15 @@ void GCodePreviewData::Travel::set_default() is_visible = false; } +size_t GCodePreviewData::Travel::memory_used() const +{ + size_t out = sizeof(*this); + out += SLIC3R_STDVEC_MEMSIZE(this->polylines, Polyline); + for (const Polyline &polyline : this->polylines) + out += SLIC3R_STDVEC_MEMSIZE(polyline.polyline.points, Vec3crd); + return out; +} + const GCodePreviewData::Color GCodePreviewData::Retraction::Default_Color = GCodePreviewData::Color(1.0f, 1.0f, 1.0f, 1.0f); GCodePreviewData::Retraction::Position::Position(const Vec3crd& position, float width, float height) @@ -239,6 +261,11 @@ void GCodePreviewData::Retraction::set_default() is_visible = false; } +size_t GCodePreviewData::Retraction::memory_used() const +{ + return sizeof(*this) + SLIC3R_STDVEC_MEMSIZE(this->positions, Position); +} + void GCodePreviewData::Shell::set_default() { is_visible = false; @@ -486,4 +513,31 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: return items; } +// Return an estimate of the memory consumed by the time estimator. +size_t GCodePreviewData::memory_used() const +{ + return + this->extrusion.memory_used() + + this->travel.memory_used() + + this->retraction.memory_used() + + this->unretraction.memory_used() + + sizeof(shell) + sizeof(ranges); +} + +GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2) +{ + return GCodePreviewData::Color(clamp(0.0f, 1.0f, c1.rgba[0] + c2.rgba[0]), + clamp(0.0f, 1.0f, c1.rgba[1] + c2.rgba[1]), + clamp(0.0f, 1.0f, c1.rgba[2] + c2.rgba[2]), + clamp(0.0f, 1.0f, c1.rgba[3] + c2.rgba[3])); +} + +GCodePreviewData::Color operator * (float f, const GCodePreviewData::Color& color) +{ + return GCodePreviewData::Color(clamp(0.0f, 1.0f, f * color.rgba[0]), + clamp(0.0f, 1.0f, f * color.rgba[1]), + clamp(0.0f, 1.0f, f * color.rgba[2]), + clamp(0.0f, 1.0f, f * color.rgba[3])); +} + } // namespace Slic3r diff --git a/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp index c1854505e..6a214890f 100644 --- a/src/libslic3r/GCode/PreviewData.hpp +++ b/src/libslic3r/GCode/PreviewData.hpp @@ -22,6 +22,7 @@ public: static const Color Dummy; }; + // Color mapping from a range into a smooth rainbow of 10 colors. struct Range { static const unsigned int Colors_Count = 10; @@ -45,9 +46,13 @@ public: struct Ranges { + // Color mapping by layer height. Range height; + // Color mapping by extrusion width. Range width; + // Color mapping by feedrate. Range feedrate; + // Color mapping by volumetric extrusion rate. Range volumetric_rate; }; @@ -100,6 +105,9 @@ public: void set_default(); bool is_role_flag_set(ExtrusionRole role) const; + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; + static bool is_role_flag_set(unsigned int flags, ExtrusionRole role); }; @@ -145,6 +153,9 @@ public: size_t color_print_idx; void set_default(); + + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; }; struct Retraction @@ -167,6 +178,9 @@ public: bool is_visible; void set_default(); + + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; }; struct Shell @@ -200,6 +214,9 @@ public: std::string get_legend_title() const; LegendItemsList get_legend_items(const std::vector& tool_colors, const std::vector>& cp_values) const; + + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; }; GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2); diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index 5c8cc2659..461b4cd35 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -290,7 +290,8 @@ namespace Slic3r { // buffer line to export only when greater than 64K to reduce writing calls std::string export_line; char time_line[64]; - while (std::getline(in, gcode_line)) + G1LineIdToBlockIdMap::const_iterator it_line_id = _g1_line_ids.begin(); + while (std::getline(in, gcode_line)) { if (!in.good()) { @@ -310,29 +311,29 @@ namespace Slic3r { // add remaining time lines where needed _parser.parse_line(gcode_line, - [this, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line) + [this, &it_line_id, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line) { if (line.cmd_is("G1")) { ++g1_lines_count; - if (!line.has_e()) - return; + assert(it_line_id == _g1_line_ids.end() || it_line_id->first >= g1_lines_count); - G1LineIdToBlockIdMap::const_iterator it = _g1_line_ids.find(g1_lines_count); - if ((it != _g1_line_ids.end()) && (it->second < (unsigned int)_blocks.size())) - { - const Block& block = _blocks[it->second]; - if (block.elapsed_time != -1.0f) + const Block *block = nullptr; + if (it_line_id != _g1_line_ids.end() && it_line_id->first == g1_lines_count) { + if (line.has_e() && it_line_id->second < (unsigned int)_blocks.size()) + block = &_blocks[it_line_id->second]; + ++it_line_id; + } + + if (block != nullptr && block->elapsed_time != -1.0f) { + float block_remaining_time = _time - block->elapsed_time; + if (std::abs(last_recorded_time - block_remaining_time) > interval) { - float block_remaining_time = _time - block.elapsed_time; - if (std::abs(last_recorded_time - block_remaining_time) > interval) - { - sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block.elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str()); - gcode_line += time_line; + sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str()); + gcode_line += time_line; - last_recorded_time = block_remaining_time; - } + last_recorded_time = block_remaining_time; } } } @@ -667,6 +668,15 @@ namespace Slic3r { return _get_time_minutes(get_time()); } + // Return an estimate of the memory consumed by the time estimator. + size_t GCodeTimeEstimator::memory_used() const + { + size_t out = sizeof(*this); + out += SLIC3R_STDVEC_MEMSIZE(this->_blocks, Block); + out += SLIC3R_STDVEC_MEMSIZE(this->_g1_line_ids, G1LineIdToBlockId); + return out; + } + void GCodeTimeEstimator::_reset() { _curr.reset(); @@ -1072,7 +1082,7 @@ namespace Slic3r { // adds block to blocks list _blocks.emplace_back(block); - _g1_line_ids.insert(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1)); + _g1_line_ids.emplace_back(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1)); } void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line) diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp index e9da584c3..ef91d5ff1 100644 --- a/src/libslic3r/GCodeTimeEstimator.hpp +++ b/src/libslic3r/GCodeTimeEstimator.hpp @@ -209,7 +209,8 @@ namespace Slic3r { typedef std::map MovesStatsMap; #endif // ENABLE_MOVE_STATS - typedef std::map G1LineIdToBlockIdMap; + typedef std::pair G1LineIdToBlockId; + typedef std::vector G1LineIdToBlockIdMap; private: EMode _mode; @@ -338,6 +339,9 @@ namespace Slic3r { // Returns the estimated time, in minutes (integer) std::string get_time_minutes() const; + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; + private: void _reset(); void _reset_time(); diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index c47c5b38b..55ce3fa2c 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -946,7 +946,6 @@ Vec3d extract_euler_angles(const Transform3d& transform) return extract_euler_angles(m); } -#if ENABLE_MODELVOLUME_TRANSFORM Transformation::Flags::Flags() : dont_translate(true) , dont_rotate(true) @@ -1116,6 +1115,5 @@ Transformation Transformation::operator * (const Transformation& other) const { return Transformation(get_matrix() * other.get_matrix()); } -#endif // ENABLE_MODELVOLUME_TRANSFORM } } diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index eba6b093f..039e3b5ee 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -167,7 +167,6 @@ Vec3d extract_euler_angles(const Eigen::Matrix& // Warning -> The transform should not contain any shear !!! Vec3d extract_euler_angles(const Transform3d& transform); -#if ENABLE_MODELVOLUME_TRANSFORM class Transformation { struct Flags @@ -226,7 +225,6 @@ public: Transformation operator * (const Transformation& other) const; }; -#endif // ENABLE_MODELVOLUME_TRANSFORM } } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 07f1b98c1..20c4a44b2 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -757,16 +757,11 @@ const BoundingBoxf3& ModelObject::bounding_box() const BoundingBoxf3 raw_bbox; for (const ModelVolume *v : this->volumes) if (v->is_model_part()) -#if ENABLE_MODELVOLUME_TRANSFORM { TriangleMesh m = v->mesh; m.transform(v->get_matrix()); raw_bbox.merge(m.bounding_box()); } -#else - // mesh.bounding_box() returns a cached value. - raw_bbox.merge(v->mesh.bounding_box()); -#endif // ENABLE_MODELVOLUME_TRANSFORM BoundingBoxf3 bb; for (const ModelInstance *i : this->instances) bb.merge(i->transform_bounding_box(raw_bbox)); @@ -797,15 +792,24 @@ TriangleMesh ModelObject::raw_mesh() const TriangleMesh mesh; for (const ModelVolume *v : this->volumes) if (v->is_model_part()) -#if ENABLE_MODELVOLUME_TRANSFORM { TriangleMesh vol_mesh(v->mesh); vol_mesh.transform(v->get_matrix()); mesh.merge(vol_mesh); } -#else - mesh.merge(v->mesh); -#endif // ENABLE_MODELVOLUME_TRANSFORM + return mesh; +} + +// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes. +TriangleMesh ModelObject::full_raw_mesh() const +{ + TriangleMesh mesh; + for (const ModelVolume *v : this->volumes) + { + TriangleMesh vol_mesh(v->mesh); + vol_mesh.transform(v->get_matrix()); + mesh.merge(vol_mesh); + } return mesh; } @@ -819,13 +823,9 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const if (this->instances.empty()) throw std::invalid_argument("Can't call raw_bounding_box() with no instances"); -#if ENABLE_MODELVOLUME_TRANSFORM TriangleMesh vol_mesh(v->mesh); vol_mesh.transform(v->get_matrix()); bb.merge(this->instances.front()->transform_mesh_bounding_box(vol_mesh, true)); -#else - bb.merge(this->instances.front()->transform_mesh_bounding_box(v->mesh, true)); -#endif // ENABLE_MODELVOLUME_TRANSFORM } return bb; } @@ -834,7 +834,6 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_translate) const { BoundingBoxf3 bb; -#if ENABLE_MODELVOLUME_TRANSFORM for (ModelVolume *v : this->volumes) { if (v->is_model_part()) @@ -844,11 +843,6 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_ bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(mesh, dont_translate)); } } -#else - for (ModelVolume *v : this->volumes) - if (v->is_model_part()) - bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&v->mesh, dont_translate)); -#endif // ENABLE_MODELVOLUME_TRANSFORM return bb; } @@ -866,15 +860,6 @@ void ModelObject::center_around_origin() this->translate(shift); this->origin_translation += shift; - -#if !ENABLE_MODELVOLUME_TRANSFORM - if (!this->instances.empty()) { - for (ModelInstance *i : this->instances) { - i->set_offset(i->get_offset() - shift); - } - this->invalidate_bounding_box(); - } -#endif // !ENABLE_MODELVOLUME_TRANSFORM } void ModelObject::ensure_on_bed() @@ -914,10 +899,6 @@ void ModelObject::scale(const Vec3d &versor) { v->scale(versor); } -#if !ENABLE_MODELVOLUME_TRANSFORM - // reset origin translation since it doesn't make sense anymore - this->origin_translation = Vec3d::Zero(); -#endif // !ENABLE_MODELVOLUME_TRANSFORM this->invalidate_bounding_box(); } @@ -929,10 +910,6 @@ void ModelObject::rotate(double angle, Axis axis) } center_around_origin(); - -#if !ENABLE_MODELVOLUME_TRANSFORM - this->origin_translation = Vec3d::Zero(); -#endif // !ENABLE_MODELVOLUME_TRANSFORM this->invalidate_bounding_box(); } @@ -944,10 +921,6 @@ void ModelObject::rotate(double angle, const Vec3d& axis) } center_around_origin(); - -#if !ENABLE_MODELVOLUME_TRANSFORM - this->origin_translation = Vec3d::Zero(); -#endif // !ENABLE_MODELVOLUME_TRANSFORM this->invalidate_bounding_box(); } @@ -958,9 +931,16 @@ void ModelObject::mirror(Axis axis) v->mirror(axis); } -#if !ENABLE_MODELVOLUME_TRANSFORM - this->origin_translation = Vec3d::Zero(); -#endif // !ENABLE_MODELVOLUME_TRANSFORM + this->invalidate_bounding_box(); +} + +void ModelObject::scale_mesh(const Vec3d &versor) +{ + for (ModelVolume *v : this->volumes) + { + v->scale_geometry(versor); + v->set_offset(versor.cwiseProduct(v->get_offset())); + } this->invalidate_bounding_box(); } @@ -1150,7 +1130,6 @@ void ModelObject::split(ModelObjectPtrs* new_objects) new_object->instances.reserve(this->instances.size()); for (const ModelInstance *model_instance : this->instances) new_object->add_instance(*model_instance); -#if ENABLE_MODELVOLUME_TRANSFORM ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh)); new_vol->center_geometry(); @@ -1161,9 +1140,6 @@ void ModelObject::split(ModelObjectPtrs* new_objects) } new_vol->set_offset(Vec3d::Zero()); -#else - new_object->add_volume(*volume, std::move(*mesh)); -#endif // ENABLE_MODELVOLUME_TRANSFORM new_objects->emplace_back(new_object); delete mesh; } @@ -1204,7 +1180,6 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const if (!v->is_model_part()) continue; -#if ENABLE_MODELVOLUME_TRANSFORM Transform3d mv = mi * v->get_matrix(); const TriangleMesh& hull = v->get_convex_hull(); for (uint32_t f = 0; f < hull.stl.stats.number_of_facets; ++f) @@ -1214,15 +1189,6 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[1].cast())); min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[2].cast())); } -#else - for (uint32_t f = 0; f < v->mesh.stl.stats.number_of_facets; ++f) - { - const stl_facet* facet = v->mesh.stl.facet_start + f; - min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[0].cast())); - min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[1].cast())); - min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[2].cast())); - } -#endif // ENABLE_MODELVOLUME_TRANSFORM } return min_z + inst->get_offset(Z); @@ -1239,11 +1205,7 @@ unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3 unsigned int inside_outside = 0; for (const ModelVolume *vol : this->volumes) if (vol->is_model_part()) { -#if ENABLE_MODELVOLUME_TRANSFORM BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix()); -#else - BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix()); -#endif // ENABLE_MODELVOLUME_TRANSFORM if (print_volume.contains(bb)) inside_outside |= INSIDE; else if (print_volume.intersects(bb)) @@ -1335,7 +1297,6 @@ int ModelVolume::extruder_id() const return extruder_id; } -#if ENABLE_MODELVOLUME_TRANSFORM void ModelVolume::center_geometry() { Vec3d shift = -mesh.bounding_box().center(); @@ -1343,7 +1304,6 @@ void ModelVolume::center_geometry() m_convex_hull.translate((float)shift(0), (float)shift(1), (float)shift(2)); translate(-shift); } -#endif // ENABLE_MODELVOLUME_TRANSFORM void ModelVolume::calculate_convex_hull() { @@ -1403,9 +1363,7 @@ size_t ModelVolume::split(unsigned int max_extruders) std::string name = this->name; Model::reset_auto_extruder_id(); -#if ENABLE_MODELVOLUME_TRANSFORM Vec3d offset = this->get_offset(); -#endif // ENABLE_MODELVOLUME_TRANSFORM for (TriangleMesh *mesh : meshptrs) { mesh->repair(); @@ -1419,11 +1377,9 @@ size_t ModelVolume::split(unsigned int max_extruders) else this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(*mesh))); -#if ENABLE_MODELVOLUME_TRANSFORM this->object->volumes[ivolume]->set_offset(Vec3d::Zero()); this->object->volumes[ivolume]->center_geometry(); this->object->volumes[ivolume]->translate(offset); -#endif // ENABLE_MODELVOLUME_TRANSFORM this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1); this->object->volumes[ivolume]->config.set_deserialize("extruder", Model::get_auto_extruder_id_as_string(max_extruders)); delete mesh; @@ -1435,52 +1391,31 @@ size_t ModelVolume::split(unsigned int max_extruders) void ModelVolume::translate(const Vec3d& displacement) { -#if ENABLE_MODELVOLUME_TRANSFORM set_offset(get_offset() + displacement); -#else - mesh.translate((float)displacement(0), (float)displacement(1), (float)displacement(2)); - m_convex_hull.translate((float)displacement(0), (float)displacement(1), (float)displacement(2)); -#endif // ENABLE_MODELVOLUME_TRANSFORM } void ModelVolume::scale(const Vec3d& scaling_factors) { -#if ENABLE_MODELVOLUME_TRANSFORM set_scaling_factor(get_scaling_factor().cwiseProduct(scaling_factors)); -#else - mesh.scale(scaling_factors); - m_convex_hull.scale(scaling_factors); -#endif // ENABLE_MODELVOLUME_TRANSFORM } void ModelVolume::rotate(double angle, Axis axis) { -#if ENABLE_MODELVOLUME_TRANSFORM switch (axis) { case X: { rotate(angle, Vec3d::UnitX()); break; } case Y: { rotate(angle, Vec3d::UnitY()); break; } case Z: { rotate(angle, Vec3d::UnitZ()); break; } } -#else - mesh.rotate(angle, axis); - m_convex_hull.rotate(angle, axis); -#endif // ENABLE_MODELVOLUME_TRANSFORM } void ModelVolume::rotate(double angle, const Vec3d& axis) { -#if ENABLE_MODELVOLUME_TRANSFORM set_rotation(get_rotation() + Geometry::extract_euler_angles(Eigen::Quaterniond(Eigen::AngleAxisd(angle, axis)).toRotationMatrix())); -#else - mesh.rotate(angle, axis); - m_convex_hull.rotate(angle, axis); -#endif // ENABLE_MODELVOLUME_TRANSFORM } void ModelVolume::mirror(Axis axis) { -#if ENABLE_MODELVOLUME_TRANSFORM Vec3d mirror = get_mirror(); switch (axis) { @@ -1489,65 +1424,14 @@ void ModelVolume::mirror(Axis axis) case Z: { mirror(2) *= -1.0; break; } } set_mirror(mirror); -#else - mesh.mirror(axis); - m_convex_hull.mirror(axis); -#endif // ENABLE_MODELVOLUME_TRANSFORM } -#if !ENABLE_MODELVOLUME_TRANSFORM -void ModelInstance::set_rotation(const Vec3d& rotation) +void ModelVolume::scale_geometry(const Vec3d& versor) { - set_rotation(X, rotation(0)); - set_rotation(Y, rotation(1)); - set_rotation(Z, rotation(2)); + mesh.scale(versor); + m_convex_hull.scale(versor); } -void ModelInstance::set_rotation(Axis axis, double rotation) -{ - static const double TWO_PI = 2.0 * (double)PI; - while (rotation < 0.0) - { - rotation += TWO_PI; - } - while (TWO_PI < rotation) - { - rotation -= TWO_PI; - } - m_rotation(axis) = rotation; -} - -void ModelInstance::set_scaling_factor(const Vec3d& scaling_factor) -{ - set_scaling_factor(X, scaling_factor(0)); - set_scaling_factor(Y, scaling_factor(1)); - set_scaling_factor(Z, scaling_factor(2)); -} - -void ModelInstance::set_scaling_factor(Axis axis, double scaling_factor) -{ - m_scaling_factor(axis) = std::abs(scaling_factor); -} - -void ModelInstance::set_mirror(const Vec3d& mirror) -{ - set_mirror(X, mirror(0)); - set_mirror(Y, mirror(1)); - set_mirror(Z, mirror(2)); -} - -void ModelInstance::set_mirror(Axis axis, double mirror) -{ - double abs_mirror = std::abs(mirror); - if (abs_mirror == 0.0) - mirror = 1.0; - else if (abs_mirror != 1.0) - mirror /= abs_mirror; - - m_mirror(axis) = mirror; -} -#endif // !ENABLE_MODELVOLUME_TRANSFORM - void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const { mesh->transform(get_matrix(dont_translate)); @@ -1564,29 +1448,17 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh& mes // Scale the bounding box along the three axes. for (unsigned int i = 0; i < 3; ++i) { -#if ENABLE_MODELVOLUME_TRANSFORM if (std::abs(get_scaling_factor((Axis)i)-1.0) > EPSILON) { bbox.min(i) *= get_scaling_factor((Axis)i); bbox.max(i) *= get_scaling_factor((Axis)i); -#else - if (std::abs(this->m_scaling_factor(i) - 1.0) > EPSILON) - { - bbox.min(i) *= this->m_scaling_factor(i); - bbox.max(i) *= this->m_scaling_factor(i); -#endif // ENABLE_MODELVOLUME_TRANSFORM } } // Translate the bounding box. if (! dont_translate) { -#if ENABLE_MODELVOLUME_TRANSFORM bbox.min += get_offset(); bbox.max += get_offset(); -#else - bbox.min += this->m_offset; - bbox.max += this->m_offset; -#endif // ENABLE_MODELVOLUME_TRANSFORM } } return bbox; @@ -1604,30 +1476,12 @@ Vec3d ModelInstance::transform_vector(const Vec3d& v, bool dont_translate) const void ModelInstance::transform_polygon(Polygon* polygon) const { -#if ENABLE_MODELVOLUME_TRANSFORM // CHECK_ME -> Is the following correct or it should take in account all three rotations ? polygon->rotate(get_rotation(Z)); // rotate around polygon origin // CHECK_ME -> Is the following correct ? polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin -#else - // CHECK_ME -> Is the following correct or it should take in account all three rotations ? - polygon->rotate(this->m_rotation(2)); // rotate around polygon origin - // CHECK_ME -> Is the following correct ? - polygon->scale(this->m_scaling_factor(0), this->m_scaling_factor(1)); // scale around polygon origin -#endif // ENABLE_MODELVOLUME_TRANSFORM } -#if !ENABLE_MODELVOLUME_TRANSFORM -Transform3d ModelInstance::get_matrix(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const -{ - Vec3d translation = dont_translate ? Vec3d::Zero() : m_offset; - Vec3d rotation = dont_rotate ? Vec3d::Zero() : m_rotation; - Vec3d scale = dont_scale ? Vec3d::Ones() : m_scaling_factor; - Vec3d mirror = dont_mirror ? Vec3d::Ones() : m_mirror; - return Geometry::assemble_transform(translation, rotation, scale, mirror); -} -#endif // !ENABLE_MODELVOLUME_TRANSFORM - // 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) @@ -1671,10 +1525,9 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO return true; //FIXME test for the content of the mesh! -#if ENABLE_MODELVOLUME_TRANSFORM if (!mv_old.get_matrix().isApprox(mv_new.get_matrix())) return true; -#endif // ENABLE_MODELVOLUME_TRANSFORM + ++i_old; ++ i_new; } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index b02862203..e65228d80 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -11,9 +11,7 @@ #include #include #include -#if ENABLE_MODELVOLUME_TRANSFORM #include "Geometry.hpp" -#endif // ENABLE_MODELVOLUME_TRANSFORM namespace Slic3r { @@ -218,6 +216,8 @@ public: // Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes. // Currently used by ModelObject::mesh() and to calculate the 2D envelope for 2D platter. TriangleMesh raw_mesh() const; + // Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes. + TriangleMesh full_raw_mesh() const; // A transformed snug bounding box around the non-modifier object volumes, without the translation applied. // This bounding box is only used for the actual slicing. BoundingBoxf3 raw_bounding_box() const; @@ -235,6 +235,9 @@ public: void rotate(double angle, Axis axis); void rotate(double angle, const Vec3d& axis); void mirror(Axis axis); + + void scale_mesh(const Vec3d& versor); + size_t materials_count() const; size_t facets_count() const; bool needed_repair() const; @@ -329,10 +332,10 @@ public: void rotate(double angle, const Vec3d& axis); void mirror(Axis axis); -#if ENABLE_MODELVOLUME_TRANSFORM + void scale_geometry(const Vec3d& versor); + // translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box void center_geometry(); -#endif // ENABLE_MODELVOLUME_TRANSFORM void calculate_convex_hull(); const TriangleMesh& get_convex_hull() const; @@ -341,7 +344,6 @@ public: static Type type_from_string(const std::string &s); static std::string type_to_string(const Type t); -#if ENABLE_MODELVOLUME_TRANSFORM const Geometry::Transformation& get_transformation() const { return m_transformation; } void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; } @@ -370,7 +372,6 @@ public: void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); } const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); } -#endif // ENABLE_MODELVOLUME_TRANSFORM protected: friend class Print; @@ -388,9 +389,7 @@ private: t_model_material_id m_material_id; // The convex hull of this model's mesh. TriangleMesh m_convex_hull; -#if ENABLE_MODELVOLUME_TRANSFORM Geometry::Transformation m_transformation; -#endif // ENABLE_MODELVOLUME_TRANSFORM ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object) { @@ -400,7 +399,6 @@ private: ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {} -#if ENABLE_MODELVOLUME_TRANSFORM // Copying an existing volume, therefore this volume will get a copy of the ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other) : ModelBase(other), // copy the ID @@ -416,25 +414,6 @@ private: if (mesh.stl.stats.number_of_facets > 1) calculate_convex_hull(); } -#else - // Copying an existing volume, therefore this volume will get a copy of the ID assigned. - ModelVolume(ModelObject *object, const ModelVolume &other) : - ModelBase(other), // copy the ID - name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object) - { - if (! other.material_id().empty()) - this->set_material_id(other.material_id()); - } - // Providing a new mesh, therefore this volume will get a new unique ID assigned. - ModelVolume(ModelObject *object, const ModelVolume &other, TriangleMesh &&mesh) : - name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object) - { - if (! other.material_id().empty()) - this->set_material_id(other.material_id()); - if (mesh.stl.stats.number_of_facets > 1) - calculate_convex_hull(); - } -#endif // ENABLE_MODELVOLUME_TRANSFORM ModelVolume& operator=(ModelVolume &rhs) = delete; }; @@ -453,14 +432,7 @@ public: }; private: -#if ENABLE_MODELVOLUME_TRANSFORM Geometry::Transformation m_transformation; -#else - Vec3d m_offset; // in unscaled coordinates - Vec3d m_rotation; // Rotation around the three axes, in radians around mesh center point - Vec3d m_scaling_factor; // Scaling factors along the three axes - Vec3d m_mirror; // Mirroring along the three axes -#endif // ENABLE_MODELVOLUME_TRANSFORM public: // flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state()) @@ -468,7 +440,6 @@ public: ModelObject* get_object() const { return this->object; } -#if ENABLE_MODELVOLUME_TRANSFORM const Geometry::Transformation& get_transformation() const { return m_transformation; } void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; } @@ -495,31 +466,6 @@ public: void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); } void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); } -#else - const Vec3d& get_offset() const { return m_offset; } - double get_offset(Axis axis) const { return m_offset(axis); } - - void set_offset(const Vec3d& offset) { m_offset = offset; } - void set_offset(Axis axis, double offset) { m_offset(axis) = offset; } - - const Vec3d& get_rotation() const { return m_rotation; } - double get_rotation(Axis axis) const { return m_rotation(axis); } - - void set_rotation(const Vec3d& rotation); - void set_rotation(Axis axis, double rotation); - - Vec3d get_scaling_factor() const { return m_scaling_factor; } - double get_scaling_factor(Axis axis) const { return m_scaling_factor(axis); } - - void set_scaling_factor(const Vec3d& scaling_factor); - void set_scaling_factor(Axis axis, double scaling_factor); - - const Vec3d& get_mirror() const { return m_mirror; } - double get_mirror(Axis axis) const { return m_mirror(axis); } - - void set_mirror(const Vec3d& mirror); - void set_mirror(Axis axis, double mirror); -#endif // ENABLE_MODELVOLUME_TRANSFORM // To be called on an external mesh void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; @@ -532,11 +478,7 @@ public: // To be called on an external polygon. It does not translate the polygon, only rotates and scales. void transform_polygon(Polygon* polygon) const; -#if ENABLE_MODELVOLUME_TRANSFORM const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); } -#else - Transform3d get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const; -#endif // ENABLE_MODELVOLUME_TRANSFORM bool is_printable() const { return print_volume_state == PVS_Inside; } @@ -552,17 +494,11 @@ private: // Parent object, owning this instance. ModelObject* object; -#if ENABLE_MODELVOLUME_TRANSFORM // Constructor, which assigns a new unique ID. explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {} // Constructor, which assigns a new unique ID. explicit ModelInstance(ModelObject *object, const ModelInstance &other) : m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) {} -#else - explicit ModelInstance(ModelObject *object) : m_offset(Vec3d::Zero()), m_rotation(Vec3d::Zero()), m_scaling_factor(Vec3d::Ones()), m_mirror(Vec3d::Ones()), object(object), print_volume_state(PVS_Inside) {} - explicit ModelInstance(ModelObject *object, const ModelInstance &other) : - m_offset(other.m_offset), m_rotation(other.m_rotation), m_scaling_factor(other.m_scaling_factor), m_mirror(other.m_mirror), object(object), print_volume_state(PVS_Inside) {} -#endif // ENABLE_MODELVOLUME_TRANSFORM ModelInstance() = delete; explicit ModelInstance(ModelInstance &&rhs) = delete; diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index d182f1501..8ed595802 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -135,11 +135,6 @@ objfunc(const PointImpl& bincenter, const ItemGroup& remaining ) { - using Coord = TCoord; - - static const double ROUNDNESS_RATIO = 0.5; - static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO; - // We will treat big items (compared to the print bed) differently auto isBig = [bin_area](double a) { return a/bin_area > BIG_ITEM_TRESHOLD ; @@ -516,8 +511,9 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { ModelInstance * finst = objptr->instances.front(); // Object instances should carry the same scaling and - // x, y rotation that is why we use the first instance - rmesh.scale(finst->get_scaling_factor()); + // x, y rotation that is why we use the first instance. + // The next line will apply only the full mirroring and scaling + rmesh.transform(finst->get_matrix(true, true, false, false)); rmesh.rotate_x(float(finst->get_rotation()(X))); rmesh.rotate_y(float(finst->get_rotation()(Y))); @@ -629,11 +625,12 @@ BedShapeHint bedShape(const Polyline &bed) { avg_dist /= vertex_distances.size(); Circle ret(center, avg_dist); - for (auto el: vertex_distances) + for(auto el : vertex_distances) { - if (abs(el - avg_dist) > 10 * SCALED_EPSILON) + if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) { ret = Circle(); - break; + break; + } } return ret; @@ -665,8 +662,6 @@ bool arrange(Model &model, std::function progressind, std::function stopcondition) { - using ArrangeResult = _IndexedPackGroup; - bool ret = true; // Get the 2D projected shapes with their 3D model instance pointers diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index 7278a64f4..c3e388e2a 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -18,8 +18,8 @@ void MultiPoint::scale(double factor_x, double factor_y) { for (Point &pt : points) { - pt(0) *= factor_x; - pt(1) *= factor_y; + pt(0) = coord_t(pt(0) * factor_x); + pt(1) = coord_t(pt(1) * factor_y); } } @@ -83,7 +83,7 @@ MultiPoint::find_point(const Point &point) const { for (const Point &pt : this->points) if (pt == point) - return &pt - &this->points.front(); + return int(&pt - &this->points.front()); return -1; // not found } @@ -202,6 +202,7 @@ Point MultiPoint::point_projection(const Point &point) const { std::vector MultiPoint::_douglas_peucker(const std::vector& pts, const double tolerance) { std::vector result_pts; + double tolerance_sq = tolerance * tolerance; if (! pts.empty()) { const Point *anchor = &pts.front(); size_t anchor_idx = 0; @@ -215,18 +216,18 @@ std::vector MultiPoint::_douglas_peucker(const std::vector& pts, c dpStack.reserve(pts.size()); dpStack.emplace_back(floater_idx); for (;;) { - double max_distSq = 0.0; + double max_dist_sq = 0.0; size_t furthest_idx = anchor_idx; // find point furthest from line seg created by (anchor, floater) and note it for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) { - double dist = Line::distance_to_squared(pts[i], *anchor, *floater); - if (dist > max_distSq) { - max_distSq = dist; + double dist_sq = Line::distance_to_squared(pts[i], *anchor, *floater); + if (dist_sq > max_dist_sq) { + max_dist_sq = dist_sq; furthest_idx = i; } } // remove point if less than tolerance - if (max_distSq <= tolerance) { + if (max_dist_sq <= tolerance_sq) { result_pts.emplace_back(*floater); anchor_idx = floater_idx; anchor = floater; @@ -369,8 +370,8 @@ Points MultiPoint::visivalingam(const Points& pts, const double& tolerance) void MultiPoint3::translate(double x, double y) { for (Vec3crd &p : points) { - p(0) += x; - p(1) += y; + p(0) += coord_t(x); + p(1) += coord_t(y); } } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 4c27d488b..cab1578d8 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -8,13 +8,14 @@ #include "SupportMaterial.hpp" #include "GCode.hpp" #include "GCode/WipeTowerPrusaMM.hpp" -#include -#include -#include +#include "Utils.hpp" #include "PrintExport.hpp" +#include +#include #include +#include //! macro used to mark string used at localization, //! return same string @@ -762,7 +763,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co update_apply_status(this->invalidate_all_steps()); for (PrintObject *object : m_objects) { model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted); - delete object; + update_apply_status(object->invalidate_all_steps()); + delete object; } m_objects.clear(); for (PrintRegion *region : m_regions) @@ -992,12 +994,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co const_cast(*it_old)->status = PrintObjectStatus::Deleted; } else { // The PrintObject already exists and the copies differ. - if ((*it_old)->print_object->copies().size() != new_instances.copies.size()) - update_apply_status(this->invalidate_step(psWipeTower)); - if ((*it_old)->print_object->set_copies(new_instances.copies)) { - // Invalidated - update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psGCodeExport })); - } + PrintBase::ApplyStatus status = (*it_old)->print_object->set_copies(new_instances.copies); + if (status != PrintBase::APPLY_STATUS_UNCHANGED) + update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED); print_objects_new.emplace_back((*it_old)->print_object); const_cast(*it_old)->status = PrintObjectStatus::Reused; } @@ -1011,13 +1010,14 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co bool deleted_objects = false; for (auto &pos : print_object_status) if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) { - // update_apply_status(pos.print_object->invalidate_all_steps()); + update_apply_status(pos.print_object->invalidate_all_steps()); delete pos.print_object; deleted_objects = true; } if (new_objects || deleted_objects) update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psWipeTower, psGCodeExport })); - update_apply_status(new_objects); + if (new_objects) + update_apply_status(false); } print_object_status.clear(); } @@ -1055,10 +1055,12 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co goto print_object_end; } else { this_region_config = region_config_from_model_volume(m_default_region_config, volume, num_extruders); - for (size_t i = 0; i < region_id; ++ i) - if (m_regions[i]->config().equals(this_region_config)) - // Regions were merged. Reset this print_object. - goto print_object_end; + for (size_t i = 0; i < region_id; ++i) { + const PrintRegion ®ion_other = *m_regions[i]; + if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config)) + // Regions were merged. Reset this print_object. + goto print_object_end; + } this_region_config_set = true; } } @@ -1096,8 +1098,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co bool fresh = print_object.region_volumes.empty(); unsigned int volume_id = 0; for (const ModelVolume *volume : model_object.volumes) { - if (! volume->is_model_part() && ! volume->is_modifier()) - continue; + if (! volume->is_model_part() && ! volume->is_modifier()) { + ++ volume_id; + continue; + } int region_id = -1; if (&print_object == &print_object0) { // Get the config applied to this volume. @@ -1105,9 +1109,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co // Find an existing print region with the same config. int idx_empty_slot = -1; for (int i = 0; i < (int)m_regions.size(); ++ i) { - if (m_regions[i]->m_refcnt == 0) - idx_empty_slot = i; - else if (config.equals(m_regions[i]->config())) { + if (m_regions[i]->m_refcnt == 0) { + if (idx_empty_slot == -1) + idx_empty_slot = i; + } else if (config.equals(m_regions[i]->config())) { region_id = i; break; } @@ -1473,7 +1478,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const // Slicing process, running at a background thread. void Print::process() { - BOOST_LOG_TRIVIAL(info) << "Staring the slicing process."; + BOOST_LOG_TRIVIAL(info) << "Staring the slicing process." << log_memory_info(); for (PrintObject *obj : m_objects) obj->make_perimeters(); this->set_status(70, "Infilling layers"); @@ -1505,7 +1510,7 @@ void Print::process() } this->set_done(psWipeTower); } - BOOST_LOG_TRIVIAL(info) << "Slicing process finished."; + BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info(); } // G-code export process, running at a background thread. @@ -1630,7 +1635,9 @@ void Print::_make_skirt() { Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, scale_(0.1)); Geometry::simplify_polygons(loops, scale_(0.05), &loops); - loop = loops.front(); + if (loops.empty()) + break; + loop = loops.front(); } // Extrude the skirt loop. ExtrusionLoop eloop(elrSkirt); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 62c42237c..de3215578 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -107,8 +107,8 @@ public: // adds region_id, too, if necessary void add_region_volume(unsigned int region_id, int volume_id) { if (region_id >= region_volumes.size()) - region_volumes.resize(region_id + 1); - region_volumes[region_id].push_back(volume_id); + region_volumes.resize(region_id + 1); + region_volumes[region_id].emplace_back(volume_id); } // This is the *total* layer count (including support layers) // this value is not supposed to be compared with Layer::id @@ -164,7 +164,7 @@ protected: void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); } void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); } void set_trafo(const Transform3d& trafo) { m_trafo = trafo; } - bool set_copies(const Points &points); + PrintBase::ApplyStatus set_copies(const Points &points); // Invalidates the step, and its depending steps in PrintObject and Print. bool invalidate_step(PrintObjectStep step); // Invalidates all PrintObject and Print steps. diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d87dc5cb4..fddfef0f1 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2670,7 +2670,9 @@ void PrintConfigDef::init_sla_params() def = this->add("display_orientation", coEnum); def->label = L("Display orientation"); - def->tooltip = L("Display orientation"); + def->tooltip = L("Set the actual LCD display orientation inside the SLA printer." + " Portrait mode will flip the meaning of display width and height parameters" + " and the output images will be rotated by 90 degrees."); def->cli = "display-orientation=s"; def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("landscape"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 74bdaab7d..32629a64b 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -36,7 +36,7 @@ enum GCodeFlavor { }; enum PrintHostType { - htOctoPrint, htDuet, + htOctoPrint, htDuet, htSL1, }; enum InfillPattern { diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index db853dea8..a159d2020 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -5,6 +5,7 @@ #include "SupportMaterial.hpp" #include "Surface.hpp" #include "Slicing.hpp" +#include "Utils.hpp" #include #include @@ -68,7 +69,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta this->layer_height_profile = model_object->layer_height_profile; } -bool PrintObject::set_copies(const Points &points) +PrintBase::ApplyStatus PrintObject::set_copies(const Points &points) { // Order copies with a nearest-neighbor search. std::vector copies; @@ -80,14 +81,15 @@ bool PrintObject::set_copies(const Points &points) copies.emplace_back(points[point_idx] + m_copies_shift); } // Invalidate and set copies. - bool invalidated = false; + PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED; if (copies != m_copies) { - invalidated = m_print->invalidate_steps({ psSkirt, psBrim, psGCodeExport }); - if (copies.size() != m_copies.size()) - invalidated |= m_print->invalidate_step(psWipeTower); + status = PrintBase::APPLY_STATUS_CHANGED; + if (m_print->invalidate_steps({ psSkirt, psBrim, psGCodeExport }) || + (copies.size() != m_copies.size() && m_print->invalidate_step(psWipeTower))) + status = PrintBase::APPLY_STATUS_INVALIDATED; m_copies = copies; } - return invalidated; + return status; } // 1) Decides Z positions of the layers, @@ -132,7 +134,7 @@ void PrintObject::make_perimeters() return; m_print->set_status(20, "Generating perimeters"); - BOOST_LOG_TRIVIAL(info) << "Generating perimeters..."; + BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); // merge slices if they were split into types if (this->typed_slices) { @@ -253,7 +255,7 @@ void PrintObject::prepare_infill() // Decide what surfaces are to be filled. // Here the S_TYPE_TOP / S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is turned to just S_TYPE_INTERNAL if zero top / bottom infill layers are configured. // Also tiny S_TYPE_INTERNAL surfaces are turned to S_TYPE_INTERNAL_SOLID. - BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..."; + BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..." << log_memory_info(); for (auto *layer : m_layers) for (auto *region : layer->m_regions) { region->prepare_fill_surfaces(); @@ -587,15 +589,15 @@ bool PrintObject::invalidate_step(PrintObjectStep step) // propagate to dependent steps if (step == posPerimeters) { - invalidated |= this->invalidate_step(posPrepareInfill); + invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill }); invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); } else if (step == posPrepareInfill) { invalidated |= this->invalidate_step(posInfill); } else if (step == posInfill) { invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); } else if (step == posSlice) { - invalidated |= this->invalidate_steps({ posPerimeters, posSupportMaterial }); - invalidated |= m_print->invalidate_step(psWipeTower); + invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posSupportMaterial }); + invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); } else if (step == posSupportMaterial) invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); @@ -882,7 +884,7 @@ void PrintObject::count_distance_solid() { // If a part of a region is of stBottom and stTop, the stBottom wins. void PrintObject::detect_surfaces_type() { - BOOST_LOG_TRIVIAL(info) << "Detecting solid surfaces..."; + BOOST_LOG_TRIVIAL(info) << "Detecting solid surfaces..." << log_memory_info(); // Interface shells: the intersecting parts are treated as self standing objects supporting each other. // Each of the objects will have a full number of top / bottom layers, even if these top / bottom layers @@ -1076,7 +1078,7 @@ void PrintObject::detect_surfaces_type() void PrintObject::process_external_surfaces() { - BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..."; + BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..." << log_memory_info(); for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) { const PrintRegion ®ion = *m_print->regions()[region_id]; @@ -1101,7 +1103,7 @@ void PrintObject::discover_vertical_shells() { PROFILE_FUNC(); - BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells..."; + BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells..." << log_memory_info(); struct DiscoverVerticalShellsCacheEntry { @@ -1485,7 +1487,7 @@ void PrintObject::discover_vertical_shells() sparse infill */ void PrintObject::bridge_over_infill() { - BOOST_LOG_TRIVIAL(info) << "Bridge over infill..."; + BOOST_LOG_TRIVIAL(info) << "Bridge over infill..." << log_memory_info(); for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { const PrintRegion ®ion = *m_print->regions()[region_id]; @@ -1833,7 +1835,7 @@ bool PrintObject::update_layer_height_profile() // this should be idempotent void PrintObject::_slice() { - BOOST_LOG_TRIVIAL(info) << "Slicing objects..."; + BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info(); this->typed_slices = false; @@ -2064,15 +2066,11 @@ std::vector PrintObject::_slice_volumes(const std::vector &z, //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. TriangleMesh mesh; for (const ModelVolume *v : volumes) -#if ENABLE_MODELVOLUME_TRANSFORM { TriangleMesh vol_mesh(v->mesh); vol_mesh.transform(v->get_matrix()); mesh.merge(vol_mesh); } -#else - mesh.merge(v->mesh); -#endif // ENABLE_MODELVOLUME_TRANSFORM if (mesh.stl.stats.number_of_facets > 0) { mesh.transform(m_trafo); // apply XY shift @@ -2197,7 +2195,7 @@ void PrintObject::_make_perimeters() if (! this->set_started(posPerimeters)) return; - BOOST_LOG_TRIVIAL(info) << "Generating perimeters..."; + BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); // merge slices if they were split into types if (this->typed_slices) { diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index ca04b1bee..ee87c6b66 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -3,23 +3,50 @@ #include "SLAAutoSupports.hpp" #include "Model.hpp" +#include "ExPolygon.hpp" +#include "SVG.hpp" +#include "Point.hpp" +#include "ClipperUtils.hpp" #include - +#include namespace Slic3r { -SLAAutoSupports::SLAAutoSupports(ModelObject& mo, const SLAAutoSupports::Config& c) -: m_model_object(mo), mesh(), m_config(c) -{} +SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices, const std::vector& heights, + const Config& config, std::function throw_on_cancel) +: m_config(config), m_V(emesh.V), m_F(emesh.F), m_throw_on_cancel(throw_on_cancel) +{ + // FIXME: It might be safer to get rid of the rand() calls altogether, because it is probably + // not always thread-safe and can be slow if it is. + srand(time(NULL)); // rand() is used by igl::random_point_on_mesh + + // Find all separate islands that will need support. The coord_t number denotes height + // of a point just below the mesh (so that we can later project the point precisely + // on the mesh by raycasting (done by igl) and not risking we will place the point inside). + std::vector> islands = find_islands(slices, heights); + + // Uniformly cover each of the islands with support points. + for (const auto& island : islands) { + std::vector points = uniformly_cover(island); + m_throw_on_cancel(); + project_upward_onto_mesh(points); + m_output.insert(m_output.end(), points.begin(), points.end()); + m_throw_on_cancel(); + } + + // We are done with the islands. Let's sprinkle the rest of the mesh. + // The function appends to m_output. + sprinkle_mesh(mesh); +} -float SLAAutoSupports::approximate_geodesic_distance(const Vec3f& p1, const Vec3f& p2, Vec3f& n1, Vec3f& n2) +float SLAAutoSupports::approximate_geodesic_distance(const Vec3d& p1, const Vec3d& p2, Vec3d& n1, Vec3d& n2) { n1.normalize(); n2.normalize(); - Vec3f v = (p2-p1); + Vec3d v = (p2-p1); v.normalize(); float c1 = n1.dot(v); @@ -32,15 +59,16 @@ float SLAAutoSupports::approximate_geodesic_distance(const Vec3f& p1, const Vec3 } -void SLAAutoSupports::generate() +void SLAAutoSupports::sprinkle_mesh(const TriangleMesh& mesh) { + std::vector points; // Loads the ModelObject raw_mesh and transforms it by first instance's transformation matrix (disregarding translation). // Instances only differ in z-rotation, so it does not matter which of them will be used for the calculation. // The supports point will be calculated on this mesh (so scaling ang vertical direction is correctly accounted for). // Results will be inverse-transformed to raw_mesh coordinates. - TriangleMesh mesh = m_model_object.raw_mesh(); - Transform3d transformation_matrix = m_model_object.instances[0]->get_matrix(true/*dont_translate*/); - mesh.transform(transformation_matrix); + //TriangleMesh mesh = m_model_object.raw_mesh(); + //Transform3d transformation_matrix = m_model_object.instances[0]->get_matrix(true/*dont_translate*/); + //mesh.transform(transformation_matrix); // Check that the object is thick enough to produce any support points BoundingBoxf3 bb = mesh.bounding_box(); @@ -48,30 +76,20 @@ void SLAAutoSupports::generate() return; // All points that we curretly have must be transformed too, so distance to them is correcly calculated. - for (Vec3f& point : m_model_object.sla_support_points) - point = transformation_matrix.cast() * point; + //for (Vec3f& point : m_model_object.sla_support_points) + // point = transformation_matrix.cast() * point; - const stl_file& stl = mesh.stl; - Eigen::MatrixXf V; - Eigen::MatrixXi F; - V.resize(3 * stl.stats.number_of_facets, 3); - F.resize(stl.stats.number_of_facets, 3); - for (unsigned int i=0; ivertex[0](0); V(3*i+0, 1) = facet->vertex[0](1); V(3*i+0, 2) = facet->vertex[0](2); - V(3*i+1, 0) = facet->vertex[1](0); V(3*i+1, 1) = facet->vertex[1](1); V(3*i+1, 2) = facet->vertex[1](2); - V(3*i+2, 0) = facet->vertex[2](0); V(3*i+2, 1) = facet->vertex[2](1); V(3*i+2, 2) = facet->vertex[2](2); - F(i, 0) = 3*i+0; - F(i, 1) = 3*i+1; - F(i, 2) = 3*i+2; - } // In order to calculate distance to already placed points, we must keep know which facet the point lies on. - std::vector facets_normals; + std::vector facets_normals; + + // Only points belonging to islands were added so far - they all lie on horizontal surfaces: + for (unsigned int i=0; i aabb; + /*igl::AABB aabb; aabb.init(V, F); for (unsigned int i=0; i()); if (angle > threshold_angle) continue; - const float distance_limit = 1./(2.4*get_required_density(angle)); + const float limit = distance_limit(angle); bool add_it = true; - for (unsigned int i=0; i()); facets_normals.push_back(normal); ++added_points; refused_points = 0; } } + m_output.insert(m_output.end(), points.begin(), points.end()); + // Now transform all support points to mesh coordinates: - for (Vec3f& point : m_model_object.sla_support_points) - point = transformation_matrix.inverse().cast() * point; + //for (Vec3f& point : m_model_object.sla_support_points) + // point = transformation_matrix.inverse().cast() * point; } @@ -150,6 +176,160 @@ float SLAAutoSupports::get_required_density(float angle) const return std::max(0.f, float(m_config.density_at_horizontal * cos(K*angle))); } +float SLAAutoSupports::distance_limit(float angle) const +{ + return 1./(2.4*get_required_density(angle)); +} + +#ifdef SLA_AUTOSUPPORTS_DEBUG +void SLAAutoSupports::output_expolygons(const ExPolygons& expolys, std::string filename) const +{ + BoundingBox bb(Point(-30000000, -30000000), Point(30000000, 30000000)); + Slic3r::SVG svg_cummulative(filename, bb); + for (size_t i = 0; i < expolys.size(); ++ i) { + /*Slic3r::SVG svg("single"+std::to_string(i)+".svg", bb); + svg.draw(expolys[i]); + svg.draw_outline(expolys[i].contour, "black", scale_(0.05)); + svg.draw_outline(expolys[i].holes, "blue", scale_(0.05)); + svg.Close();*/ + + svg_cummulative.draw(expolys[i]); + svg_cummulative.draw_outline(expolys[i].contour, "black", scale_(0.05)); + svg_cummulative.draw_outline(expolys[i].holes, "blue", scale_(0.05)); + } +} +#endif /* SLA_AUTOSUPPORTS_DEBUG */ + +std::vector> SLAAutoSupports::find_islands(const std::vector& slices, const std::vector& heights) const +{ + std::vector> islands; + + struct PointAccessor { + const Point* operator()(const Point &pt) const { return &pt; } + }; + typedef ClosestPointInRadiusLookup ClosestPointLookupType; + + for (unsigned int i = 0; i SLAAutoSupports::uniformly_cover(const std::pair& island) +{ + int num_of_points = std::max(1, (int)(island.first.area()*pow(SCALING_FACTOR, 2) * get_required_density(0))); + + // In case there is just one point to place, we'll place it into the polygon's centroid (unless it lies in a hole). + if (num_of_points == 1) { + Point out(island.first.contour.centroid()); + + for (const auto& hole : island.first.holes) + if (hole.contains(out)) + goto HOLE_HIT; + return std::vector{unscale(out(0), out(1), island.second)}; + } + +HOLE_HIT: + // In this case either the centroid lies in a hole, or there are multiple points + // to place. We will cover the island another way. + // For now we'll just place the points randomly not too close to the others. + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution<> dis(0., 1.); + + std::vector island_new_points; + const BoundingBox& bb = get_extents(island.first); + const int refused_limit = 30; + int refused_points = 0; + while (refused_points < refused_limit) { + Point out(bb.min(0) + bb.size()(0) * dis(gen), + bb.min(1) + bb.size()(1) * dis(gen)) ; + Vec3d unscaled_out = unscale(out(0), out(1), island.second); + bool add_it = true; + + if (!island.first.contour.contains(out)) + add_it = false; + else + for (const Polygon& hole : island.first.holes) + if (hole.contains(out)) + add_it = false; + + if (add_it) { + for (const Vec3d& p : island_new_points) { + if ((p - unscaled_out).squaredNorm() < distance_limit(0)) { + add_it = false; + ++refused_points; + break; + } + } + } + if (add_it) + island_new_points.emplace_back(unscaled_out); + } + return island_new_points; +} + +void SLAAutoSupports::project_upward_onto_mesh(std::vector& points) const +{ + Vec3f dir(0., 0., 1.); + igl::Hit hit{0, 0, 0.f, 0.f, 0.f}; + for (Vec3d& p : points) { + igl::ray_mesh_intersect(p.cast(), dir, m_V, m_F, hit); + int fid = hit.id; + Vec3f bc(1-hit.u-hit.v, hit.u, hit.v); + p = (bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2))).cast(); + } +} } // namespace Slic3r \ No newline at end of file diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index 40726fd0e..311d7b0c7 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -3,15 +3,12 @@ #include #include +#include +// #define SLA_AUTOSUPPORTS_DEBUG namespace Slic3r { -class ModelObject; - - - - class SLAAutoSupports { public: struct Config { @@ -20,22 +17,34 @@ public: float minimal_z; }; - SLAAutoSupports(ModelObject& mo, const SLAAutoSupports::Config& c); - void generate(); + SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices, + const std::vector& heights, const Config& config, std::function throw_on_cancel); + const std::vector& output() { return m_output; } private: + std::vector m_output; + std::vector m_normals; TriangleMesh mesh; static float angle_from_normal(const stl_normal& normal) { return acos((-normal.normalized())(2)); } float get_required_density(float angle) const; - static float approximate_geodesic_distance(const Vec3f& p1, const Vec3f& p2, Vec3f& n1, Vec3f& n2); + float distance_limit(float angle) const; + static float approximate_geodesic_distance(const Vec3d& p1, const Vec3d& p2, Vec3d& n1, Vec3d& n2); + std::vector> find_islands(const std::vector& slices, const std::vector& heights) const; + void sprinkle_mesh(const TriangleMesh& mesh); + std::vector uniformly_cover(const std::pair& island); + void project_upward_onto_mesh(std::vector& points) const; + +#ifdef SLA_AUTOSUPPORTS_DEBUG + void output_expolygons(const ExPolygons& expolys, std::string filename) const; +#endif /* SLA_AUTOSUPPORTS_DEBUG */ - ModelObject& m_model_object; SLAAutoSupports::Config m_config; + std::function m_throw_on_cancel; + const Eigen::MatrixXd& m_V; + const Eigen::MatrixXi& m_F; }; - - } // namespace Slic3r diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 8615ab91c..746acc547 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -8,11 +8,12 @@ #include "SLABoilerPlate.hpp" #include "SLASpatIndex.hpp" #include "SLABasePool.hpp" -#include -#include "ClipperUtils.hpp" +#include "ClipperUtils.hpp" #include "Model.hpp" +#include + /** * Terminology: * @@ -612,7 +613,9 @@ double ray_mesh_intersect(const Vec3d& s, const Vec3d& dir, const EigenMesh3D& m); -PointSet normals(const PointSet& points, const EigenMesh3D& mesh); +PointSet normals(const PointSet& points, const EigenMesh3D& mesh, + double eps = 0.05, // min distance from edges + std::function throw_on_cancel = [](){}); inline Vec2d to_vec2(const Vec3d& v3) { return {v3(X), v3(Y)}; @@ -720,6 +723,10 @@ public: return m_pad; } + void remove_pad() { + m_pad = Pad(); + } + const Pad& pad() const { return m_pad; } // WITHOUT THE PAD!!! @@ -1049,7 +1056,7 @@ bool SLASupportTree::generate(const PointSet &points, tifcl(); // calculate the normals to the triangles belonging to filtered points - auto nmls = sla::normals(filt_pts, mesh); + auto nmls = sla::normals(filt_pts, mesh, cfg.head_front_radius_mm, tifcl); head_norm.resize(count, 3); head_pos.resize(count, 3); @@ -1078,7 +1085,8 @@ bool SLASupportTree::generate(const PointSet &points, double polar = std::acos(z / r); double azimuth = std::atan2(n(1), n(0)); - if(polar >= PI / 2) { // skip if the tilt is not sane + // skip if the tilt is not sane + if(polar >= PI - cfg.normal_cutoff_angle) { // We saturate the polar angle to 3pi/4 polar = std::max(polar, 3*PI / 4); @@ -1109,7 +1117,9 @@ bool SLASupportTree::generate(const PointSet &points, head_norm.row(pcount) = nn; ++pcount; - } else { + } else if( polar >= 3*PI/4 ) { + // Headless supports do not tilt like the headed ones so + // the normal should point almost to the ground. headless_norm.row(hlcount) = nn; headless_pos.row(hlcount++) = hp; } @@ -1238,7 +1248,7 @@ bool SLASupportTree::generate(const PointSet &points, result.add_bridge(sj, ej, pillar.r); // double bridging: (crosses) - if(bridge_distance > 2*cfg.base_radius_mm) { + if(pillar_dist > 2*cfg.base_radius_mm) { // If the columns are close together, no need to // double bridge them Vec3d bsj(ej(X), ej(Y), sj(Z)); @@ -1509,22 +1519,39 @@ bool SLASupportTree::generate(const PointSet &points, { // TODO: connect these to the ground pillars if possible for(auto idx : nogndidx) { tifcl(); + double gh = gndheight[idx]; + double base_width = cfg.head_width_mm; + auto& head = result.head(idx); + + // In this case there is no room for the base pinhead. + if(gh < head.fullwidth()) { + base_width = gh - 2 * cfg.head_front_radius_mm - + 2*cfg.head_back_radius_mm + cfg.head_penetration_mm; + } + head.transform(); - double gh = gndheight[idx]; Vec3d headend = head.junction_point(); Head base_head(cfg.head_back_radius_mm, cfg.head_front_radius_mm, - cfg.head_width_mm, + base_width, cfg.head_penetration_mm, {0.0, 0.0, 1.0}, {headend(X), headend(Y), headend(Z) - gh}); base_head.transform(); - double hl = head.fullwidth() - head.r_back_mm; + // Robustness check: + if(headend(Z) < base_head.junction_point()(Z)) { + // This should not happen it is against all assumptions + BOOST_LOG_TRIVIAL(warning) + << "Ignoring invalid supports connecting to model body"; + continue; + } + + double hl = base_head.fullwidth() - head.r_back_mm; result.add_pillar(idx, Vec3d{headend(X), headend(Y), headend(Z) - gh + hl}, @@ -1547,7 +1574,7 @@ bool SLASupportTree::generate(const PointSet &points, const double HWIDTH_MM = R/3; // We will sink the pins into the model surface for a distance of 1/3 of - // HWIDTH_MM + // the pin radius for(int i = 0; i < headless_pts.rows(); i++) { tifcl(); Vec3d sp = headless_pts.row(i); @@ -1558,7 +1585,7 @@ bool SLASupportTree::generate(const PointSet &points, Vec3d sj = sp + R * n; double dist = ray_mesh_intersect(sj, dir, emesh); - if(std::isinf(dist) || std::isnan(dist)) continue; + if(std::isinf(dist) || std::isnan(dist) || dist < 2*R) continue; Vec3d ej = sj + (dist + HWIDTH_MM)* dir; result.add_compact_bridge(sp, ej, n, R); @@ -1727,6 +1754,11 @@ const TriangleMesh &SLASupportTree::get_pad() const return m_impl->pad().tmesh; } +void SLASupportTree::remove_pad() +{ + m_impl->remove_pad(); +} + SLASupportTree::SLASupportTree(const PointSet &points, const EigenMesh3D& emesh, const SupportConfig &cfg, diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index 62e790611..c187cf5b3 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -67,6 +67,10 @@ struct SupportConfig { // The elevation in Z direction upwards. This is the space between the pad // and the model object's bounding box bottom. double object_elevation_mm = 10; + + // The max Z angle for a normal at which it will get completely ignored. + double normal_cutoff_angle = 110.0 * M_PI / 180.0; + }; struct PoolConfig; @@ -164,6 +168,8 @@ public: /// Get the pad geometry const TriangleMesh& get_pad() const; + void remove_pad(); + }; } diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 50d7775a2..0cc9f14e0 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -1,3 +1,4 @@ +#include #include "SLA/SLASupportTree.hpp" #include "SLA/SLABoilerPlate.hpp" #include "SLA/SLASpatIndex.hpp" @@ -9,15 +10,8 @@ #include "boost/geometry/index/rtree.hpp" #include - -//#if !defined(_MSC_VER) || defined(_WIN64) -#if 1 -#define IGL_COMPATIBLE -#endif - -#ifdef IGL_COMPATIBLE #include -#endif +#include #include "SLASpatIndex.hpp" #include "ClipperUtils.hpp" @@ -84,33 +78,148 @@ size_t SpatIndex::size() const return m_impl->m_store.size(); } -PointSet normals(const PointSet& points, const EigenMesh3D& mesh) { - if(points.rows() == 0 || mesh.V.rows() == 0 || mesh.F.rows() == 0) return {}; -#ifdef IGL_COMPATIBLE +bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2, + double eps = 0.05) +{ + using Line3D = Eigen::ParametrizedLine; + + auto line = Line3D::Through(e1, e2); + double d = line.distance(p); + return std::abs(d) < eps; +} + +template double distance(const Vec& pp1, const Vec& pp2) { + auto p = pp2 - pp1; + return std::sqrt(p.transpose() * p); +} + +PointSet normals(const PointSet& points, const EigenMesh3D& emesh, + double eps, + std::function throw_on_cancel) { + if(points.rows() == 0 || emesh.V.rows() == 0 || emesh.F.rows() == 0) + return {}; + Eigen::VectorXd dists; Eigen::VectorXi I; PointSet C; + // We need to remove duplicate vertices and have a true index triangle + // structure + EigenMesh3D mesh; + Eigen::VectorXi SVI, SVJ; + static const double dEPS = 1e-6; + igl::remove_duplicate_vertices(emesh.V, emesh.F, dEPS, + mesh.V, SVI, SVJ, mesh.F); + igl::point_mesh_squared_distance( points, mesh.V, mesh.F, dists, I, C); PointSet ret(I.rows(), 3); for(int i = 0; i < I.rows(); i++) { + throw_on_cancel(); auto idx = I(i); auto trindex = mesh.F.row(idx); - auto& p1 = mesh.V.row(trindex(0)); - auto& p2 = mesh.V.row(trindex(1)); - auto& p3 = mesh.V.row(trindex(2)); + const Vec3d& p1 = mesh.V.row(trindex(0)); + const Vec3d& p2 = mesh.V.row(trindex(1)); + const Vec3d& p3 = mesh.V.row(trindex(2)); - Eigen::Vector3d U = p2 - p1; - Eigen::Vector3d V = p3 - p1; - ret.row(i) = U.cross(V).normalized(); + // We should check if the point lies on an edge of the hosting triangle. + // If it does than all the other triangles using the same two points + // have to be searched and the final normal should be some kind of + // aggregation of the participating triangle normals. We should also + // consider the cases where the support point lies right on a vertex + // of its triangle. The procedure is the same, get the neighbor + // triangles and calculate an average normal. + + const Vec3d& p = C.row(i); + + // mark the vertex indices of the edge. ia and ib marks and edge ic + // will mark a single vertex. + int ia = -1, ib = -1, ic = -1; + + if(std::abs(distance(p, p1)) < eps) { + ic = trindex(0); + } + else if(std::abs(distance(p, p2)) < eps) { + ic = trindex(1); + } + else if(std::abs(distance(p, p3)) < eps) { + ic = trindex(2); + } + else if(point_on_edge(p, p1, p2, eps)) { + ia = trindex(0); ib = trindex(1); + } + else if(point_on_edge(p, p2, p3, eps)) { + ia = trindex(1); ib = trindex(2); + } + else if(point_on_edge(p, p1, p3, eps)) { + ia = trindex(0); ib = trindex(2); + } + + // vector for the neigboring triangles including the detected one. + std::vector neigh; + if(ic >= 0) { // The point is right on a vertex of the triangle + for(int n = 0; n < mesh.F.rows(); ++n) { + throw_on_cancel(); + Vec3i ni = mesh.F.row(n); + if((ni(X) == ic || ni(Y) == ic || ni(Z) == ic)) + neigh.emplace_back(ni); + } + } + else if(ia >= 0 && ib >= 0) { // the point is on and edge + // now get all the neigboring triangles + for(int n = 0; n < mesh.F.rows(); ++n) { + throw_on_cancel(); + Vec3i ni = mesh.F.row(n); + if((ni(X) == ia || ni(Y) == ia || ni(Z) == ia) && + (ni(X) == ib || ni(Y) == ib || ni(Z) == ib)) + neigh.emplace_back(ni); + } + } + + // Calculate the normals for the neighboring triangles + std::vector neighnorms; neighnorms.reserve(neigh.size()); + for(const Vec3i& tri : neigh) { + const Vec3d& pt1 = mesh.V.row(tri(0)); + const Vec3d& pt2 = mesh.V.row(tri(1)); + const Vec3d& pt3 = mesh.V.row(tri(2)); + Eigen::Vector3d U = pt2 - pt1; + Eigen::Vector3d V = pt3 - pt1; + neighnorms.emplace_back(U.cross(V).normalized()); + } + + // Throw out duplicates. They would cause trouble with summing. We will + // use std::unique which works on sorted ranges. We will sort by the + // coefficient-wise sum of the normals. It should force the same + // elements to be consecutive. + std::sort(neighnorms.begin(), neighnorms.end(), + [](const Vec3d& v1, const Vec3d& v2){ + return v1.sum() < v2.sum(); + }); + + auto lend = std::unique(neighnorms.begin(), neighnorms.end(), + [](const Vec3d& n1, const Vec3d& n2) { + // Compare normals for equivalence. This is controvers stuff. + auto deq = [](double a, double b) { return std::abs(a-b) < 1e-3; }; + return deq(n1(X), n2(X)) && deq(n1(Y), n2(Y)) && deq(n1(Z), n2(Z)); + }); + + if(!neighnorms.empty()) { // there were neighbors to count with + // sum up the normals and then normalize the result again. + // This unification seems to be enough. + Vec3d sumnorm(0, 0, 0); + sumnorm = std::accumulate(neighnorms.begin(), lend, sumnorm); + sumnorm.normalize(); + ret.row(i) = sumnorm; + } + else { // point lies safely within its triangle + Eigen::Vector3d U = p2 - p1; + Eigen::Vector3d V = p3 - p1; + ret.row(i) = U.cross(V).normalized(); + } } return ret; -#else // TODO: do something on 32 bit windows - return {}; -#endif } double ray_mesh_intersect(const Vec3d& s, @@ -223,7 +332,7 @@ Segments model_boundary(const EigenMesh3D& emesh, double offs) pp.emplace_back(p); } - ExPolygons merged = union_ex(offset(pp, float(scale_(offs))), true); + ExPolygons merged = union_ex(Slic3r::offset(pp, float(scale_(offs))), true); for(auto& expoly : merged) { auto lines = expoly.lines(); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 90abe290f..f45d805b6 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1,6 +1,7 @@ #include "SLAPrint.hpp" #include "SLA/SLASupportTree.hpp" #include "SLA/SLABasePool.hpp" +#include "SLA/SLAAutoSupports.hpp" #include "MTUtils.hpp" #include @@ -36,8 +37,7 @@ namespace { const std::array OBJ_STEP_LEVELS = { 10, // slaposObjectSlice, - 10, // slaposSupportIslands, - 20, // slaposSupportPoints, + 30, // slaposSupportPoints, 25, // slaposSupportTree, 25, // slaposBasePool, 5, // slaposSliceSupports, @@ -47,8 +47,7 @@ const std::array OBJ_STEP_LEVELS = const std::array OBJ_STEP_LABELS = { L("Slicing model"), // slaposObjectSlice, - L("Generating islands"), // slaposSupportIslands, - L("Scanning model structure"), // slaposSupportPoints, + L("Generating support points"), // slaposSupportPoints, L("Generating support tree"), // slaposSupportTree, L("Generating base pool"), // slaposBasePool, L("Slicing supports"), // slaposSliceSupports, @@ -185,6 +184,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf update_apply_status(this->invalidate_all_steps()); for (SLAPrintObject *object : m_objects) { model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted); + update_apply_status(object->invalidate_all_steps()); delete object; } m_objects.clear(); @@ -377,11 +377,12 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf bool deleted_objects = false; for (auto &pos : print_object_status) if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) { - // update_apply_status(pos.print_object->invalidate_all_steps()); + update_apply_status(pos.print_object->invalidate_all_steps()); delete pos.print_object; deleted_objects = true; } - update_apply_status(new_objects); + if (new_objects) + update_apply_status(false); } this->update_object_placeholders(); @@ -420,12 +421,34 @@ void swapXY(ExPolygon& expoly) { } +std::vector SLAPrint::calculate_heights(const BoundingBoxf3& bb3d, + float elevation, + float initial_layer_height, + float layer_height) const +{ + std::vector heights; + float minZ = float(bb3d.min(Z)) - float(elevation); + float maxZ = float(bb3d.max(Z)); + auto flh = float(layer_height); + auto gnd = float(bb3d.min(Z)); + + // The first layer (the one before the initial height) is added only + // if there is no pad and no elevation value + if(minZ >= gnd) heights.emplace_back(minZ); + + for(float h = minZ + initial_layer_height; h < maxZ; h += flh) + if(h >= gnd) heights.emplace_back(h); + + return heights; +} + template void report_status(SLAPrint& p, int st, const std::string& msg, Args&&...args) { BOOST_LOG_TRIVIAL(info) << st << "% " << msg; p.set_status(st, msg, std::forward(args)...); } + void SLAPrint::process() { using namespace sla; @@ -462,47 +485,66 @@ void SLAPrint::process() TriangleMesh mesh = po.transformed_mesh(); TriangleMeshSlicer slicer(&mesh); - auto bb3d = mesh.bounding_box(); - - double elevation = po.get_elevation(); - - float minZ = float(bb3d.min(Z)) - float(elevation); - float maxZ = float(bb3d.max(Z)) ; - auto flh = float(lh); - auto gnd = float(bb3d.min(Z)); // The 1D grid heights - std::vector heights; - - // The first layer (the one before the initial height) is added only - // if there is no pad and no elevation value - if(minZ >= gnd) heights.emplace_back(minZ); - - for(float h = minZ + ilh; h < maxZ; h += flh) - if(h >= gnd) heights.emplace_back(h); + std::vector heights = calculate_heights(mesh.bounding_box(), + float(po.get_elevation()), + ilh, float(lh)); auto& layers = po.m_model_slices; layers.clear(); slicer.slice(heights, &layers, [this](){ throw_if_canceled(); }); }; - // this procedure simply converts the points and copies them into - // the support data cache - auto support_points = [](SLAPrintObject& po) { - ModelObject& mo = *po.m_model_object; + // In this step we check the slices, identify island and cover them with + // support points. Then we sprinkle the rest of the mesh. + auto support_points = [this, ilh](SLAPrintObject& po) { + const ModelObject& mo = *po.m_model_object; po.m_supportdata.reset(new SLAPrintObject::SupportData()); + po.m_supportdata->emesh = sla::to_eigenmesh(po.transformed_mesh()); - if(!mo.sla_support_points.empty()) { - po.m_supportdata->emesh = sla::to_eigenmesh(po.transformed_mesh()); + BOOST_LOG_TRIVIAL(debug) << "Support point count " + << mo.sla_support_points.size(); + + // If there are no points on the front-end, we will do the + // autoplacement. Otherwise we will just blindly copy the frontend data + // into the backend cache. + if(mo.sla_support_points.empty()) { + // calculate heights of slices (slices are calculated already) + double lh = po.m_config.layer_height.getFloat(); + + std::vector heights = + calculate_heights(po.transformed_mesh().bounding_box(), + float(po.get_elevation()), + ilh, float(lh)); + + this->throw_if_canceled(); + SLAAutoSupports::Config config; + const SLAPrintObjectConfig& cfg = po.config(); + config.minimal_z = float(cfg.support_minimal_z); + config.density_at_45 = cfg.support_density_at_45 / 10000.f; + config.density_at_horizontal = cfg.support_density_at_horizontal / 10000.f; + + // Construction of this object does the calculation. + this->throw_if_canceled(); + SLAAutoSupports auto_supports(po.transformed_mesh(), + po.m_supportdata->emesh, + po.get_model_slices(), + heights, + config, + [this]() { throw_if_canceled(); }); + + // Now let's extract the result. + const std::vector& points = auto_supports.output(); + this->throw_if_canceled(); + po.m_supportdata->support_points = sla::to_point_set(points); + + BOOST_LOG_TRIVIAL(debug) << "Automatic support points: " + << po.m_supportdata->support_points.rows(); + } + else { + // There are some points on the front-end, no calculation will be done. po.m_supportdata->support_points = sla::to_point_set(po.transformed_support_points()); - } else if(po.m_config.supports_enable.getBool()) { - // Supports are enabled but there are no support points to process. - // We throw here a runtime exception with some explanation and - // the background processing framework will handle it. - throw std::runtime_error( - L("Supports are enabled but no support points selected." - " Hint: create some support points or disable support " - "creation.")); } }; @@ -545,8 +587,18 @@ void SLAPrint::process() // Create the unified mesh auto rc = SlicingStatus::RELOAD_SCENE; + + // This is to prevent "Done." being displayed during merged_mesh() report_status(*this, -1, L("Visualizing supports")); po.m_supportdata->support_tree_ptr->merged_mesh(); + + BOOST_LOG_TRIVIAL(debug) << "Processed support point count " + << po.m_supportdata->support_points.rows(); + + // Check the mesh for later troubleshooting. + if(po.support_mesh().empty()) + BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty"; + report_status(*this, -1, L("Visualizing supports"), rc); } catch(sla::SLASupportsStoppedException&) { // no need to rethrow @@ -560,9 +612,13 @@ void SLAPrint::process() // and before the supports had been sliced. (or the slicing has to be // repeated) - if(po.m_config.pad_enable.getBool() && - po.m_supportdata && - po.m_supportdata->support_tree_ptr) + if(!po.m_supportdata || !po.m_supportdata->support_tree_ptr) { + BOOST_LOG_TRIVIAL(warning) << "Uninitialized support data at " + << "pad creation."; + return; + } + + if(po.m_config.pad_enable.getBool()) { double wt = po.m_config.pad_wall_thickness.getFloat(); double h = po.m_config.pad_wall_height.getFloat(); @@ -586,6 +642,8 @@ void SLAPrint::process() pcfg.throw_on_cancel = thrfn; po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg); + } else { + po.m_supportdata->support_tree_ptr->remove_pad(); } po.throw_if_canceled(); @@ -816,8 +874,7 @@ void SLAPrint::process() // This is the actual order of steps done on each PrintObject std::array objectsteps = { - slaposObjectSlice, // Support Islands will need this step - slaposSupportIslands, + slaposObjectSlice, // SupportPoints will need this step slaposSupportPoints, slaposSupportTree, slaposBasePool, @@ -828,7 +885,6 @@ void SLAPrint::process() std::array pobj_program = { slice_model, - [](SLAPrintObject&){}, // slaposSupportIslands now empty support_points, support_tree, base_pool, @@ -866,6 +922,7 @@ void SLAPrint::process() if(po->m_stepmask[currentstep] && po->set_started(currentstep)) { report_status(*this, int(st), OBJ_STEP_LABELS[currentstep]); pobj_program[currentstep](*po); + throw_if_canceled(); po->set_done(currentstep); } @@ -891,6 +948,7 @@ void SLAPrint::process() { report_status(*this, int(st), PRINT_STEP_LABELS[currentstep]); print_program[currentstep](); + throw_if_canceled(); set_done(currentstep); } @@ -1031,9 +1089,6 @@ bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step) // propagate to dependent steps if (step == slaposObjectSlice) { invalidated |= this->invalidate_all_steps(); - } else if (step == slaposSupportIslands) { - invalidated |= this->invalidate_steps({ slaposSupportPoints, slaposSupportTree, slaposBasePool, slaposSliceSupports, slaposIndexSlices }); - invalidated |= m_print->invalidate_step(slapsRasterize); } else if (step == slaposSupportPoints) { invalidated |= this->invalidate_steps({ slaposSupportTree, slaposBasePool, slaposSliceSupports, slaposIndexSlices }); invalidated |= m_print->invalidate_step(slapsRasterize); @@ -1083,12 +1138,13 @@ double SLAPrintObject::get_current_elevation() const bool se = m_config.supports_enable.getBool(); bool has_supports = is_step_done(slaposSupportTree); bool has_pad = is_step_done(slaposBasePool); - if(!has_supports && !has_pad) return 0; + + if(!has_supports && !has_pad) + return 0; else if(has_supports && !has_pad) return se ? m_config.support_object_elevation.getFloat() : 0; - else return get_elevation(); - return 0; + return get_elevation(); } namespace { // dummy empty static containers for return values in some methods @@ -1096,6 +1152,11 @@ const std::vector EMPTY_SLICES; const TriangleMesh EMPTY_MESH; } +const Eigen::MatrixXd& SLAPrintObject::get_support_points() const +{ + return m_supportdata->support_points; +} + const std::vector &SLAPrintObject::get_support_slices() const { // assert(is_step_done(slaposSliceSupports)); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 4a0876767..9fab4d550 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -18,7 +18,6 @@ enum SLAPrintStep : unsigned int { enum SLAPrintObjectStep : unsigned int { slaposObjectSlice, - slaposSupportIslands, slaposSupportPoints, slaposSupportTree, slaposBasePool, @@ -91,6 +90,9 @@ public: const std::vector& get_model_slices() const; const std::vector& get_support_slices() const; + // This method returns the support points of this SLAPrintObject. + const Eigen::MatrixXd& get_support_points() const; + // An index record referencing the slices // (get_model_slices(), get_support_slices()) where the keys are the height // levels of the model in scaled-clipper coordinates. The levels correspond @@ -188,7 +190,7 @@ public: // Returns true if an object step is done on all objects and there's at least one object. bool is_step_done(SLAPrintObjectStep step) const; // Returns true if the last step was finished with success. - bool finished() const override { return this->is_step_done(slaposIndexSlices); } + bool finished() const override { return this->is_step_done(slaposIndexSlices) && this->Inherited::is_step_done(slapsRasterize); } template void export_raster(const std::string& fname) { if(m_printer) m_printer->save(fname); @@ -226,6 +228,8 @@ private: lref(std::cref(lyr)), copies(std::cref(cp)) {} }; + std::vector calculate_heights(const BoundingBoxf3& bb, float elevation, float initial_layer_height, float layer_height) const; + // One level may contain multiple slices from multiple objects and their // supports using LayerRefs = std::vector; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index d604896ad..c32fe3e8d 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -10,36 +10,39 @@ // Log debug messages to console when changing selection #define ENABLE_SELECTION_DEBUG_OUTPUT 0 -//============= -// 1.42.0 techs -//============= -#define ENABLE_1_42_0 1 +//==================== +// 1.42.0.alpha1 techs +//==================== +#define ENABLE_1_42_0_ALPHA1 1 // Uses a unique opengl context -#define ENABLE_USE_UNIQUE_GLCONTEXT (1 && ENABLE_1_42_0) +#define ENABLE_USE_UNIQUE_GLCONTEXT (1 && ENABLE_1_42_0_ALPHA1) // Disable synchronization of unselected instances -#define DISABLE_INSTANCES_SYNCH (0 && ENABLE_1_42_0) -// Modified camera target behavior -#define ENABLE_MODIFIED_CAMERA_TARGET (1 && ENABLE_1_42_0) -// Add Geometry::Transformation class and use it into ModelInstance, ModelVolume and GLVolume -#define ENABLE_MODELVOLUME_TRANSFORM (1 && ENABLE_1_42_0) +#define DISABLE_INSTANCES_SYNCH (0 && ENABLE_1_42_0_ALPHA1) // Keeps objects on bed while scaling them using the scale gizmo -#define ENABLE_ENSURE_ON_BED_WHILE_SCALING (1 && ENABLE_MODELVOLUME_TRANSFORM) +#define ENABLE_ENSURE_ON_BED_WHILE_SCALING (1 && ENABLE_1_42_0_ALPHA1) // All rotations made using the rotate gizmo are done with respect to the world reference system -#define ENABLE_WORLD_ROTATIONS (1 && ENABLE_1_42_0) +#define ENABLE_WORLD_ROTATIONS (1 && ENABLE_1_42_0_ALPHA1) // Scene's GUI made using imgui library -#define ENABLE_IMGUI (1 && ENABLE_1_42_0) +#define ENABLE_IMGUI (1 && ENABLE_1_42_0_ALPHA1) #define DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI (1 && ENABLE_IMGUI) // Modified Sla support gizmo -#define ENABLE_SLA_SUPPORT_GIZMO_MOD (1 && ENABLE_1_42_0) -// Removes the wxNotebook from plater -#define ENABLE_REMOVE_TABS_FROM_PLATER (1 && ENABLE_1_42_0) -// Constrains the camera target into the scene bounding box -#define ENABLE_CONSTRAINED_CAMERA_TARGET (1 && ENABLE_1_42_0) +#define ENABLE_SLA_SUPPORT_GIZMO_MOD (1 && ENABLE_1_42_0_ALPHA1) // Use wxDataViewRender instead of wxDataViewCustomRenderer -#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0) -// Adds background texture to toolbars -#define ENABLE_TOOLBAR_BACKGROUND_TEXTURE (1 && ENABLE_1_42_0) +#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1) +// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active +#define ENABLE_RENDER_SELECTION_CENTER (0 && ENABLE_1_42_0_ALPHA1) +// Show visual hints in the 3D scene when sidebar matrix fields have focus +#define ENABLE_SIDEBAR_VISUAL_HINTS (1 && ENABLE_1_42_0_ALPHA1) +// Separate rendering for opaque and transparent volumes +#define ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_1_42_0_ALPHA1) + +//==================== +// 1.42.0.alpha2 techs +//==================== +#define ENABLE_1_42_0_ALPHA2 1 + +#define ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION (1 && ENABLE_1_42_0_ALPHA2) #endif // _technologies_h_ diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 7f2c94f03..cfae9edd1 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -11,7 +11,13 @@ namespace Slic3r { extern void set_logging_level(unsigned int level); +extern unsigned get_logging_level(); extern void trace(unsigned int level, const char *message); +// Format memory allocated, separate thousands by comma. +extern std::string format_memsize_MB(size_t n); +// Return string to be added to the boost::log output to inform about the current process memory allocation. +// The string is non-empty only if the loglevel >= info (3). +extern std::string log_memory_info(); extern void disable_multi_threading(); // Set a path with GUI resource files. @@ -182,7 +188,12 @@ public: void reset() { closure = Closure(); } }; - } // namespace Slic3r +#if WIN32 + #define SLIC3R_STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + __alignof(TYPE) - 1) / __alignof(TYPE)) * __alignof(TYPE) +#else + #define SLIC3R_STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + alignof(TYPE) - 1) / alignof(TYPE)) * alignof(TYPE) +#endif + #endif // slic3r_Utils_hpp_ diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 7a51a6104..f48abfd89 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -8,6 +8,7 @@ #ifdef WIN32 #include +#include #else #include #endif @@ -58,6 +59,18 @@ void set_logging_level(unsigned int level) ); } +unsigned get_logging_level() +{ + switch (logSeverity) { + case boost::log::trivial::fatal : return 0; + case boost::log::trivial::error : return 1; + case boost::log::trivial::warning : return 2; + case boost::log::trivial::info : return 3; + case boost::log::trivial::debug : return 4; + default: return 1; + } +} + // Force set_logging_level(<=error) after loading of the DLL. // Switch boost::filesystem to utf8. static struct RunOnInit { @@ -365,4 +378,71 @@ std::string xml_escape(std::string text) return text; } +std::string format_memsize_MB(size_t n) +{ + std::string out; + size_t n2 = 0; + size_t scale = 1; + // Round to MB + n += 500000; + n /= 1000000; + while (n >= 1000) { + n2 = n2 + scale * (n % 1000); + n /= 1000; + scale *= 1000; + } + char buf[8]; + sprintf(buf, "%d", n); + out = buf; + while (scale != 1) { + scale /= 1000; + n = n2 / scale; + n2 = n2 % scale; + sprintf(buf, ",%03d", n); + out += buf; + } + return out + "MB"; +} + +#ifdef WIN32 + +#ifndef PROCESS_MEMORY_COUNTERS_EX + // MingW32 doesn't have this struct in psapi.h + typedef struct _PROCESS_MEMORY_COUNTERS_EX { + DWORD cb; + DWORD PageFaultCount; + SIZE_T PeakWorkingSetSize; + SIZE_T WorkingSetSize; + SIZE_T QuotaPeakPagedPoolUsage; + SIZE_T QuotaPagedPoolUsage; + SIZE_T QuotaPeakNonPagedPoolUsage; + SIZE_T QuotaNonPagedPoolUsage; + SIZE_T PagefileUsage; + SIZE_T PeakPagefileUsage; + SIZE_T PrivateUsage; + } PROCESS_MEMORY_COUNTERS_EX, *PPROCESS_MEMORY_COUNTERS_EX; +#endif /* PROCESS_MEMORY_COUNTERS_EX */ + +std::string log_memory_info() +{ + std::string out; + if (logSeverity <= boost::log::trivial::info) { + HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ::GetCurrentProcessId()); + if (hProcess != nullptr) { + PROCESS_MEMORY_COUNTERS_EX pmc; + if (GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) + out = " WorkingSet: " + format_memsize_MB(pmc.WorkingSetSize) + " PrivateBytes: " + format_memsize_MB(pmc.PrivateUsage) + " Pagefile(peak): " + format_memsize_MB(pmc.PagefileUsage) + "(" + format_memsize_MB(pmc.PeakPagefileUsage) + ")"; + CloseHandle(hProcess); + } + } + return out; +} + +#else +std::string log_memory_info() +{ + return std::string(); +} +#endif + }; // namespace Slic3r diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 3b6bad16d..d79421439 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -10,6 +10,8 @@ add_library(libslic3r_gui STATIC GUI/AboutDialog.hpp GUI/SysInfoDialog.cpp GUI/SysInfoDialog.hpp + GUI/KBShortcutsDialog.cpp + GUI/KBShortcutsDialog.hpp GUI/AppConfig.cpp GUI/AppConfig.hpp GUI/BackgroundSlicingProcess.cpp diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 09c3443c3..42b025093 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -205,17 +205,7 @@ const float GLVolume::SLA_SUPPORT_COLOR[4] = { 0.75f, 0.75f, 0.75f, 1.0f }; const float GLVolume::SLA_PAD_COLOR[4] = { 0.0f, 0.2f, 0.0f, 1.0f }; GLVolume::GLVolume(float r, float g, float b, float a) -#if ENABLE_MODELVOLUME_TRANSFORM : m_transformed_bounding_box_dirty(true) -#else - : m_offset(Vec3d::Zero()) - , m_rotation(Vec3d::Zero()) - , m_scaling_factor(Vec3d::Ones()) - , m_mirror(Vec3d::Ones()) - , m_world_matrix(Transform3f::Identity()) - , m_world_matrix_dirty(true) - , m_transformed_bounding_box_dirty(true) -#endif // ENABLE_MODELVOLUME_TRANSFORM , m_sla_shift_z(0.0) , m_transformed_convex_hull_bounding_box_dirty(true) , m_convex_hull(nullptr) @@ -300,125 +290,18 @@ void GLVolume::set_color_from_model_volume(const ModelVolume *model_volume) color[3] = model_volume->is_model_part() ? 1.f : 0.5f; } -#if !ENABLE_MODELVOLUME_TRANSFORM -const Vec3d& GLVolume::get_rotation() const -{ - return m_rotation; -} - -void GLVolume::set_rotation(const Vec3d& rotation) -{ - static const double TWO_PI = 2.0 * (double)PI; - - if (m_rotation != rotation) - { - m_rotation = rotation; - for (int i = 0; i < 3; ++i) - { - while (m_rotation(i) < 0.0) - { - m_rotation(i) += TWO_PI; - } - while (TWO_PI < m_rotation(i)) - { - m_rotation(i) -= TWO_PI; - } - } - m_world_matrix_dirty = true; - m_transformed_bounding_box_dirty = true; - m_transformed_convex_hull_bounding_box_dirty = true; - } -} - -const Vec3d& GLVolume::get_offset() const -{ - return m_offset; -} - -void GLVolume::set_offset(const Vec3d& offset) -{ - if (m_offset != offset) - { - m_offset = offset; - m_world_matrix_dirty = true; - m_transformed_bounding_box_dirty = true; - m_transformed_convex_hull_bounding_box_dirty = true; - } -} - -const Vec3d& GLVolume::get_scaling_factor() const -{ - return m_scaling_factor; -} - -void GLVolume::set_scaling_factor(const Vec3d& scaling_factor) -{ - if (m_scaling_factor != scaling_factor) - { - m_scaling_factor = scaling_factor; - m_world_matrix_dirty = true; - m_transformed_bounding_box_dirty = true; - m_transformed_convex_hull_bounding_box_dirty = true; - } -} - -const Vec3d& GLVolume::get_mirror() const -{ - return m_mirror; -} - -double GLVolume::get_mirror(Axis axis) const -{ - return m_mirror(axis); -} - -void GLVolume::set_mirror(const Vec3d& mirror) -{ - if (m_mirror != mirror) - { - m_mirror = mirror; - m_world_matrix_dirty = true; - m_transformed_bounding_box_dirty = true; - m_transformed_convex_hull_bounding_box_dirty = true; - } -} - -void GLVolume::set_mirror(Axis axis, double mirror) -{ - if (m_mirror(axis) != mirror) - { - m_mirror(axis) = mirror; - m_world_matrix_dirty = true; - m_transformed_bounding_box_dirty = true; - m_transformed_convex_hull_bounding_box_dirty = true; - } -} -#endif // !ENABLE_MODELVOLUME_TRANSFORM - void GLVolume::set_convex_hull(const TriangleMesh *convex_hull, bool owned) { m_convex_hull = convex_hull; m_convex_hull_owned = owned; } -#if ENABLE_MODELVOLUME_TRANSFORM Transform3d GLVolume::world_matrix() const { Transform3d m = m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); m.translation()(2) += m_sla_shift_z; return m; } -#else -const Transform3f& GLVolume::world_matrix() const -{ - if (m_world_matrix_dirty) - { - m_world_matrix = Geometry::assemble_transform(m_offset, m_rotation, m_scaling_factor, m_mirror).cast(); - m_world_matrix_dirty = false; - } - return m_world_matrix; -} -#endif // ENABLE_MODELVOLUME_TRANSFORM const BoundingBoxf3& GLVolume::transformed_bounding_box() const { @@ -426,11 +309,7 @@ const BoundingBoxf3& GLVolume::transformed_bounding_box() const if (m_transformed_bounding_box_dirty) { -#if ENABLE_MODELVOLUME_TRANSFORM m_transformed_bounding_box = bounding_box.transformed(world_matrix()); -#else - m_transformed_bounding_box = bounding_box.transformed(world_matrix().cast()); -#endif // ENABLE_MODELVOLUME_TRANSFORM m_transformed_bounding_box_dirty = false; } @@ -441,17 +320,10 @@ const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const { if (m_transformed_convex_hull_bounding_box_dirty) { -#if ENABLE_MODELVOLUME_TRANSFORM if ((m_convex_hull != nullptr) && (m_convex_hull->stl.stats.number_of_facets > 0)) m_transformed_convex_hull_bounding_box = m_convex_hull->transformed_bounding_box(world_matrix()); else m_transformed_convex_hull_bounding_box = bounding_box.transformed(world_matrix()); -#else - if ((m_convex_hull != nullptr) && (m_convex_hull->stl.stats.number_of_facets > 0)) - m_transformed_convex_hull_bounding_box = m_convex_hull->transformed_bounding_box(world_matrix().cast()); - else - m_transformed_convex_hull_bounding_box = bounding_box.transformed(world_matrix().cast()); -#endif // ENABLE_MODELVOLUME_TRANSFORM m_transformed_convex_hull_bounding_box_dirty = false; } @@ -502,11 +374,7 @@ void GLVolume::render() const ::glCullFace(GL_BACK); ::glPushMatrix(); -#if ENABLE_MODELVOLUME_TRANSFORM ::glMultMatrixd(world_matrix().data()); -#else - ::glMultMatrixf(world_matrix().data()); -#endif // ENABLE_MODELVOLUME_TRANSFORM if (this->indexed_vertex_array.indexed()) this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); else @@ -544,11 +412,7 @@ void GLVolume::render_using_layer_height() const glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width); if (world_matrix_id >= 0) -#if ENABLE_MODELVOLUME_TRANSFORM ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast().data()); -#else - ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); -#endif // ENABLE_MODELVOLUME_TRANSFORM GLsizei w = (GLsizei)layer_height_texture_width(); GLsizei h = (GLsizei)layer_height_texture_height(); @@ -608,11 +472,7 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0); if (worldmatrix_id != -1) -#if ENABLE_MODELVOLUME_TRANSFORM ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast().data()); -#else - ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); -#endif // ENABLE_MODELVOLUME_TRANSFORM render(); @@ -631,11 +491,7 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0); if (worldmatrix_id != -1) -#if ENABLE_MODELVOLUME_TRANSFORM ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast().data()); -#else - ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); -#endif // ENABLE_MODELVOLUME_TRANSFORM ::glBindBuffer(GL_ARRAY_BUFFER, indexed_vertex_array.vertices_and_normals_interleaved_VBO_id); ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))); @@ -643,11 +499,7 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c ::glPushMatrix(); -#if ENABLE_MODELVOLUME_TRANSFORM ::glMultMatrixd(world_matrix().data()); -#else - ::glMultMatrixf(world_matrix().data()); -#endif // ENABLE_MODELVOLUME_TRANSFORM if (n_triangles > 0) { @@ -691,11 +543,7 @@ void GLVolume::render_legacy() const ::glPushMatrix(); -#if ENABLE_MODELVOLUME_TRANSFORM ::glMultMatrixd(world_matrix().data()); -#else - ::glMultMatrixf(world_matrix().data()); -#endif // ENABLE_MODELVOLUME_TRANSFORM if (n_triangles > 0) ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first); @@ -780,11 +628,7 @@ int GLVolumeCollection::load_object_volume( const ModelVolume *model_volume = model_object->volumes[volume_idx]; const int extruder_id = model_volume->extruder_id(); const ModelInstance *instance = model_object->instances[instance_idx]; -#if ENABLE_MODELVOLUME_TRANSFORM const TriangleMesh& mesh = model_volume->mesh; -#else - TriangleMesh mesh = model_volume->mesh; -#endif // ENABLE_MODELVOLUME_TRANSFORM float color[4]; memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); /* if (model_volume->is_support_blocker()) { @@ -797,6 +641,9 @@ int GLVolumeCollection::load_object_volume( color[2] = 1.0f; } color[3] = model_volume->is_model_part() ? 1.f : 0.5f; */ +#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + color[3] = model_volume->is_model_part() ? 1.f : 0.5f; +#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING this->volumes.emplace_back(new GLVolume(color)); GLVolume &v = *this->volumes.back(); v.set_color_from_model_volume(model_volume); @@ -819,15 +666,8 @@ int GLVolumeCollection::load_object_volume( } v.is_modifier = ! model_volume->is_model_part(); v.shader_outside_printer_detection_enabled = model_volume->is_model_part(); -#if ENABLE_MODELVOLUME_TRANSFORM v.set_instance_transformation(instance->get_transformation()); v.set_volume_transformation(model_volume->get_transformation()); -#else - v.set_offset(instance->get_offset()); - v.set_rotation(instance->get_rotation()); - v.set_scaling_factor(instance->get_scaling_factor()); - v.set_mirror(instance->get_mirror()); -#endif // ENABLE_MODELVOLUME_TRANSFORM return int(this->volumes.size() - 1); } @@ -938,11 +778,7 @@ int GLVolumeCollection::load_wipe_tower_preview( else v.indexed_vertex_array.load_mesh_flat_shading(mesh); -#if ENABLE_MODELVOLUME_TRANSFORM v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); -#else - v.set_offset(Vec3d(pos_x, pos_y, 0.0)); -#endif // ENABLE_MODELVOLUME_TRANSFORM // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). v.bounding_box = v.indexed_vertex_array.bounding_box(); @@ -953,12 +789,54 @@ int GLVolumeCollection::load_wipe_tower_preview( return int(this->volumes.size() - 1); } +#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING +typedef std::pair GLVolumeWithZ; +typedef std::vector GLVolumesWithZList; +GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type) +{ + GLVolumesWithZList list; + + for (GLVolume* volume : volumes) + { + bool is_transparent = (volume->render_color[3] < 1.0f); + if (((type == GLVolumeCollection::Opaque) && !is_transparent) || + ((type == GLVolumeCollection::Transparent) && is_transparent) || + (type == GLVolumeCollection::All)) + list.push_back(std::make_pair(volume, 0.0)); + } + + if ((type == GLVolumeCollection::Transparent) && (list.size() > 1)) + { + Transform3d modelview_matrix; + ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data()); + + for (GLVolumeWithZ& volume : list) + { + volume.second = volume.first->bounding_box.transformed(modelview_matrix * volume.first->world_matrix()).max(2); + } + + std::sort(list.begin(), list.end(), + [](const GLVolumeWithZ& v1, const GLVolumeWithZ& v2) -> bool { return v1.second < v2.second; } + ); + } + + return list; +} + +void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool disable_cullface) const +#else void GLVolumeCollection::render_VBOs() const +#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING { ::glEnable(GL_BLEND); ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); ::glCullFace(GL_BACK); +#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + if (disable_cullface) + ::glDisable(GL_CULL_FACE); +#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + ::glEnableClientState(GL_VERTEX_ARRAY); ::glEnableClientState(GL_NORMAL_ARRAY); @@ -980,6 +858,18 @@ void GLVolumeCollection::render_VBOs() const if (z_range_id != -1) ::glUniform2fv(z_range_id, 1, (const GLfloat*)z_range); +#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + GLVolumesWithZList to_render = volumes_to_render(this->volumes, type); + for (GLVolumeWithZ& volume : to_render) + { + if (volume.first->layer_height_texture_data.can_use()) + volume.first->generate_layer_height_texture(volume.first->layer_height_texture_data.print_object, false); + else + volume.first->set_render_color(); + + volume.first->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id); + } +#else for (GLVolume *volume : this->volumes) { if (volume->layer_height_texture_data.can_use()) @@ -989,6 +879,7 @@ void GLVolumeCollection::render_VBOs() const volume->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id); } +#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING ::glBindBuffer(GL_ARRAY_BUFFER, 0); ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); @@ -996,27 +887,55 @@ void GLVolumeCollection::render_VBOs() const ::glDisableClientState(GL_VERTEX_ARRAY); ::glDisableClientState(GL_NORMAL_ARRAY); +#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + if (disable_cullface) + ::glEnable(GL_CULL_FACE); +#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + ::glDisable(GL_BLEND); } +#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING +void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface) const +#else void GLVolumeCollection::render_legacy() const +#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glCullFace(GL_BACK); +#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + if (disable_cullface) + ::glDisable(GL_CULL_FACE); +#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); +#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + GLVolumesWithZList to_render = volumes_to_render(this->volumes, type); + for (GLVolumeWithZ& volume : to_render) + { + volume.first->set_render_color(); + volume.first->render_legacy(); + } +#else for (GLVolume *volume : this->volumes) { volume->set_render_color(); volume->render_legacy(); } +#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); +#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + if (disable_cullface) + ::glEnable(GL_CULL_FACE); +#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + glDisable(GL_BLEND); } @@ -1868,6 +1787,290 @@ void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height GUI::GLCanvas3DManager _3DScene::s_canvas_mgr; +#if ENABLE_SIDEBAR_VISUAL_HINTS +GLModel::GLModel() + : m_useVBOs(false) +{ + m_volume.shader_outside_printer_detection_enabled = false; +} + +GLModel::~GLModel() +{ + m_volume.release_geometry(); +} + +void GLModel::set_color(const float* color, unsigned int size) +{ + m_volume.set_render_color(color, size); +} + +const Vec3d& GLModel::get_offset() const +{ + return m_volume.get_volume_offset(); +} + +void GLModel::set_offset(const Vec3d& offset) +{ + m_volume.set_volume_offset(offset); +} + +const Vec3d& GLModel::get_rotation() const +{ + return m_volume.get_volume_rotation(); +} + +void GLModel::set_rotation(const Vec3d& rotation) +{ + m_volume.set_volume_rotation(rotation); +} + +const Vec3d& GLModel::get_scale() const +{ + return m_volume.get_volume_scaling_factor(); +} + +void GLModel::set_scale(const Vec3d& scale) +{ + m_volume.set_volume_scaling_factor(scale); +} + +void GLModel::render() const +{ + if (m_useVBOs) + render_VBOs(); + else + render_legacy(); +} + +void GLModel::render_VBOs() const +{ + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + ::glCullFace(GL_BACK); + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_NORMAL_ARRAY); + + GLint current_program_id; + ::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id); + GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1; + GLint print_box_detection_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1; + + m_volume.render_VBOs(color_id, print_box_detection_id, -1); + + ::glBindBuffer(GL_ARRAY_BUFFER, 0); + ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + ::glDisableClientState(GL_VERTEX_ARRAY); + ::glDisableClientState(GL_NORMAL_ARRAY); + + ::glDisable(GL_BLEND); +} + +void GLModel::render_legacy() const +{ + ::glEnable(GL_LIGHTING); + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + ::glCullFace(GL_BACK); + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_NORMAL_ARRAY); + + m_volume.render_legacy(); + + ::glDisableClientState(GL_VERTEX_ARRAY); + ::glDisableClientState(GL_NORMAL_ARRAY); + + ::glDisable(GL_BLEND); + ::glDisable(GL_LIGHTING); +} + +bool GLArrow::on_init(bool useVBOs) +{ + Pointf3s vertices; + std::vector triangles; + + // bottom face + vertices.emplace_back(0.5, 0.0, -0.1); + vertices.emplace_back(0.5, 2.0, -0.1); + vertices.emplace_back(1.0, 2.0, -0.1); + vertices.emplace_back(0.0, 3.0, -0.1); + vertices.emplace_back(-1.0, 2.0, -0.1); + vertices.emplace_back(-0.5, 2.0, -0.1); + vertices.emplace_back(-0.5, 0.0, -0.1); + + // top face + vertices.emplace_back(0.5, 0.0, 0.1); + vertices.emplace_back(0.5, 2.0, 0.1); + vertices.emplace_back(1.0, 2.0, 0.1); + vertices.emplace_back(0.0, 3.0, 0.1); + vertices.emplace_back(-1.0, 2.0, 0.1); + vertices.emplace_back(-0.5, 2.0, 0.1); + vertices.emplace_back(-0.5, 0.0, 0.1); + + // bottom face + triangles.emplace_back(0, 6, 1); + triangles.emplace_back(6, 5, 1); + triangles.emplace_back(5, 4, 3); + triangles.emplace_back(5, 3, 1); + triangles.emplace_back(1, 3, 2); + + // top face + triangles.emplace_back(7, 8, 13); + triangles.emplace_back(13, 8, 12); + triangles.emplace_back(12, 10, 11); + triangles.emplace_back(8, 10, 12); + triangles.emplace_back(8, 9, 10); + + // side face + triangles.emplace_back(0, 1, 8); + triangles.emplace_back(8, 7, 0); + triangles.emplace_back(1, 2, 9); + triangles.emplace_back(9, 8, 1); + triangles.emplace_back(2, 3, 10); + triangles.emplace_back(10, 9, 2); + triangles.emplace_back(3, 4, 11); + triangles.emplace_back(11, 10, 3); + triangles.emplace_back(4, 5, 12); + triangles.emplace_back(12, 11, 4); + triangles.emplace_back(5, 6, 13); + triangles.emplace_back(13, 12, 5); + triangles.emplace_back(6, 0, 7); + triangles.emplace_back(7, 13, 6); + + m_useVBOs = useVBOs; + + if (m_useVBOs) + m_volume.indexed_vertex_array.load_mesh_full_shading(TriangleMesh(vertices, triangles)); + else + m_volume.indexed_vertex_array.load_mesh_flat_shading(TriangleMesh(vertices, triangles)); + + m_volume.finalize_geometry(m_useVBOs); + return true; +} + +GLCurvedArrow::GLCurvedArrow(unsigned int resolution) + : GLModel() + , m_resolution(resolution) +{ + if (m_resolution == 0) + m_resolution = 1; +} + +bool GLCurvedArrow::on_init(bool useVBOs) +{ + Pointf3s vertices; + std::vector triangles; + + double ext_radius = 2.5; + double int_radius = 1.5; + double step = 0.5 * (double)PI / (double)m_resolution; + + unsigned int vertices_per_level = 4 + 2 * m_resolution; + + // bottom face + vertices.emplace_back(0.0, 1.5, -0.1); + vertices.emplace_back(0.0, 1.0, -0.1); + vertices.emplace_back(-1.0, 2.0, -0.1); + vertices.emplace_back(0.0, 3.0, -0.1); + vertices.emplace_back(0.0, 2.5, -0.1); + + for (unsigned int i = 1; i <= m_resolution; ++i) + { + double angle = (double)i * step; + double x = ext_radius * ::sin(angle); + double y = ext_radius * ::cos(angle); + + vertices.emplace_back(x, y, -0.1); + } + + for (unsigned int i = 0; i < m_resolution; ++i) + { + double angle = (double)i * step; + double x = int_radius * ::cos(angle); + double y = int_radius * ::sin(angle); + + vertices.emplace_back(x, y, -0.1); + } + + // top face + vertices.emplace_back(0.0, 1.5, 0.1); + vertices.emplace_back(0.0, 1.0, 0.1); + vertices.emplace_back(-1.0, 2.0, 0.1); + vertices.emplace_back(0.0, 3.0, 0.1); + vertices.emplace_back(0.0, 2.5, 0.1); + + for (unsigned int i = 1; i <= m_resolution; ++i) + { + double angle = (double)i * step; + double x = ext_radius * ::sin(angle); + double y = ext_radius * ::cos(angle); + + vertices.emplace_back(x, y, 0.1); + } + + for (unsigned int i = 0; i < m_resolution; ++i) + { + double angle = (double)i * step; + double x = int_radius * ::cos(angle); + double y = int_radius * ::sin(angle); + + vertices.emplace_back(x, y, 0.1); + } + + // bottom face + triangles.emplace_back(0, 1, 2); + triangles.emplace_back(0, 2, 4); + triangles.emplace_back(4, 2, 3); + + int first_id = 4; + int last_id = (int)vertices_per_level; + triangles.emplace_back(last_id, 0, first_id); + triangles.emplace_back(last_id, first_id, first_id + 1); + for (unsigned int i = 1; i < m_resolution; ++i) + { + triangles.emplace_back(last_id - i, last_id - i + 1, first_id + i); + triangles.emplace_back(last_id - i, first_id + i, first_id + i + 1); + } + + // top face + last_id += 1; + triangles.emplace_back(last_id + 0, last_id + 2, last_id + 1); + triangles.emplace_back(last_id + 0, last_id + 4, last_id + 2); + triangles.emplace_back(last_id + 4, last_id + 3, last_id + 2); + + first_id = last_id + 4; + last_id = last_id + 4 + 2 * (int)m_resolution; + triangles.emplace_back(last_id, first_id, (int)vertices_per_level + 1); + triangles.emplace_back(last_id, first_id + 1, first_id); + for (unsigned int i = 1; i < m_resolution; ++i) + { + triangles.emplace_back(last_id - i, first_id + i, last_id - i + 1); + triangles.emplace_back(last_id - i, first_id + i + 1, first_id + i); + } + + // side face + for (unsigned int i = 0; i < 4 + 2 * (unsigned int)m_resolution; ++i) + { + triangles.emplace_back(i, vertices_per_level + 2 + i, i + 1); + triangles.emplace_back(i, vertices_per_level + 1 + i, vertices_per_level + 2 + i); + } + triangles.emplace_back(vertices_per_level, vertices_per_level + 1, 0); + triangles.emplace_back(vertices_per_level, 2 * vertices_per_level + 1, vertices_per_level + 1); + + m_useVBOs = useVBOs; + + if (m_useVBOs) + m_volume.indexed_vertex_array.load_mesh_full_shading(TriangleMesh(vertices, triangles)); + else + m_volume.indexed_vertex_array.load_mesh_flat_shading(TriangleMesh(vertices, triangles)); + + m_volume.finalize_geometry(m_useVBOs); + return true; +} +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + std::string _3DScene::get_gl_info(bool format_as_html, bool extensions) { return s_canvas_mgr.get_gl_info(format_as_html, extensions); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 97b0310e5..38996dc04 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -18,7 +18,6 @@ class SLAPrintObject; enum SLAPrintObjectStep : unsigned int; class Model; class ModelObject; -class GCodePreviewData; class DynamicPrintConfig; class ExtrusionPath; class ExtrusionMultiPath; @@ -258,23 +257,9 @@ public: ~GLVolume(); private: -#if ENABLE_MODELVOLUME_TRANSFORM Geometry::Transformation m_instance_transformation; Geometry::Transformation m_volume_transformation; -#else - // Offset of the volume to be rendered. - Vec3d m_offset; - // Rotation around three axes of the volume to be rendered. - Vec3d m_rotation; - // Scale factor along the three axes of the volume to be rendered. - Vec3d m_scaling_factor; - // Mirroring along the three axes of the volume to be rendered. - Vec3d m_mirror; - // World matrix of the volume to be rendered. - mutable Transform3f m_world_matrix; - // Whether or not is needed to recalculate the world matrix. - mutable bool m_world_matrix_dirty; -#endif // ENABLE_MODELVOLUME_TRANSFORM + // Shift in z required by sla supports+pad double m_sla_shift_z; // Bounding box of this volume, in unscaled coordinates. @@ -358,7 +343,6 @@ public: // set color according to model volume void set_color_from_model_volume(const ModelVolume *model_volume); -#if ENABLE_MODELVOLUME_TRANSFORM const Geometry::Transformation& get_instance_transformation() const { return m_instance_transformation; } void set_instance_transformation(const Geometry::Transformation& transformation) { m_instance_transformation = transformation; set_bounding_boxes_as_dirty(); } @@ -412,21 +396,6 @@ public: void set_volume_mirror(const Vec3d& mirror) { m_volume_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); } void set_volume_mirror(Axis axis, double mirror) { m_volume_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); } -#else - const Vec3d& get_rotation() const; - void set_rotation(const Vec3d& rotation); - - const Vec3d& get_scaling_factor() const; - void set_scaling_factor(const Vec3d& scaling_factor); - - const Vec3d& get_mirror() const; - double get_mirror(Axis axis) const; - void set_mirror(const Vec3d& mirror); - void set_mirror(Axis axis, double mirror); - - const Vec3d& get_offset() const; - void set_offset(const Vec3d& offset); -#endif // ENABLE_MODELVOLUME_TRANSFORM double get_sla_shift_z() const { return m_sla_shift_z; } void set_sla_shift_z(double z) { m_sla_shift_z = z; } @@ -437,11 +406,8 @@ public: int volume_idx() const { return this->composite_id.volume_id; } int instance_idx() const { return this->composite_id.instance_id; } -#if ENABLE_MODELVOLUME_TRANSFORM Transform3d world_matrix() const; -#else - const Transform3f& world_matrix() const; -#endif // ENABLE_MODELVOLUME_TRANSFORM + const BoundingBoxf3& transformed_bounding_box() const; const BoundingBoxf3& transformed_convex_hull_bounding_box() const; @@ -492,15 +458,24 @@ public: void reset_layer_height_texture_data() { layer_height_texture_data.reset(); } -#if ENABLE_MODELVOLUME_TRANSFORM void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; } -#endif // ENABLE_MODELVOLUME_TRANSFORM }; typedef std::vector GLVolumePtrs; class GLVolumeCollection { +#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING +public: + enum ERenderType : unsigned char + { + Opaque, + Transparent, + All + }; + +private: +#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING // min and max vertex of the print box volume float print_box_min[3]; float print_box_max[3]; @@ -545,8 +520,13 @@ public: int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width); // Render the volumes by OpenGL. +#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + void render_VBOs(ERenderType type, bool disable_cullface) const; + void render_legacy(ERenderType type, bool disable_cullface) const; +#else void render_VBOs() const; void render_legacy() const; +#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING // Finalize the initialization of the geometry & indices, // upload the geometry and indices to OpenGL VBO objects @@ -583,6 +563,56 @@ private: GLVolumeCollection& operator=(const GLVolumeCollection &); }; +#if ENABLE_SIDEBAR_VISUAL_HINTS +class GLModel +{ +protected: + GLVolume m_volume; + bool m_useVBOs; + +public: + GLModel(); + virtual ~GLModel(); + + bool init(bool useVBOs) { return on_init(useVBOs); } + + void set_color(const float* color, unsigned int size); + + const Vec3d& get_offset() const; + void set_offset(const Vec3d& offset); + const Vec3d& get_rotation() const; + void set_rotation(const Vec3d& rotation); + const Vec3d& get_scale() const; + void set_scale(const Vec3d& scale); + + void render() const; + +protected: + virtual bool on_init(bool useVBOs) = 0; + +private: + void render_VBOs() const; + void render_legacy() const; +}; + +class GLArrow : public GLModel +{ +protected: + virtual bool on_init(bool useVBOs); +}; + +class GLCurvedArrow : public GLModel +{ + unsigned int m_resolution; + +public: + explicit GLCurvedArrow(unsigned int resolution); + +protected: + virtual bool on_init(bool useVBOs); +}; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + class _3DScene { static GUI::GLCanvas3DManager s_canvas_mgr; diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 891e5f0dc..44ac1bc71 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -15,8 +15,8 @@ #include "libslic3r/SLAPrint.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/GCode/PostProcessor.hpp" +#include "libslic3r/GCode/PreviewData.hpp" -//#undef NDEBUG #include #include #include @@ -65,11 +65,6 @@ PrinterTechnology BackgroundSlicingProcess::current_printer_technology() const return m_print->technology(); } -static bool isspace(int ch) -{ - return std::isspace(ch) != 0; -} - // This function may one day be merged into the Print, but historically the print was separated // from the G-code generator. void BackgroundSlicingProcess::process_fff() @@ -88,6 +83,8 @@ void BackgroundSlicingProcess::process_fff() m_print->set_status(95, "Running post-processing scripts"); run_post_process_scripts(export_path, m_fff_print->config()); m_print->set_status(100, "G-code file exported to " + export_path); + } else if (! m_upload_job.empty()) { + prepare_upload(); } else { m_print->set_status(100, "Slicing complete"); } @@ -154,6 +151,10 @@ void BackgroundSlicingProcess::process_sla() if (! m_export_path.empty()) { m_sla_print->export_raster(m_export_path); m_print->set_status(100, "Zip file exported to " + m_export_path); + } else if (! m_upload_job.empty()) { + prepare_upload(); + } else { + m_print->set_status(100, "Slicing complete"); } this->set_step_done(bspsGCodeFinalize); } @@ -351,6 +352,13 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn assert(m_print != nullptr); assert(config.opt_enum("printer_technology") == m_print->technology()); Print::ApplyStatus invalidated = m_print->apply(model, config); + if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF && + m_gcode_preview_data != nullptr && ! this->m_fff_print->is_step_done(psGCodeExport)) { + // Some FFF status was invalidated, and the G-code was not exported yet. + // Let the G-code preview UI know that the final G-code preview is not valid. + // In addition, this early memory deallocation reduces memory footprint. + m_gcode_preview_data->reset(); + } return invalidated; } @@ -373,13 +381,10 @@ void BackgroundSlicingProcess::schedule_upload(Slic3r::PrintHostJob upload_job) if (! m_export_path.empty()) return; - const boost::filesystem::path path = boost::filesystem::temp_directory_path() - / boost::filesystem::unique_path(".upload.%%%%-%%%%-%%%%-%%%%.gcode"); - // Guard against entering the export step before changing the export path. tbb::mutex::scoped_lock lock(m_print->state_mutex()); this->invalidate_step(bspsGCodeFinalize); - m_export_path = path.string(); + m_export_path.clear(); m_upload_job = std::move(upload_job); } @@ -420,4 +425,35 @@ bool BackgroundSlicingProcess::invalidate_all_steps() return m_step_state.invalidate_all([this](){ this->stop_internal(); }); } +void BackgroundSlicingProcess::prepare_upload() +{ + // A print host upload job has been scheduled, enqueue it to the printhost job queue + + // XXX: is fs::path::string() right? + + // Generate a unique temp path to which the gcode/zip file is copied/exported + boost::filesystem::path source_path = boost::filesystem::temp_directory_path() + / boost::filesystem::unique_path(".printhost.%%%%-%%%%-%%%%-%%%%.gcode"); + + if (m_print == m_fff_print) { + m_print->set_status(95, "Running post-processing scripts"); + run_post_process_scripts(source_path.string(), m_fff_print->config()); + + if (copy_file(m_temp_output_path, source_path.string()) != 0) { + throw std::runtime_error("Copying of the temporary G-code to the output G-code failed"); + } + + 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_sla_print->export_raster(source_path.string()); + // TODO: Also finalize upload path like with FFF when there are statistics for SLA print + } + + m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str()); + + m_upload_job.upload_data.source_path = std::move(source_path); + + GUI::wxGetApp().printhost_job_queue().enqueue(std::move(m_upload_job)); +} + }; // namespace Slic3r diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 222ed147e..5911c8a02 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -167,6 +167,7 @@ private: bool invalidate_all_steps(); // If the background processing stop was requested, throw CanceledException. void throw_if_canceled() const { if (m_print->canceled()) throw CanceledException(); } + void prepare_upload(); // wxWidgets command ID to be sent to the platter to inform that the slicing is finished, and the G-code export will continue. int m_event_slicing_completed_id = 0; diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index b80ed42dd..e1bec8578 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -163,17 +163,42 @@ void Field::get_value_by_opt_type(wxString& str) break; } case coString: case coStrings: - case coFloatOrPercent: - m_value = str.ToStdString(); - break; + case coFloatOrPercent: { + if (m_opt.type == coFloatOrPercent && !str.IsEmpty() && str.Last() != '%') + { + double val; + if (!str.ToCDouble(&val)) + { + show_error(m_parent, _(L("Input value contains incorrect symbol(s).\nUse, please, only digits"))); + set_value(double_to_string(val), true); + } + else if (m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max || + m_opt.sidetext.rfind("mm ") != std::string::npos && val > 1) + { + std::string sidetext = m_opt.sidetext.rfind("mm/s") != std::string::npos ? "mm/s" : "mm"; + const int nVal = int(val); + wxString msg_text = wxString::Format(_(L("Do you mean %d%% instead of %d %s?\n" + "Select YES if you want to change this value to %d%%, \n" + "or NO if you are sure that %d %s is a correct value.")), nVal, nVal, sidetext, nVal, nVal, sidetext); + auto dialog = new wxMessageDialog(m_parent, msg_text, _(L("Parameter validation")), wxICON_WARNING | wxYES | wxNO); + if (dialog->ShowModal() == wxID_YES) { + set_value(wxString::Format("%s%%", str), true); + str += "%%"; + } + } + } + + m_value = str.ToStdString(); + break; } default: break; } } -bool TextCtrl::is_defined_input_value() const +template +bool is_defined_input_value(wxWindow* win, const ConfigOptionType& type) { - if (static_cast(window)->GetValue().empty() && m_opt.type != coString && m_opt.type != coStrings) + if (static_cast(win)->GetValue().empty() && type != coString && type != coStrings) return false; return true; } @@ -251,8 +276,7 @@ void TextCtrl::BUILD() { e.Skip(); temp->GetToolTip()->Enable(true); #endif // __WXGTK__ -// if (!is_defined_input_value()) - if (is_defined_input_value()) + if (is_defined_input_value(window, m_opt.type)) on_change_field(); else on_kill_focus(e); @@ -376,7 +400,11 @@ void SpinCtrl::BUILD() { auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, 0, min_val, max_val, default_value); -// temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { tmp_value = undef_spin_val; on_change_field(); }), temp->GetId()); +#ifndef __WXOSX__ + // #ys_FIXME_KILL_FOCUS + // wxEVT_KILL_FOCUS doesn't handled on OSX now (wxWidgets 3.1.1) + // So, we will update values on KILL_FOCUS & SPINCTRL events under MSW and GTK + // and on TEXT event under OSX temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { if (tmp_value < 0) @@ -386,6 +414,10 @@ void SpinCtrl::BUILD() { on_change_field(); } }), temp->GetId()); + + temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); +#endif + temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { // # On OSX / Cocoa, wxSpinCtrl::GetValue() doesn't return the new value @@ -397,10 +429,14 @@ void SpinCtrl::BUILD() { if (is_matched(value, "^\\d+$")) tmp_value = std::stoi(value); else tmp_value = -9999; -// on_change_field(); -// # We don't reset tmp_value here because _on_change might put callbacks -// # in the CallAfter queue, and we want the tmp value to be available from -// # them as well. +#ifdef __WXOSX__ + if (tmp_value < 0) { + if (m_on_kill_focus != nullptr) + m_on_kill_focus(m_opt_id); + } + else + on_change_field(); +#endif }), temp->GetId()); temp->SetToolTip(get_tooltip_text(text_value)); @@ -432,9 +468,24 @@ void Choice::BUILD() { } set_selection(); } - temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); +// temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); temp->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); + if (temp->GetWindowStyle() != wxCB_READONLY) { + temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { + e.Skip(); + double old_val = !m_value.empty() ? boost::any_cast(m_value) : -99999; + if (is_defined_input_value(window, m_opt.type)) { + if (fabs(old_val - boost::any_cast(get_value())) <= 0.0001) + return; + else + on_change_field(); + } + else + on_kill_focus(e); + }), temp->GetId()); + } + temp->SetToolTip(get_tooltip_text(temp->GetValue())); } @@ -632,9 +683,7 @@ boost::any& Choice::get_value() if (m_opt_id == rp_option) return m_value = boost::any(ret_str); - if (m_opt.type != coEnum) - /*m_value = */get_value_by_opt_type(ret_str); - else + if (m_opt.type == coEnum) { int ret_enum = static_cast(window)->GetSelection(); if (m_opt_id.compare("top_fill_pattern") == 0 || m_opt_id.compare("bottom_fill_pattern") == 0 ) @@ -671,7 +720,16 @@ boost::any& Choice::get_value() m_value = static_cast(ret_enum); else if (m_opt_id.compare("display_orientation") == 0) m_value = static_cast(ret_enum); - } + } + else if (m_opt.gui_type == "f_enum_open") { + const int ret_enum = static_cast(window)->GetSelection(); + if (ret_enum < 0 || m_opt.enum_values.empty()) + get_value_by_opt_type(ret_str); + else + m_value = atof(m_opt.enum_values[ret_enum].c_str()); + } + else + get_value_by_opt_type(ret_str); return m_value; } @@ -735,8 +793,11 @@ void PointCtrl::BUILD() temp->Add(new wxStaticText(m_parent, wxID_ANY, " y : "), 0, wxALIGN_CENTER_VERTICAL, 0); temp->Add(y_textctrl); - x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_textctrl->GetId()); - y_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), y_textctrl->GetId()); +// x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_textctrl->GetId()); +// y_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), y_textctrl->GetId()); + + x_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { OnKillFocus(e, x_textctrl); }), x_textctrl->GetId()); + y_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { OnKillFocus(e, x_textctrl); }), y_textctrl->GetId()); // // recast as a wxWindow to fit the calling convention sizer = dynamic_cast(temp); @@ -745,6 +806,16 @@ void PointCtrl::BUILD() y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y)); } +void PointCtrl::OnKillFocus(wxEvent& e, wxTextCtrl* win) +{ + e.Skip(); + if (!win->GetValue().empty()) { + on_change_field(); + } + else + on_kill_focus(e); +} + void PointCtrl::set_value(const Vec2d& value, bool change_event) { m_disable_change_event = !change_event; diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 0097d3ec0..4a19b6103 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -266,7 +266,6 @@ public: } boost::any& get_value() override; - bool is_defined_input_value() const ; virtual void enable(); virtual void disable(); @@ -395,6 +394,7 @@ public: void BUILD() override; + void OnKillFocus(wxEvent& e, wxTextCtrl* win); void set_value(const Vec2d& value, bool change_event = false); void set_value(const boost::any& value, bool change_event = false); boost::any& get_value() override; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 33938b8e8..1ed266d09 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -73,6 +73,11 @@ static const float DEFAULT_BG_LIGHT_COLOR[3] = { 0.753f, 0.753f, 0.753f }; static const float ERROR_BG_DARK_COLOR[3] = { 0.478f, 0.192f, 0.039f }; static const float ERROR_BG_LIGHT_COLOR[3] = { 0.753f, 0.192f, 0.039f }; +#if ENABLE_SIDEBAR_VISUAL_HINTS +static const float UNIFORM_SCALE_COLOR[3] = { 1.0f, 0.38f, 0.0f }; +static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + namespace Slic3r { namespace GUI { @@ -275,13 +280,8 @@ GLCanvas3D::Camera::Camera() , zoom(1.0f) , phi(45.0f) // , distance(0.0f) -#if !ENABLE_CONSTRAINED_CAMERA_TARGET - , target(0.0, 0.0, 0.0) -#endif // !ENABLE_CONSTRAINED_CAMERA_TARGET , m_theta(45.0f) -#if ENABLE_CONSTRAINED_CAMERA_TARGET , m_target(Vec3d::Zero()) -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET { } @@ -304,7 +304,6 @@ void GLCanvas3D::Camera::set_theta(float theta) m_theta = clamp(0.0f, GIMBALL_LOCK_THETA_MAX, theta); } -#if ENABLE_CONSTRAINED_CAMERA_TARGET void GLCanvas3D::Camera::set_target(const Vec3d& target, GLCanvas3D& canvas) { m_target = target; @@ -323,7 +322,6 @@ void GLCanvas3D::Camera::set_scene_box(const BoundingBoxf3& box, GLCanvas3D& can canvas.viewport_changed(); } } -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET GLCanvas3D::Bed::Bed() : m_type(Custom) @@ -617,42 +615,71 @@ bool GLCanvas3D::Bed::_are_equal(const Pointfs& bed_1, const Pointfs& bed_2) return true; } +const double GLCanvas3D::Axes::Radius = 0.5; +const double GLCanvas3D::Axes::ArrowBaseRadius = 2.5 * GLCanvas3D::Axes::Radius; +const double GLCanvas3D::Axes::ArrowLength = 5.0; + GLCanvas3D::Axes::Axes() : origin(Vec3d::Zero()) - , length(0.0f) + , length(Vec3d::Zero()) { + m_quadric = ::gluNewQuadric(); + if (m_quadric != nullptr) + ::gluQuadricDrawStyle(m_quadric, GLU_FILL); } -void GLCanvas3D::Axes::render(bool depth_test) const +GLCanvas3D::Axes::~Axes() { - if (depth_test) - ::glEnable(GL_DEPTH_TEST); - else - ::glDisable(GL_DEPTH_TEST); + if (m_quadric != nullptr) + ::gluDeleteQuadric(m_quadric); +} - ::glLineWidth(2.0f); - ::glBegin(GL_LINES); - // draw line for x axis +void GLCanvas3D::Axes::render() const +{ + if (m_quadric == nullptr) + return; + + ::glEnable(GL_DEPTH_TEST); + ::glEnable(GL_LIGHTING); + + // x axis ::glColor3f(1.0f, 0.0f, 0.0f); - ::glVertex3dv(origin.data()); - ::glVertex3f((GLfloat)origin(0) + length, (GLfloat)origin(1), (GLfloat)origin(2)); - // draw line for y axis - ::glColor3f(0.0f, 1.0f, 0.0f); - ::glVertex3dv(origin.data()); - ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1) + length, (GLfloat)origin(2)); - ::glEnd(); - // draw line for Z axis - // (re-enable depth test so that axis is correctly shown when objects are behind it) - if (!depth_test) - ::glEnable(GL_DEPTH_TEST); + ::glPushMatrix(); + ::glTranslated(origin(0), origin(1), origin(2)); + ::glRotated(90.0, 0.0, 1.0, 0.0); + render_axis(length(0)); + ::glPopMatrix(); - ::glBegin(GL_LINES); + // y axis + ::glColor3f(0.0f, 1.0f, 0.0f); + ::glPushMatrix(); + ::glTranslated(origin(0), origin(1), origin(2)); + ::glRotated(-90.0, 1.0, 0.0, 0.0); + render_axis(length(1)); + ::glPopMatrix(); + + // z axis ::glColor3f(0.0f, 0.0f, 1.0f); - ::glVertex3dv(origin.data()); - ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2) + length); - ::glEnd(); + ::glPushMatrix(); + ::glTranslated(origin(0), origin(1), origin(2)); + render_axis(length(2)); + ::glPopMatrix(); + + ::glDisable(GL_LIGHTING); } +void GLCanvas3D::Axes::render_axis(double length) const +{ + ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); + ::gluCylinder(m_quadric, Radius, Radius, length, 32, 1); + ::gluQuadricOrientation(m_quadric, GLU_INSIDE); + ::gluDisk(m_quadric, 0.0, Radius, 32, 1); + ::glTranslated(0.0, 0.0, length); + ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); + ::gluCylinder(m_quadric, ArrowBaseRadius, 0.0, ArrowLength, 32, 1); + ::gluQuadricOrientation(m_quadric, GLU_INSIDE); + ::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1); +} GLCanvas3D::Shader::Shader() : m_shader(nullptr) @@ -1077,7 +1104,6 @@ bool GLCanvas3D::Mouse::is_start_position_3D_defined() const return (drag.start_position_3D != Drag::Invalid_3D_Point); } -#if ENABLE_MODELVOLUME_TRANSFORM GLCanvas3D::Selection::VolumeCache::TransformCache::TransformCache() : position(Vec3d::Zero()) , rotation(Vec3d::Zero()) @@ -1105,25 +1131,6 @@ GLCanvas3D::Selection::VolumeCache::VolumeCache(const Geometry::Transformation& , m_instance(instance_transform) { } -#else -GLCanvas3D::Selection::VolumeCache::VolumeCache() - : m_position(Vec3d::Zero()) - , m_rotation(Vec3d::Zero()) - , m_scaling_factor(Vec3d::Ones()) -{ - m_rotation_matrix = Transform3d::Identity(); - m_scale_matrix = Transform3d::Identity(); -} - -GLCanvas3D::Selection::VolumeCache::VolumeCache(const Vec3d& position, const Vec3d& rotation, const Vec3d& scaling_factor) - : m_position(position) - , m_rotation(rotation) - , m_scaling_factor(scaling_factor) -{ - m_rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), m_rotation); - m_scale_matrix = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), m_scaling_factor); -} -#endif // ENABLE_MODELVOLUME_TRANSFORM GLCanvas3D::Selection::Selection() : m_volumes(nullptr) @@ -1132,15 +1139,45 @@ GLCanvas3D::Selection::Selection() , m_type(Empty) , m_valid(false) , m_bounding_box_dirty(true) + , m_curved_arrow(16) { +#if ENABLE_RENDER_SELECTION_CENTER + m_quadric = ::gluNewQuadric(); + if (m_quadric != nullptr) + ::gluQuadricDrawStyle(m_quadric, GLU_FILL); +#endif // ENABLE_RENDER_SELECTION_CENTER } +#if ENABLE_RENDER_SELECTION_CENTER +GLCanvas3D::Selection::~Selection() +{ + if (m_quadric != nullptr) + ::gluDeleteQuadric(m_quadric); +} +#endif // ENABLE_RENDER_SELECTION_CENTER + void GLCanvas3D::Selection::set_volumes(GLVolumePtrs* volumes) { m_volumes = volumes; _update_valid(); } +#if ENABLE_SIDEBAR_VISUAL_HINTS +bool GLCanvas3D::Selection::init(bool useVBOs) +{ + if (!m_arrow.init(useVBOs)) + return false; + + m_arrow.set_scale(5.0 * Vec3d::Ones()); + + if (!m_curved_arrow.init(useVBOs)) + return false; + + m_curved_arrow.set_scale(5.0 * Vec3d::Ones()); + return true; +} +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + void GLCanvas3D::Selection::set_model(Model* model) { m_model = model; @@ -1184,13 +1221,6 @@ void GLCanvas3D::Selection::add(unsigned int volume_idx, bool as_single_selectio _add_instance(volume->object_idx(), volume->instance_idx()); break; } -#if !ENABLE_MODELVOLUME_TRANSFORM - case Object: - { - _add_object(volume->object_idx()); - break; - } -#endif // !ENABLE_MODELVOLUME_TRANSFORM } _update_type(); @@ -1216,13 +1246,6 @@ void GLCanvas3D::Selection::remove(unsigned int volume_idx) _remove_instance(volume->object_idx(), volume->instance_idx()); break; } -#if !ENABLE_MODELVOLUME_TRANSFORM - case Object: - { - _remove_object(volume->object_idx()); - break; - } -#endif // !ENABLE_MODELVOLUME_TRANSFORM } _update_type(); @@ -1436,6 +1459,14 @@ bool GLCanvas3D::Selection::is_from_single_object() const return (0 <= idx) && (idx < 1000); } +bool GLCanvas3D::Selection::requires_uniform_scale() const +{ + if (is_single_full_instance() || is_single_modifier() || is_single_volume()) + return false; + + return true; +} + int GLCanvas3D::Selection::get_object_idx() const { return (m_cache.content.size() == 1) ? m_cache.content.begin()->first : -1; @@ -1480,24 +1511,25 @@ void GLCanvas3D::Selection::start_dragging() _set_caches(); } -void GLCanvas3D::Selection::translate(const Vec3d& displacement) +void GLCanvas3D::Selection::translate(const Vec3d& displacement, bool local) { if (!m_valid) return; for (unsigned int i : m_list) { -#if ENABLE_MODELVOLUME_TRANSFORM if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower) { - Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; - (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); + if (local) + (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement); + else + { + Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; + (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); + } } else if (m_mode == Instance) (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); -#else - (*m_volumes)[i]->set_offset(m_cache.volumes_data[i].get_position() + displacement); -#endif // ENABLE_MODELVOLUME_TRANSFORM } #if !DISABLE_INSTANCES_SYNCH @@ -1520,34 +1552,37 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) if (is_single_full_instance()) #if ENABLE_WORLD_ROTATIONS { - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix()); - (*m_volumes)[i]->set_instance_rotation(new_rotation); + if (local) + (*m_volumes)[i]->set_instance_rotation(rotation); + else + { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix()); + (*m_volumes)[i]->set_instance_rotation(new_rotation); + } } #else -#if ENABLE_MODELVOLUME_TRANSFORM (*m_volumes)[i]->set_instance_rotation(rotation); -#else - (*m_volumes)[i]->set_rotation(rotation); -#endif // ENABLE_MODELVOLUME_TRANSFORM #endif // ENABLE_WORLD_ROTATIONS -#if ENABLE_MODELVOLUME_TRANSFORM else if (is_single_volume() || is_single_modifier()) #if ENABLE_WORLD_ROTATIONS { - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - const Transform3d& inst_m = m_cache.volumes_data[i].get_instance_rotation_matrix(); - Vec3d new_rotation = Geometry::extract_euler_angles(inst_m.inverse() * m * inst_m * m_cache.volumes_data[i].get_volume_rotation_matrix()); - (*m_volumes)[i]->set_volume_rotation(new_rotation); - } + if (requires_local_axes()) + (*m_volumes)[i]->set_volume_rotation(rotation); + else + { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + const Transform3d& inst_m = m_cache.volumes_data[i].get_instance_rotation_matrix(); + Vec3d new_rotation = Geometry::extract_euler_angles(inst_m.inverse() * m * inst_m * m_cache.volumes_data[i].get_volume_rotation_matrix()); + (*m_volumes)[i]->set_volume_rotation(new_rotation); + } + } #else (*m_volumes)[i]->set_volume_rotation(rotation); #endif // ENABLE_WORLD_ROTATIONS -#endif // ENABLE_MODELVOLUME_TRANSFORM else { Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); -#if ENABLE_MODELVOLUME_TRANSFORM if (m_mode == Instance) { // extracts rotations from the composed transformation @@ -1568,12 +1603,6 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) } (*m_volumes)[i]->set_volume_rotation(new_rotation); } -#else - // extracts rotations from the composed transformation - Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_rotation_matrix()); - (*m_volumes)[i]->set_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_position() - m_cache.dragging_center)); - (*m_volumes)[i]->set_rotation(new_rotation); -#endif // ENABLE_MODELVOLUME_TRANSFORM } } @@ -1598,7 +1627,6 @@ void GLCanvas3D::Selection::flattening_rotate(const Vec3d& normal) for (unsigned int i : m_list) { -#if ENABLE_MODELVOLUME_TRANSFORM Transform3d wst = m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_volume_scale_matrix(); Vec3d scaling_factor = Vec3d(1./wst(0,0), 1./wst(1,1), 1./wst(2,2)); @@ -1614,23 +1642,6 @@ void GLCanvas3D::Selection::flattening_rotate(const Vec3d& normal) Vec3d new_rotation = Geometry::extract_euler_angles(extra_rotation * m_cache.volumes_data[i].get_instance_rotation_matrix() ); (*m_volumes)[i]->set_instance_rotation(new_rotation); -#else - Transform3d wst = m_cache.volumes_data[i].get_scale_matrix() * m_cache.volumes_data[i].get_scale_matrix(); - Vec3d scaling_factor = Vec3d(1. / wst(0, 0), 1. / wst(1, 1), 1. / wst(2, 2)); - - Vec3d rotation = Geometry::extract_euler_angles(m_cache.volumes_data[i].get_rotation_matrix() * m_cache.volumes_data[i].get_rotation_matrix()); - Vec3d transformed_normal = Geometry::assemble_transform(Vec3d::Zero(), rotation, scaling_factor) * normal; - transformed_normal.normalize(); - - Vec3d axis = transformed_normal(2) > 0.999f ? Vec3d(1., 0., 0.) : Vec3d(transformed_normal.cross(Vec3d(0., 0., -1.))); - axis.normalize(); - - Transform3d extra_rotation = Transform3d::Identity(); - extra_rotation.rotate(Eigen::AngleAxisd(acos(-transformed_normal(2)), axis)); - - Vec3d new_rotation = Geometry::extract_euler_angles(extra_rotation * m_cache.volumes_data[i].get_rotation_matrix()); - (*m_volumes)[i]->set_rotation(new_rotation); -#endif // ENABLE_MODELVOLUME_TRANSFORM } #if !DISABLE_INSTANCES_SYNCH @@ -1649,19 +1660,12 @@ void GLCanvas3D::Selection::scale(const Vec3d& scale, bool local) for (unsigned int i : m_list) { if (is_single_full_instance()) -#if ENABLE_MODELVOLUME_TRANSFORM (*m_volumes)[i]->set_instance_scaling_factor(scale); -#else - (*m_volumes)[i]->set_scaling_factor(scale); -#endif // ENABLE_MODELVOLUME_TRANSFORM -#if ENABLE_MODELVOLUME_TRANSFORM else if (is_single_volume() || is_single_modifier()) (*m_volumes)[i]->set_volume_scaling_factor(scale); -#endif // ENABLE_MODELVOLUME_TRANSFORM else { Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); -#if ENABLE_MODELVOLUME_TRANSFORM if (m_mode == Instance) { Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3); @@ -1684,13 +1688,6 @@ void GLCanvas3D::Selection::scale(const Vec3d& scale, bool local) } (*m_volumes)[i]->set_volume_scaling_factor(new_scale); } -#else - Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_scale_matrix()).matrix().block(0, 0, 3, 3); - // extracts scaling factors from the composed transformation - Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); - (*m_volumes)[i]->set_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_position() - m_cache.dragging_center)); - (*m_volumes)[i]->set_scaling_factor(new_scale); -#endif // ENABLE_MODELVOLUME_TRANSFORM } } @@ -1718,13 +1715,9 @@ void GLCanvas3D::Selection::mirror(Axis axis) for (unsigned int i : m_list) { if (single_full_instance) -#if ENABLE_MODELVOLUME_TRANSFORM (*m_volumes)[i]->set_instance_mirror(axis, -(*m_volumes)[i]->get_instance_mirror(axis)); else if (m_mode == Volume) (*m_volumes)[i]->set_volume_mirror(axis, -(*m_volumes)[i]->get_volume_mirror(axis)); -#else - (*m_volumes)[i]->set_mirror(axis, -(*m_volumes)[i]->get_mirror(axis)); -#endif // ENABLE_MODELVOLUME_TRANSFORM } #if !DISABLE_INSTANCES_SYNCH @@ -1746,11 +1739,7 @@ void GLCanvas3D::Selection::translate(unsigned int object_idx, const Vec3d& disp { GLVolume* v = (*m_volumes)[i]; if (v->object_idx() == object_idx) -#if ENABLE_MODELVOLUME_TRANSFORM v->set_instance_offset(v->get_instance_offset() + displacement); -#else - v->set_offset(v->get_offset() + displacement); -#endif // ENABLE_MODELVOLUME_TRANSFORM } std::set done; // prevent processing volumes twice @@ -1778,11 +1767,7 @@ void GLCanvas3D::Selection::translate(unsigned int object_idx, const Vec3d& disp if (v->object_idx() != object_idx) continue; -#if ENABLE_MODELVOLUME_TRANSFORM v->set_instance_offset(v->get_instance_offset() + displacement); -#else - v->set_offset(v->get_offset() + displacement); -#endif // ENABLE_MODELVOLUME_TRANSFORM done.insert(j); } } @@ -1799,11 +1784,7 @@ void GLCanvas3D::Selection::translate(unsigned int object_idx, unsigned int inst { GLVolume* v = (*m_volumes)[i]; if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) -#if ENABLE_MODELVOLUME_TRANSFORM v->set_instance_offset(v->get_instance_offset() + displacement); -#else - v->set_offset(v->get_offset() + displacement); -#endif // ENABLE_MODELVOLUME_TRANSFORM } std::set done; // prevent processing volumes twice @@ -1831,11 +1812,7 @@ void GLCanvas3D::Selection::translate(unsigned int object_idx, unsigned int inst if ((v->object_idx() != object_idx) || (v->instance_idx() != instance_idx)) continue; -#if ENABLE_MODELVOLUME_TRANSFORM v->set_instance_offset(v->get_instance_offset() + displacement); -#else - v->set_offset(v->get_offset() + displacement); -#endif // ENABLE_MODELVOLUME_TRANSFORM done.insert(j); } } @@ -1960,7 +1937,7 @@ void GLCanvas3D::Selection::erase() void GLCanvas3D::Selection::render() const { - if (is_empty()) + if (!m_valid || is_empty()) return; // render cumulative bounding box of selected volumes @@ -1968,6 +1945,85 @@ void GLCanvas3D::Selection::render() const _render_synchronized_volumes(); } +#if ENABLE_RENDER_SELECTION_CENTER +void GLCanvas3D::Selection::render_center() const +{ + if (!m_valid || is_empty() || (m_quadric == nullptr)) + return; + + const Vec3d& center = get_bounding_box().center(); + + ::glDisable(GL_DEPTH_TEST); + + ::glEnable(GL_LIGHTING); + + ::glColor3f(1.0f, 1.0f, 1.0f); + ::glPushMatrix(); + ::glTranslated(center(0), center(1), center(2)); + ::gluSphere(m_quadric, 0.75, 32, 32); + ::glPopMatrix(); + + ::glDisable(GL_LIGHTING); +} +#endif // ENABLE_RENDER_SELECTION_CENTER + +#if ENABLE_SIDEBAR_VISUAL_HINTS +void GLCanvas3D::Selection::render_sidebar_hints(const std::string& sidebar_field) const +{ + if (sidebar_field.empty()) + return; + + ::glClear(GL_DEPTH_BUFFER_BIT); + ::glEnable(GL_DEPTH_TEST); + + ::glEnable(GL_LIGHTING); + + ::glPushMatrix(); + + const Vec3d& center = get_bounding_box().center(); + + if (is_single_full_instance()) + ::glTranslated(center(0), center(1), center(2)); + else if (is_single_volume() || is_single_modifier()) + { + const GLVolume* volume = (*m_volumes)[*m_list.begin()]; + Transform3d orient_matrix = volume->get_instance_transformation().get_matrix(true, false, true, true); + const Vec3d& offset = get_bounding_box().center(); + + ::glTranslated(offset(0), offset(1), offset(2)); + ::glMultMatrixd(orient_matrix.data()); + } + else + { + ::glTranslated(center(0), center(1), center(2)); + if (requires_local_axes()) + { + const GLVolume* volume = (*m_volumes)[*m_list.begin()]; + Transform3d orient_matrix = volume->get_instance_transformation().get_matrix(true, false, true, true); + ::glMultMatrixd(orient_matrix.data()); + } + } + + if (boost::starts_with(sidebar_field, "position")) + _render_sidebar_position_hints(sidebar_field); + else if (boost::starts_with(sidebar_field, "rotation")) + _render_sidebar_rotation_hints(sidebar_field); + else if (boost::starts_with(sidebar_field, "scale")) + _render_sidebar_scale_hints(sidebar_field); + else if (boost::starts_with(sidebar_field, "size")) + _render_sidebar_size_hints(sidebar_field); + + ::glPopMatrix(); + + ::glDisable(GL_LIGHTING); +} +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + +bool GLCanvas3D::Selection::requires_local_axes() const +{ + return (m_mode == Volume) && is_from_single_instance(); +} + void GLCanvas3D::Selection::_update_valid() { m_valid = (m_volumes != nullptr) && (m_model != nullptr); @@ -2207,11 +2263,7 @@ void GLCanvas3D::Selection::_set_caches() for (unsigned int i : m_list) { const GLVolume* v = (*m_volumes)[i]; -#if ENABLE_MODELVOLUME_TRANSFORM m_cache.volumes_data.emplace(i, VolumeCache(v->get_volume_transformation(), v->get_instance_transformation())); -#else - m_cache.volumes_data.emplace(i, VolumeCache(v->get_offset(), v->get_rotation(), v->get_scaling_factor())); -#endif // ENABLE_MODELVOLUME_TRANSFORM } m_cache.dragging_center = get_bounding_box().center(); } @@ -2370,6 +2422,102 @@ void GLCanvas3D::Selection::_render_bounding_box(const BoundingBoxf3& box, float ::glEnd(); } +#if ENABLE_SIDEBAR_VISUAL_HINTS +void GLCanvas3D::Selection::_render_sidebar_position_hints(const std::string& sidebar_field) const +{ + if (boost::ends_with(sidebar_field, "x")) + { + ::glRotated(-90.0, 0.0, 0.0, 1.0); + _render_sidebar_position_hint(X); + } + else if (boost::ends_with(sidebar_field, "y")) + _render_sidebar_position_hint(Y); + else if (boost::ends_with(sidebar_field, "z")) + { + ::glRotated(90.0, 1.0, 0.0, 0.0); + _render_sidebar_position_hint(Z); + } +} + +void GLCanvas3D::Selection::_render_sidebar_rotation_hints(const std::string& sidebar_field) const +{ + if (boost::ends_with(sidebar_field, "x")) + { + ::glRotated(90.0, 0.0, 1.0, 0.0); + _render_sidebar_rotation_hint(X); + } + else if (boost::ends_with(sidebar_field, "y")) + { + ::glRotated(-90.0, 1.0, 0.0, 0.0); + _render_sidebar_rotation_hint(Y); + } + else if (boost::ends_with(sidebar_field, "z")) + _render_sidebar_rotation_hint(Z); +} + +void GLCanvas3D::Selection::_render_sidebar_scale_hints(const std::string& sidebar_field) const +{ + if (boost::ends_with(sidebar_field, "x") || requires_uniform_scale()) + { + ::glPushMatrix(); + ::glRotated(-90.0, 0.0, 0.0, 1.0); + _render_sidebar_scale_hint(X); + ::glPopMatrix(); + } + + if (boost::ends_with(sidebar_field, "y") || requires_uniform_scale()) + { + ::glPushMatrix(); + _render_sidebar_scale_hint(Y); + ::glPopMatrix(); + } + + if (boost::ends_with(sidebar_field, "z") || requires_uniform_scale()) + { + ::glPushMatrix(); + ::glRotated(90.0, 1.0, 0.0, 0.0); + _render_sidebar_scale_hint(Z); + ::glPopMatrix(); + } +} + +void GLCanvas3D::Selection::_render_sidebar_size_hints(const std::string& sidebar_field) const +{ + _render_sidebar_scale_hints(sidebar_field); +} + +void GLCanvas3D::Selection::_render_sidebar_position_hint(Axis axis) const +{ + m_arrow.set_color(AXES_COLOR[axis], 3); + m_arrow.render(); +} + +void GLCanvas3D::Selection::_render_sidebar_rotation_hint(Axis axis) const +{ + m_curved_arrow.set_color(AXES_COLOR[axis], 3); + m_curved_arrow.render(); + + ::glRotated(180.0, 0.0, 0.0, 1.0); + m_curved_arrow.render(); +} + +void GLCanvas3D::Selection::_render_sidebar_scale_hint(Axis axis) const +{ + m_arrow.set_color((requires_uniform_scale() ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3); + + ::glTranslated(0.0, 5.0, 0.0); + m_arrow.render(); + + ::glTranslated(0.0, -10.0, 0.0); + ::glRotated(180.0, 0.0, 0.0, 1.0); + m_arrow.render(); +} + +void GLCanvas3D::Selection::_render_sidebar_size_hint(Axis axis, double length) const +{ +} +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + void GLCanvas3D::Selection::_synchronize_unselected_instances() { std::set done; // prevent processing volumes twice @@ -2386,15 +2534,9 @@ void GLCanvas3D::Selection::_synchronize_unselected_instances() continue; int instance_idx = volume->instance_idx(); -#if ENABLE_MODELVOLUME_TRANSFORM const Vec3d& rotation = volume->get_instance_rotation(); const Vec3d& scaling_factor = volume->get_instance_scaling_factor(); const Vec3d& mirror = volume->get_instance_mirror(); -#else - const Vec3d& rotation = volume->get_rotation(); - const Vec3d& scaling_factor = volume->get_scaling_factor(); - const Vec3d& mirror = volume->get_mirror(); -#endif // ENABLE_MODELVOLUME_TRANSFORM // Process unselected instances. for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) @@ -2409,15 +2551,9 @@ void GLCanvas3D::Selection::_synchronize_unselected_instances() if ((v->object_idx() != object_idx) || (v->instance_idx() == instance_idx)) continue; -#if ENABLE_MODELVOLUME_TRANSFORM v->set_instance_rotation(Vec3d(rotation(0), rotation(1), v->get_instance_rotation()(2))); v->set_instance_scaling_factor(scaling_factor); v->set_instance_mirror(mirror); -#else - v->set_rotation(Vec3d(rotation(0), rotation(1), v->get_rotation()(2))); - v->set_scaling_factor(scaling_factor); - v->set_mirror(mirror); -#endif // ENABLE_MODELVOLUME_TRANSFORM done.insert(j); } @@ -2434,17 +2570,10 @@ void GLCanvas3D::Selection::_synchronize_unselected_volumes() continue; int volume_idx = volume->volume_idx(); -#if ENABLE_MODELVOLUME_TRANSFORM const Vec3d& offset = volume->get_volume_offset(); const Vec3d& rotation = volume->get_volume_rotation(); const Vec3d& scaling_factor = volume->get_volume_scaling_factor(); const Vec3d& mirror = volume->get_volume_mirror(); -#else - const Vec3d& offset = volume->get_offset(); - const Vec3d& rotation = volume->get_rotation(); - const Vec3d& scaling_factor = volume->get_scaling_factor(); - const Vec3d& mirror = volume->get_mirror(); -#endif // ENABLE_MODELVOLUME_TRANSFORM // Process unselected volumes. for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) @@ -2456,17 +2585,10 @@ void GLCanvas3D::Selection::_synchronize_unselected_volumes() if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx)) continue; -#if ENABLE_MODELVOLUME_TRANSFORM v->set_volume_offset(offset); v->set_volume_rotation(rotation); v->set_volume_scaling_factor(scaling_factor); v->set_volume_mirror(mirror); -#else - v->set_offset(offset); - v->set_rotation(Vec3d(rotation)); - v->set_scaling_factor(scaling_factor); - v->set_mirror(mirror); -#endif // ENABLE_MODELVOLUME_TRANSFORM } } } @@ -2584,7 +2706,6 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo)); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_background_texture.metadata.filename = "toolbar_background.png"; m_background_texture.metadata.left = 16; m_background_texture.metadata.top = 16; @@ -2599,7 +2720,6 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) return false; } } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE return true; } @@ -3032,7 +3152,6 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; float height = _get_total_overlay_height(); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_border = OverlayBorder * inv_zoom; float top_x = (-0.5f * cnv_w) * inv_zoom; @@ -3111,10 +3230,6 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva top_x += OverlayBorder * inv_zoom; top_y -= OverlayBorder * inv_zoom; -#else - float top_x = (OverlayBorder - 0.5f * cnv_w) * inv_zoom; - float top_y = (0.5f * height - OverlayBorder) * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_gap_y = OverlayGapY * inv_zoom; for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { @@ -3153,7 +3268,6 @@ float GLCanvas3D::Gizmos::_get_total_overlay_height() const return height - OverlayGapY; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float GLCanvas3D::Gizmos::_get_total_overlay_width() const { float max_icon_width = 0.0f; @@ -3167,7 +3281,6 @@ float GLCanvas3D::Gizmos::_get_total_overlay_width() const return max_icon_width + 2.0f * OverlayBorder; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const { @@ -3522,9 +3635,7 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const } } -#if ENABLE_REMOVE_TABS_FROM_PLATER wxDEFINE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent); @@ -3532,8 +3643,13 @@ wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_MODEL_UPDATE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event); wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION +wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_ROTATED, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); @@ -3543,15 +3659,10 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) : m_canvas(canvas) , m_context(nullptr) , m_in_render(false) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE , m_toolbar(GLToolbar::Normal) -#else - , m_toolbar(*this) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#if ENABLE_REMOVE_TABS_FROM_PLATER , m_view_toolbar(nullptr) -#endif // ENABLE_REMOVE_TABS_FROM_PLATER , m_use_clipping_planes(false) + , m_sidebar_field("") , m_config(nullptr) , m_process(nullptr) , m_model(nullptr) @@ -3566,7 +3677,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_legend_texture_enabled(false) , m_picking_enabled(false) , m_moving_enabled(false) - , m_shader_enabled(false) , m_dynamic_background_enabled(false) , m_multisample_allowed(false) , m_regenerate_volumes(true) @@ -3694,9 +3804,12 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) if (!_init_toolbar()) return false; -#if ENABLE_REMOVE_TABS_FROM_PLATER +#if ENABLE_SIDEBAR_VISUAL_HINTS + if (!m_selection.init(m_use_VBOs)) + return false; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + post_event(SimpleEvent(EVT_GLCANVAS_INIT)); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER m_initialized = true; @@ -3743,21 +3856,12 @@ void GLCanvas3D::reset_volumes() _reset_warning_texture(); } -#if ENABLE_REMOVE_TABS_FROM_PLATER int GLCanvas3D::check_volumes_outside_state() const { ModelInstance::EPrintVolumeState state; m_volumes.check_outside_state(m_config, &state); return (int)state; } -#else -int GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) const -{ - ModelInstance::EPrintVolumeState state; - m_volumes.check_outside_state(config, &state); - return (int)state; -} -#endif // ENABLE_REMOVE_TABS_FROM_PLATER void GLCanvas3D::set_config(DynamicPrintConfig* config) { @@ -3781,7 +3885,7 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape) // Set the origin and size for painting of the coordinate system axes. m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z); - set_axes_length(0.3f * (float)m_bed.get_bounding_box().max_size()); + set_bed_axes_length(0.1 * m_bed.get_bounding_box().max_size()); if (new_shape) zoom_to_bed(); @@ -3789,9 +3893,9 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape) m_dirty = true; } -void GLCanvas3D::set_axes_length(float length) +void GLCanvas3D::set_bed_axes_length(double length) { - m_axes.length = length; + m_axes.length = length * Vec3d::Ones(); } void GLCanvas3D::set_color_by(const std::string& value) @@ -3815,14 +3919,12 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const return bb; } -#if ENABLE_CONSTRAINED_CAMERA_TARGET BoundingBoxf3 GLCanvas3D::scene_bounding_box() const { BoundingBoxf3 bb = volumes_bounding_box(); bb.merge(m_bed.get_bounding_box()); return bb; } -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET bool GLCanvas3D::is_layers_editing_enabled() const { @@ -3875,11 +3977,6 @@ void GLCanvas3D::enable_toolbar(bool enable) m_toolbar.set_enabled(enable); } -void GLCanvas3D::enable_shader(bool enable) -{ - m_shader_enabled = enable; -} - void GLCanvas3D::enable_force_zoom_to_bed(bool enable) { m_force_zoom_to_bed_enabled = enable; @@ -3920,13 +4017,11 @@ void GLCanvas3D::zoom_to_volumes() m_apply_zoom_to_volumes_filter = false; } -#if ENABLE_MODIFIED_CAMERA_TARGET void GLCanvas3D::zoom_to_selection() { if (!m_selection.is_empty()) _zoom_to_bounding_box(m_selection.get_bounding_box()); } -#endif // ENABLE_MODIFIED_CAMERA_TARGET void GLCanvas3D::select_view(const std::string& direction) { @@ -3963,12 +4058,8 @@ void GLCanvas3D::set_viewport_from_scene(const GLCanvas3D& other) { m_camera.phi = other.m_camera.phi; m_camera.set_theta(other.m_camera.get_theta()); -#if ENABLE_CONSTRAINED_CAMERA_TARGET m_camera.set_scene_box(other.m_camera.get_scene_box(), *this); m_camera.set_target(other.m_camera.get_target(), *this); -#else - m_camera.target = other.m_camera.target; -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET m_camera.zoom = other.m_camera.zoom; m_dirty = true; } @@ -4035,10 +4126,6 @@ void GLCanvas3D::render() float theta = m_camera.get_theta(); bool is_custom_bed = m_bed.is_custom(); -#if !ENABLE_REMOVE_TABS_FROM_PLATER - set_tooltip(""); -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER - #if ENABLE_IMGUI wxGetApp().imgui()->new_frame(); #endif // ENABLE_IMGUI @@ -4050,28 +4137,41 @@ void GLCanvas3D::render() ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); _render_background(); +#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + // textured bed needs to be rendered after objects if the texture is transparent + bool early_bed_render = is_custom_bed || (theta <= 90.0f); + if (early_bed_render) +#else if (is_custom_bed) // untextured bed needs to be rendered before objects - { +#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING _render_bed(theta); - // disable depth testing so that axes are not covered by ground - _render_axes(false); - } _render_objects(); _render_sla_slices(); _render_selection(); + _render_axes(); + +#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + if (!early_bed_render) +#else if (!is_custom_bed) // textured bed needs to be rendered after objects - { - _render_axes(true); +#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING _render_bed(theta); - } + +#if ENABLE_RENDER_SELECTION_CENTER + _render_selection_center(); +#endif // ENABLE_RENDER_SELECTION_CENTER // we need to set the mouse's scene position here because the depth buffer // could be invalidated by the following gizmo render methods // this position is used later into on_mouse() to drag the objects m_mouse.scene_position = _mouse_to_3d(m_mouse.position.cast()); +#if ENABLE_SIDEBAR_VISUAL_HINTS + _render_selection_sidebar_hints(); +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + _render_current_gizmo(); #if ENABLE_SHOW_CAMERA_TARGET _render_camera_target(); @@ -4081,13 +4181,9 @@ void GLCanvas3D::render() _render_gizmos_overlay(); _render_warning_texture(); _render_legend_texture(); -#if ENABLE_REMOVE_TABS_FROM_PLATER _resize_toolbars(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER _render_toolbar(); -#if ENABLE_REMOVE_TABS_FROM_PLATER _render_view_toolbar(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER _render_layer_editing_overlay(); #if ENABLE_IMGUI @@ -4507,10 +4603,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // restore to default value m_regenerate_volumes = true; -#if ENABLE_CONSTRAINED_CAMERA_TARGET m_camera.set_scene_box(scene_bounding_box(), *this); m_camera.set_target(m_camera.get_target(), *this); -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET // and force this canvas to be redrawn. m_dirty = true; @@ -4685,6 +4779,8 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 43: { post_event(Event(EVT_GLCANVAS_INCREASE_INSTANCES, +1)); break; } // key - case 45: { post_event(Event(EVT_GLCANVAS_INCREASE_INSTANCES, -1)); break; } + // key ? + case 63: { post_event(SimpleEvent(EVT_GLCANVAS_QUESTION_MARK)); break; } // key A/a case 65: case 97: { post_event(SimpleEvent(EVT_GLCANVAS_ARRANGE)); break; } @@ -4697,7 +4793,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) // key O/o case 79: case 111: { set_camera_zoom(-1.0f); break; } -#if ENABLE_MODIFIED_CAMERA_TARGET // key Z/z case 90: case 122: @@ -4709,11 +4804,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) break; } -#else - // key Z/z - case 90: - case 122: { zoom_to_volumes(); break; } -#endif // ENABLE_MODIFIED_CAMERA_TARGET default: { if (m_gizmos.handle_shortcut(keyCode, m_selection)) @@ -4788,21 +4878,22 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1; m_layers_editing.last_object_id = layer_editing_object_idx; bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position, *this); -#else - int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#if ENABLE_REMOVE_TABS_FROM_PLATER int view_toolbar_contains_mouse = (m_view_toolbar != nullptr) ? m_view_toolbar->contains_mouse(m_mouse.position, *this) : -1; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER if (evt.Entering()) { #if defined(__WXMSW__) || defined(__linux__) // On Windows and Linux needs focus in order to catch key events - if (m_canvas != nullptr) - m_canvas->SetFocus(); + if (m_canvas != nullptr) { + // Only set focus, if the top level window of this canvas is active. + auto p = dynamic_cast(evt.GetEventObject()); + while (p->GetParent()) + p = p->GetParent(); + auto *top_level_wnd = dynamic_cast(p); + if (top_level_wnd && top_level_wnd->IsActive()) + m_canvas->SetFocus(); + } m_mouse.set_start_position_2D_as_invalid(); #endif @@ -4816,11 +4907,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.LeftDClick() && (toolbar_contains_mouse != -1)) { m_toolbar_action_running = true; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.do_action((unsigned int)toolbar_contains_mouse, *this); -#else - m_toolbar.do_action((unsigned int)toolbar_contains_mouse); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } else if (evt.LeftDClick() && (m_gizmos.get_current_type() != Gizmos::Undefined)) { @@ -4889,21 +4976,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_gizmos.get_current_type() == Gizmos::SlaSupports) m_gizmos.delete_current_grabber(); } -#if ENABLE_REMOVE_TABS_FROM_PLATER else if (view_toolbar_contains_mouse != -1) { if (m_view_toolbar != nullptr) m_view_toolbar->do_action((unsigned int)view_toolbar_contains_mouse, *this); } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER else if (toolbar_contains_mouse != -1) { m_toolbar_action_running = true; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.do_action((unsigned int)toolbar_contains_mouse, *this); -#else - m_toolbar.do_action((unsigned int)toolbar_contains_mouse); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_mouse.left_down = false; } else @@ -4919,6 +5000,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) bool already_selected = m_selection.contains_volume(m_hover_volume_id); bool shift_down = evt.ShiftDown(); +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + Selection::IndicesList curr_idxs = m_selection.get_volume_idxs(); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + if (already_selected && shift_down) m_selection.remove(m_hover_volume_id); else @@ -4927,11 +5012,21 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_selection.add(m_hover_volume_id, add_as_single); } - m_gizmos.update_on_off_state(m_selection); - _update_gizmos_data(); - wxGetApp().obj_manipul()->update_settings_value(m_selection); - post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); - m_dirty = true; +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + if (curr_idxs != m_selection.get_volume_idxs()) + { +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + + m_gizmos.update_on_off_state(m_selection); + _update_gizmos_data(); +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + wxGetApp().obj_manipul()->update_settings_value(m_selection); +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); + m_dirty = true; +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + } +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } } @@ -4989,6 +5084,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag Vec3d cur_pos = m_selection.contains_volume(m_hover_volume_id) ? Linef3(_mouse_to_3d(pos, &z0), _mouse_to_3d(pos, &z1)).intersect_plane(m_mouse.drag.start_position_3D(2)) : m_mouse.drag.start_position_3D; + m_regenerate_volumes = false; m_selection.translate(cur_pos - m_mouse.drag.start_position_3D); wxGetApp().obj_manipul()->update_settings_value(m_selection); @@ -5064,11 +5160,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) float z = 0.0f; const Vec3d& cur_pos = _mouse_to_3d(pos, &z); Vec3d orig = _mouse_to_3d(m_mouse.drag.start_position_2D, &z); -#if ENABLE_CONSTRAINED_CAMERA_TARGET m_camera.set_target(m_camera.get_target() + orig - cur_pos, *this); -#else - m_camera.target += orig - cur_pos; -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET viewport_changed(); @@ -5108,7 +5200,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled()) { // deselect and propagate event through callback - if (m_picking_enabled && !m_toolbar_action_running && !m_mouse.ignore_up_event) + if (!evt.ShiftDown() && m_picking_enabled && !m_toolbar_action_running && !m_mouse.ignore_up_event) { m_selection.clear(); m_selection.set_mode(Selection::Instance); @@ -5154,10 +5246,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Let the platter know that the dragging finished, so a delayed refresh // of the scene with the background processing data should be performed. post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); -#if ENABLE_CONSTRAINED_CAMERA_TARGET m_camera.set_scene_box(scene_bounding_box(), *this); set_camera_zoom(0.0f); -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET } m_moving = false; @@ -5175,7 +5265,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.Moving()) { m_mouse.position = pos.cast(); -#if ENABLE_REMOVE_TABS_FROM_PLATER std::string tooltip = ""; // updates gizmos overlay @@ -5186,11 +5275,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // updates toolbar overlay if (tooltip.empty()) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE tooltip = m_toolbar.update_hover_state(m_mouse.position, *this); -#else - tooltip = m_toolbar.update_hover_state(m_mouse.position); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE // updates view toolbar overlay if (tooltip.empty() && (m_view_toolbar != nullptr)) @@ -5201,7 +5286,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } set_tooltip(tooltip); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER // Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over. if (m_picking_enabled) @@ -5313,7 +5397,15 @@ void GLCanvas3D::do_move() ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { -#if ENABLE_MODELVOLUME_TRANSFORM +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + if (selection_mode == Selection::Instance) + model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); + else if (selection_mode == Selection::Volume) + model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); + + object_moved = true; + model_object->invalidate_bounding_box(); +#else if (selection_mode == Selection::Instance) { model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); @@ -5325,20 +5417,13 @@ void GLCanvas3D::do_move() object_moved = true; } if (object_moved) -#else - model_object->instances[instance_idx]->set_offset(v->get_offset()); - object_moved = true; -#endif // ENABLE_MODELVOLUME_TRANSFORM - model_object->invalidate_bounding_box(); + model_object->invalidate_bounding_box(); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } } else if (object_idx == 1000) // Move a wipe tower proxy. -#if ENABLE_MODELVOLUME_TRANSFORM wipe_tower_origin = v->get_volume_offset(); -#else - wipe_tower_origin = v->get_offset(); -#endif // ENABLE_MODELVOLUME_TRANSFORM } // Fixes sinking/flying instances @@ -5381,7 +5466,6 @@ void GLCanvas3D::do_rotate() ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { -#if ENABLE_MODELVOLUME_TRANSFORM if (selection_mode == Selection::Instance) { model_object->instances[instance_idx]->set_rotation(v->get_instance_rotation()); @@ -5392,10 +5476,6 @@ void GLCanvas3D::do_rotate() model_object->volumes[volume_idx]->set_rotation(v->get_volume_rotation()); model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); } -#else - model_object->instances[instance_idx]->set_rotation(v->get_rotation()); - model_object->instances[instance_idx]->set_offset(v->get_offset()); -#endif // ENABLE_MODELVOLUME_TRANSFORM model_object->invalidate_bounding_box(); } } @@ -5409,7 +5489,12 @@ void GLCanvas3D::do_rotate() m->translate_instance(i.second, shift); } +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + if (!done.empty()) + post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED)); +#else post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } void GLCanvas3D::do_scale() @@ -5436,7 +5521,6 @@ void GLCanvas3D::do_scale() ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { -#if ENABLE_MODELVOLUME_TRANSFORM if (selection_mode == Selection::Instance) { model_object->instances[instance_idx]->set_scaling_factor(v->get_instance_scaling_factor()); @@ -5448,10 +5532,6 @@ void GLCanvas3D::do_scale() model_object->volumes[volume_idx]->set_scaling_factor(v->get_volume_scaling_factor()); model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); } -#else - model_object->instances[instance_idx]->set_scaling_factor(v->get_scaling_factor()); - model_object->instances[instance_idx]->set_offset(v->get_offset()); -#endif // ENABLE_MODELVOLUME_TRANSFORM model_object->invalidate_bounding_box(); } } @@ -5465,7 +5545,12 @@ void GLCanvas3D::do_scale() m->translate_instance(i.second, shift); } +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + if (!done.empty()) + post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED)); +#else post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } void GLCanvas3D::do_flatten() @@ -5497,14 +5582,11 @@ void GLCanvas3D::do_mirror() ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { -#if ENABLE_MODELVOLUME_TRANSFORM if (selection_mode == Selection::Instance) model_object->instances[instance_idx]->set_mirror(v->get_instance_mirror()); else if (selection_mode == Selection::Volume) model_object->volumes[volume_idx]->set_mirror(v->get_volume_mirror()); -#else - model_object->instances[instance_idx]->set_mirror(v->get_mirror()); -#endif // ENABLE_MODELVOLUME_TRANSFORM + model_object->invalidate_bounding_box(); } } @@ -5542,6 +5624,17 @@ void GLCanvas3D::update_gizmos_on_off_state() m_gizmos.update_on_off_state(get_selection()); } +void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool focus_on) +{ + m_sidebar_field = focus_on ? opt_key : ""; + + if (!m_sidebar_field.empty()) + { + m_gizmos.reset_all_states(); + m_dirty = true; + } +} + bool GLCanvas3D::_is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; @@ -5558,13 +5651,17 @@ bool GLCanvas3D::_init_toolbar() if (!m_toolbar.is_enabled()) return true; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE ItemsIconsTexture::Metadata icons_data; icons_data.filename = "toolbar.png"; icons_data.icon_size = 36; icons_data.icon_border_size = 1; icons_data.icon_gap_size = 1; +// icons_data.filename = "toolbar141.png"; +// icons_data.icon_size = 52; +// icons_data.icon_border_size = 0; +// icons_data.icon_gap_size = 0; + BackgroundTexture::Metadata background_data; background_data.filename = "toolbar_background.png"; background_data.left = 16; @@ -5573,9 +5670,6 @@ bool GLCanvas3D::_init_toolbar() background_data.bottom = 16; if (!m_toolbar.init(icons_data, background_data)) -#else - if (!m_toolbar.init("toolbar.png", 36, 1, 1)) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { // unable to init the toolbar texture, disable it m_toolbar.set_enabled(false); @@ -5584,10 +5678,8 @@ bool GLCanvas3D::_init_toolbar() // m_toolbar.set_layout_type(GLToolbar::Layout::Vertical); m_toolbar.set_layout_type(GLToolbar::Layout::Horizontal); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.set_layout_orientation(GLToolbar::Layout::Top); m_toolbar.set_border(5.0f); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.set_separator_size(5); m_toolbar.set_gap_size(2); @@ -5780,11 +5872,7 @@ void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) { m_camera.zoom = zoom; // center view around bounding box center -#if ENABLE_CONSTRAINED_CAMERA_TARGET m_camera.set_target(bbox.center(), *this); -#else - m_camera.target = bbox.center(); -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET viewport_changed(); @@ -5835,7 +5923,7 @@ float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) co // margin factor to give some empty space around the bbox double margin_factor = 1.25; - for (const Vec3d v : vertices) + for (const Vec3d& v : vertices) { // project vertex on the plane perpendicular to camera forward axis Vec3d pos(v(0) - bb_center(0), v(1) - bb_center(1), v(2) - bb_center(2)); @@ -5906,12 +5994,8 @@ void GLCanvas3D::_camera_tranform() const ::glRotatef(-m_camera.get_theta(), 1.0f, 0.0f, 0.0f); // pitch ::glRotatef(m_camera.phi, 0.0f, 0.0f, 1.0f); // yaw -#if ENABLE_CONSTRAINED_CAMERA_TARGET Vec3d target = -m_camera.get_target(); ::glTranslated(target(0), target(1), target(2)); -#else - ::glTranslated(-m_camera.target(0), -m_camera.target(1), -m_camera.target(2)); -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET } void GLCanvas3D::_picking_pass() const @@ -5961,20 +6045,6 @@ void GLCanvas3D::_picking_pass() const } _update_volumes_hover_state(); - -#if !ENABLE_REMOVE_TABS_FROM_PLATER - // updates gizmos overlay - if (!m_selection.is_empty()) - { - std::string name = m_gizmos.update_hover_state(*this, pos, m_selection); - if (!name.empty()) - set_tooltip(name); - } - else - m_gizmos.reset_all_states(); - - m_toolbar.update_hover_state(pos); -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER } } @@ -6019,9 +6089,9 @@ void GLCanvas3D::_render_bed(float theta) const m_bed.render(theta); } -void GLCanvas3D::_render_axes(bool depth_test) const +void GLCanvas3D::_render_axes() const { - m_axes.render(depth_test); + m_axes.render(); } void GLCanvas3D::_render_objects() const @@ -6032,9 +6102,7 @@ void GLCanvas3D::_render_objects() const ::glEnable(GL_LIGHTING); ::glEnable(GL_DEPTH_TEST); - if (!m_shader_enabled) - _render_volumes(false); - else if (m_use_VBOs) + if (m_use_VBOs) { if (m_picking_enabled) { @@ -6046,8 +6114,10 @@ void GLCanvas3D::_render_objects() const m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height")); m_volumes.check_outside_state(m_config, nullptr); } +#if !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING // do not cull backfaces to show broken geometry, if any ::glDisable(GL_CULL_FACE); +#endif // !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING } if (m_use_clipping_planes) @@ -6056,11 +6126,19 @@ void GLCanvas3D::_render_objects() const m_volumes.set_z_range(-FLT_MAX, FLT_MAX); m_shader.start_using(); +#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + // do not cull backfaces to show broken geometry, if any + m_volumes.render_VBOs(GLVolumeCollection::Opaque, m_picking_enabled); + m_volumes.render_VBOs(GLVolumeCollection::Transparent, false); +#else m_volumes.render_VBOs(); +#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING m_shader.stop_using(); +#if !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING if (m_picking_enabled) ::glEnable(GL_CULL_FACE); +#endif // !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING } else { @@ -6072,14 +6150,24 @@ void GLCanvas3D::_render_objects() const ::glEnable(GL_CLIP_PLANE1); } +#if !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING // do not cull backfaces to show broken geometry, if any if (m_picking_enabled) ::glDisable(GL_CULL_FACE); +#endif // !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING +#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING + // do not cull backfaces to show broken geometry, if any + m_volumes.render_legacy(GLVolumeCollection::Opaque, m_picking_enabled); + m_volumes.render_legacy(GLVolumeCollection::Transparent, false); +#else m_volumes.render_legacy(); +#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING +#if !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING if (m_picking_enabled) ::glEnable(GL_CULL_FACE); +#endif // !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING if (m_use_clipping_planes) { @@ -6097,6 +6185,14 @@ void GLCanvas3D::_render_selection() const m_selection.render(); } +#if ENABLE_RENDER_SELECTION_CENTER +void GLCanvas3D::_render_selection_center() const +{ + if (!m_gizmos.is_running()) + m_selection.render_center(); +} +#endif // ENABLE_RENDER_SELECTION_CENTER + void GLCanvas3D::_render_warning_texture() const { if (!m_warning_texture_enabled) @@ -6207,23 +6303,14 @@ void GLCanvas3D::_render_gizmos_overlay() const void GLCanvas3D::_render_toolbar() const { -#if !ENABLE_REMOVE_TABS_FROM_PLATER - _resize_toolbar(); -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.render(*this); -#else - m_toolbar.render(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } -#if ENABLE_REMOVE_TABS_FROM_PLATER void GLCanvas3D::_render_view_toolbar() const { if (m_view_toolbar != nullptr) m_view_toolbar->render(*this); } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER #if ENABLE_SHOW_CAMERA_TARGET void GLCanvas3D::_render_camera_target() const @@ -6234,7 +6321,6 @@ void GLCanvas3D::_render_camera_target() const ::glLineWidth(2.0f); ::glBegin(GL_LINES); -#if ENABLE_CONSTRAINED_CAMERA_TARGET const Vec3d& target = m_camera.get_target(); // draw line for x axis ::glColor3f(1.0f, 0.0f, 0.0f); @@ -6244,28 +6330,10 @@ void GLCanvas3D::_render_camera_target() const ::glColor3f(0.0f, 1.0f, 0.0f); ::glVertex3d(target(0), target(1) - half_length, target(2)); ::glVertex3d(target(0), target(1) + half_length, target(2)); - ::glEnd(); - - ::glBegin(GL_LINES); + // draw line for z axis ::glColor3f(0.0f, 0.0f, 1.0f); ::glVertex3d(target(0), target(1), target(2) - half_length); ::glVertex3d(target(0), target(1), target(2) + half_length); -#else - // draw line for x axis - ::glColor3f(1.0f, 0.0f, 0.0f); - ::glVertex3d(m_camera.target(0) - half_length, m_camera.target(1), m_camera.target(2)); - ::glVertex3d(m_camera.target(0) + half_length, m_camera.target(1), m_camera.target(2)); - // draw line for y axis - ::glColor3f(0.0f, 1.0f, 0.0f); - ::glVertex3d(m_camera.target(0), m_camera.target(1) - half_length, m_camera.target(2)); - ::glVertex3d(m_camera.target(0), m_camera.target(1) + half_length, m_camera.target(2)); - ::glEnd(); - - ::glBegin(GL_LINES); - ::glColor3f(0.0f, 0.0f, 1.0f); - ::glVertex3d(m_camera.target(0), m_camera.target(1), m_camera.target(2) - half_length); - ::glVertex3d(m_camera.target(0), m_camera.target(1), m_camera.target(2) + half_length); -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET ::glEnd(); } #endif // ENABLE_SHOW_CAMERA_TARGET @@ -6465,6 +6533,19 @@ void GLCanvas3D::_render_sla_slices() const } } +#if ENABLE_SIDEBAR_VISUAL_HINTS +void GLCanvas3D::_render_selection_sidebar_hints() const +{ + if (m_use_VBOs) + m_shader.start_using(); + + m_selection.render_sidebar_hints(m_sidebar_field); + + if (m_use_VBOs) + m_shader.stop_using(); +} +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + void GLCanvas3D::_update_volumes_hover_state() const { for (GLVolume* v : m_volumes.volumes) @@ -6515,7 +6596,6 @@ void GLCanvas3D::_update_gizmos_data() if (m_selection.is_single_full_instance()) { -#if ENABLE_MODELVOLUME_TRANSFORM // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()]; m_gizmos.set_scale(volume->get_instance_scaling_factor()); @@ -6531,24 +6611,7 @@ void GLCanvas3D::_update_gizmos_data() #else m_gizmos.set_model_object_ptr(model_object); #endif // ENABLE_SLA_SUPPORT_GIZMO_MOD -#else - ModelObject* model_object = m_model->objects[m_selection.get_object_idx()]; - ModelInstance* model_instance = model_object->instances[m_selection.get_instance_idx()]; - m_gizmos.set_scale(model_instance->get_scaling_factor()); -#if ENABLE_WORLD_ROTATIONS - m_gizmos.set_rotation(Vec3d::Zero()); -#else - m_gizmos.set_rotation(model_instance->get_rotation()); -#endif // ENABLE_WORLD_ROTATIONS - m_gizmos.set_flattening_data(model_object); -#if ENABLE_SLA_SUPPORT_GIZMO_MOD - m_gizmos.set_sla_support_data(model_object, m_selection); -#else - m_gizmos.set_model_object_ptr(model_object); -#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD -#endif // ENABLE_MODELVOLUME_TRANSFORM } -#if ENABLE_MODELVOLUME_TRANSFORM else if (m_selection.is_single_volume() || m_selection.is_single_modifier()) { const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()]; @@ -6565,7 +6628,6 @@ void GLCanvas3D::_update_gizmos_data() m_gizmos.set_model_object_ptr(nullptr); #endif // ENABLE_SLA_SUPPORT_GIZMO_MOD } -#endif // ENABLE_MODELVOLUME_TRANSFORM else { m_gizmos.set_scale(Vec3d::Ones()); @@ -7865,19 +7927,13 @@ bool GLCanvas3D::_is_any_volume_outside() const return false; } -#if ENABLE_REMOVE_TABS_FROM_PLATER void GLCanvas3D::_resize_toolbars() const -#else -void GLCanvas3D::_resize_toolbar() const -#endif // ENABLE_REMOVE_TABS_FROM_PLATER { Size cnv_size = get_canvas_size(); float zoom = get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE switch (m_toolbar.get_layout_type()) { @@ -7885,7 +7941,6 @@ void GLCanvas3D::_resize_toolbar() const case GLToolbar::Layout::Horizontal: { // centers the toolbar on the top edge of the 3d scene -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float top, left; if (orientation == GLToolbar::Layout::Top) { @@ -7897,17 +7952,12 @@ void GLCanvas3D::_resize_toolbar() const top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar->get_height()) * inv_zoom; left = -0.5f * m_toolbar.get_width() * inv_zoom; } -#else - float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - float left = -0.5f * m_toolbar.get_width() * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.set_position(top, left); break; } case GLToolbar::Layout::Vertical: { // centers the toolbar on the right edge of the 3d scene -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float top, left; if (orientation == GLToolbar::Layout::Left) { @@ -7919,16 +7969,11 @@ void GLCanvas3D::_resize_toolbar() const top = 0.5f * m_toolbar.get_height() * inv_zoom; left = (0.5f * (float)cnv_size.get_width() - m_toolbar.get_width()) * inv_zoom; } -#else - float top = 0.5f * m_toolbar.get_height() * inv_zoom; - float left = (0.5f * (float)cnv_size.get_width() - m_toolbar.get_width()) * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.set_position(top, left); break; } } -#if ENABLE_REMOVE_TABS_FROM_PLATER if (m_view_toolbar != nullptr) { // places the toolbar on the bottom-left corner of the 3d scene @@ -7936,7 +7981,6 @@ void GLCanvas3D::_resize_toolbar() const float left = -0.5f * (float)cnv_size.get_width() * inv_zoom; m_view_toolbar->set_position(top, left); } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } const Print* GLCanvas3D::fff_print() const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index fd732ddf1..73aa4bd02 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -20,12 +20,15 @@ class wxTimerEvent; class wxPaintEvent; class wxGLCanvas; +class GLUquadric; +typedef class GLUquadric GLUquadricObj; namespace Slic3r { class GLShader; class ExPolygon; class BackgroundSlicingProcess; +class GCodePreviewData; namespace GUI { @@ -94,18 +97,21 @@ template using Vec2dsEvent = ArrayEvent; using Vec3dEvent = Event; template using Vec3dsEvent = ArrayEvent; -#if ENABLE_REMOVE_TABS_FROM_PLATER wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent); wxDECLARE_EVENT(EVT_GLCANVAS_MODEL_UPDATE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event); // data: +1 => increase, -1 => decrease wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION +wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_ROTATED, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION wxDECLARE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); @@ -153,15 +159,10 @@ class GLCanvas3D float zoom; float phi; // float distance; -#if !ENABLE_CONSTRAINED_CAMERA_TARGET - Vec3d target; -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET private: -#if ENABLE_CONSTRAINED_CAMERA_TARGET Vec3d m_target; BoundingBoxf3 m_scene_box; -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET float m_theta; public: @@ -172,13 +173,11 @@ class GLCanvas3D float get_theta() const { return m_theta; } void set_theta(float theta); -#if ENABLE_CONSTRAINED_CAMERA_TARGET const Vec3d& get_target() const { return m_target; } void set_target(const Vec3d& target, GLCanvas3D& canvas); const BoundingBoxf3& get_scene_box() const { return m_scene_box; } void set_scene_box(const BoundingBoxf3& box, GLCanvas3D& canvas); -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET }; class Bed @@ -231,12 +230,20 @@ class GLCanvas3D struct Axes { + static const double Radius; + static const double ArrowBaseRadius; + static const double ArrowLength; Vec3d origin; - float length; + Vec3d length; + GLUquadricObj* m_quadric; Axes(); + ~Axes(); - void render(bool depth_test) const; + void render() const; + + private: + void render_axis(double length) const; }; class Shader @@ -362,14 +369,8 @@ public: enum EMode : unsigned char { -#if ENABLE_MODELVOLUME_TRANSFORM Volume, Instance -#else - Volume, - Instance, - Object -#endif // ENABLE_MODELVOLUME_TRANSFORM }; enum EType : unsigned char @@ -392,7 +393,6 @@ public: struct VolumeCache { private: -#if ENABLE_MODELVOLUME_TRANSFORM struct TransformCache { Vec3d position; @@ -409,24 +409,11 @@ public: TransformCache m_volume; TransformCache m_instance; -#else - Vec3d m_position; - Vec3d m_rotation; - Vec3d m_scaling_factor; - Transform3d m_rotation_matrix; - Transform3d m_scale_matrix; -#endif // ENABLE_MODELVOLUME_TRANSFORM public: -#if ENABLE_MODELVOLUME_TRANSFORM VolumeCache() {} VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform); -#else - VolumeCache(); - VolumeCache(const Vec3d& position, const Vec3d& rotation, const Vec3d& scaling_factor); -#endif // ENABLE_MODELVOLUME_TRANSFORM -#if ENABLE_MODELVOLUME_TRANSFORM const Vec3d& get_volume_position() const { return m_volume.position; } const Vec3d& get_volume_rotation() const { return m_volume.rotation; } const Vec3d& get_volume_scaling_factor() const { return m_volume.scaling_factor; } @@ -442,13 +429,6 @@ public: const Transform3d& get_instance_rotation_matrix() const { return m_instance.rotation_matrix; } const Transform3d& get_instance_scale_matrix() const { return m_instance.scale_matrix; } const Transform3d& get_instance_mirror_matrix() const { return m_instance.mirror_matrix; } -#else - const Vec3d& get_position() const { return m_position; } - const Vec3d& get_rotation() const { return m_rotation; } - const Vec3d& get_scaling_factor() const { return m_scaling_factor; } - const Transform3d& get_rotation_matrix() const { return m_rotation_matrix; } - const Transform3d& get_scale_matrix() const { return m_scale_matrix; } -#endif // ENABLE_MODELVOLUME_TRANSFORM }; typedef std::map VolumesCache; @@ -481,10 +461,24 @@ public: mutable BoundingBoxf3 m_bounding_box; mutable bool m_bounding_box_dirty; +#if ENABLE_RENDER_SELECTION_CENTER + GLUquadricObj* m_quadric; +#endif // ENABLE_RENDER_SELECTION_CENTER +#if ENABLE_SIDEBAR_VISUAL_HINTS + mutable GLArrow m_arrow; + mutable GLCurvedArrow m_curved_arrow; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + public: Selection(); +#if ENABLE_RENDER_SELECTION_CENTER + ~Selection(); +#endif // ENABLE_RENDER_SELECTION_CENTER void set_volumes(GLVolumePtrs* volumes); +#if ENABLE_SIDEBAR_VISUAL_HINTS + bool init(bool useVBOs); +#endif // ENABLE_SIDEBAR_VISUAL_HINTS Model* get_model() const { return m_model; } void set_model(Model* model); @@ -515,6 +509,7 @@ public: bool is_wipe_tower() const { return m_type == WipeTower; } bool is_modifier() const { return (m_type == SingleModifier) || (m_type == MultipleModifier); } bool is_single_modifier() const { return m_type == SingleModifier; } + bool is_multiple_modifier() const { return m_type == MultipleModifier; } bool is_single_full_instance() const; bool is_multiple_full_instance() const { return m_type == MultipleFullInstance; } bool is_single_full_object() const { return m_type == SingleFullObject; } @@ -526,6 +521,7 @@ public: bool is_from_single_object() const; bool contains_volume(unsigned int volume_idx) const { return std::find(m_list.begin(), m_list.end(), volume_idx) != m_list.end(); } + bool requires_uniform_scale() const; // Returns the the object id if the selection is from a single object, otherwise is -1 int get_object_idx() const; @@ -545,7 +541,7 @@ public: void start_dragging(); - void translate(const Vec3d& displacement); + void translate(const Vec3d& displacement, bool local = false); void rotate(const Vec3d& rotation, bool local); void flattening_rotate(const Vec3d& normal); void scale(const Vec3d& scale, bool local); @@ -557,6 +553,14 @@ public: void erase(); void render() const; +#if ENABLE_RENDER_SELECTION_CENTER + void render_center() const; +#endif // ENABLE_RENDER_SELECTION_CENTER +#if ENABLE_SIDEBAR_VISUAL_HINTS + void render_sidebar_hints(const std::string& sidebar_field) const; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + + bool requires_local_axes() const; private: void _update_valid(); @@ -572,6 +576,16 @@ public: void _render_selected_volumes() const; void _render_synchronized_volumes() const; void _render_bounding_box(const BoundingBoxf3& box, float* color) const; +#if ENABLE_SIDEBAR_VISUAL_HINTS + void _render_sidebar_position_hints(const std::string& sidebar_field) const; + void _render_sidebar_rotation_hints(const std::string& sidebar_field) const; + void _render_sidebar_scale_hints(const std::string& sidebar_field) const; + void _render_sidebar_size_hints(const std::string& sidebar_field) const; + void _render_sidebar_position_hint(Axis axis) const; + void _render_sidebar_rotation_hint(Axis axis) const; + void _render_sidebar_scale_hint(Axis axis) const; + void _render_sidebar_size_hint(Axis axis, double length) const; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS void _synchronize_unselected_instances(); void _synchronize_unselected_volumes(); #if ENABLE_ENSURE_ON_BED_WHILE_SCALING @@ -628,9 +642,7 @@ private: bool m_enabled; typedef std::map GizmosMap; GizmosMap m_gizmos; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE BackgroundTexture m_background_texture; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE EType m_current; public: @@ -699,9 +711,8 @@ private: void _render_current_gizmo(const Selection& selection) const; float _get_total_overlay_height() const; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float _get_total_overlay_width() const; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + GLGizmoBase* _get_current() const; }; @@ -774,16 +785,11 @@ private: Mouse m_mouse; mutable Gizmos m_gizmos; mutable GLToolbar m_toolbar; -#if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLToolbar* m_view_toolbar; -#else - GLRadioToolbar* m_view_toolbar; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#endif // ENABLE_REMOVE_TABS_FROM_PLATER ClippingPlane m_clipping_planes[2]; bool m_use_clipping_planes; mutable SlaCap m_sla_caps[2]; + std::string m_sidebar_field; mutable GLVolumeCollection m_volumes; Selection m_selection; @@ -803,7 +809,6 @@ private: bool m_legend_texture_enabled; bool m_picking_enabled; bool m_moving_enabled; - bool m_shader_enabled; bool m_dynamic_background_enabled; bool m_multisample_allowed; bool m_regenerate_volumes; @@ -819,10 +824,6 @@ private: wxWindow *m_external_gizmo_widgets_parent; #endif // not ENABLE_IMGUI -#if !ENABLE_CONSTRAINED_CAMERA_TARGET - void viewport_changed(); -#endif // !ENABLE_CONSTRAINED_CAMERA_TARGET - public: GLCanvas3D(wxGLCanvas* canvas); ~GLCanvas3D(); @@ -833,13 +834,7 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas; } -#if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_view_toolbar(GLToolbar* toolbar) { m_view_toolbar = toolbar; } -#else - void set_view_toolbar(GLRadioToolbar* toolbar) { m_view_toolbar = toolbar; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#endif // ENABLE_REMOVE_TABS_FROM_PLATER bool init(bool useVBOs, bool use_legacy_opengl); void post_event(wxEvent &&event); @@ -852,11 +847,7 @@ public: unsigned int get_volumes_count() const; void reset_volumes(); -#if ENABLE_REMOVE_TABS_FROM_PLATER int check_volumes_outside_state() const; -#else - int check_volumes_outside_state(const DynamicPrintConfig* config) const; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER void set_config(DynamicPrintConfig* config); void set_process(BackgroundSlicingProcess* process); @@ -870,8 +861,7 @@ public: // fills the m_bed.m_grid_lines and sets m_bed.m_origin. // Sets m_bed.m_polygon to limit the object placement. void set_bed_shape(const Pointfs& shape); - - void set_axes_length(float length); + void set_bed_axes_length(double length); void set_clipping_plane(unsigned int id, const ClippingPlane& plane) { @@ -888,9 +878,7 @@ public: float get_camera_zoom() const; BoundingBoxf3 volumes_bounding_box() const; -#if ENABLE_CONSTRAINED_CAMERA_TARGET BoundingBoxf3 scene_bounding_box() const; -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET bool is_layers_editing_enabled() const; bool is_layers_editing_allowed() const; @@ -904,7 +892,6 @@ public: void enable_moving(bool enable); void enable_gizmos(bool enable); void enable_toolbar(bool enable); - void enable_shader(bool enable); void enable_force_zoom_to_bed(bool enable); void enable_dynamic_background(bool enable); void allow_multisample(bool allow); @@ -914,9 +901,7 @@ public: void zoom_to_bed(); void zoom_to_volumes(); -#if ENABLE_MODIFIED_CAMERA_TARGET void zoom_to_selection(); -#endif // ENABLE_MODIFIED_CAMERA_TARGET void select_view(const std::string& direction); void set_viewport_from_scene(const GLCanvas3D& other); @@ -982,11 +967,9 @@ public: void update_gizmos_on_off_state(); -#if ENABLE_CONSTRAINED_CAMERA_TARGET void viewport_changed(); -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET - void handle_sidebar_focus_event(const std::string& opt_key) {} + void handle_sidebar_focus_event(const std::string& opt_key, bool focus_on); private: bool _is_shown_on_screen() const; @@ -1011,9 +994,12 @@ private: void _picking_pass() const; void _render_background() const; void _render_bed(float theta) const; - void _render_axes(bool depth_test) const; + void _render_axes() const; void _render_objects() const; void _render_selection() const; +#if ENABLE_RENDER_SELECTION_CENTER + void _render_selection_center() const; +#endif // ENABLE_RENDER_SELECTION_CENTER void _render_warning_texture() const; void _render_legend_texture() const; void _render_layer_editing_overlay() const; @@ -1021,13 +1007,14 @@ private: void _render_current_gizmo() const; void _render_gizmos_overlay() const; void _render_toolbar() const; -#if ENABLE_REMOVE_TABS_FROM_PLATER void _render_view_toolbar() const; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER #if ENABLE_SHOW_CAMERA_TARGET void _render_camera_target() const; #endif // ENABLE_SHOW_CAMERA_TARGET void _render_sla_slices() const; +#if ENABLE_SIDEBAR_VISUAL_HINTS + void _render_selection_sidebar_hints() const; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS void _update_volumes_hover_state() const; void _update_gizmos_data(); @@ -1087,14 +1074,11 @@ private: bool _is_any_volume_outside() const; -#if ENABLE_REMOVE_TABS_FROM_PLATER void _resize_toolbars() const; -#else - void _resize_toolbar() const; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER static std::vector _parse_colors(const std::vector& colors); +public: const Print* fff_print() const; const SLAPrint* sla_print() const; }; diff --git a/src/slic3r/GUI/GLCanvas3DManager.hpp b/src/slic3r/GUI/GLCanvas3DManager.hpp index 1ed4a8251..64301c73d 100644 --- a/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -19,7 +19,6 @@ class ExPolygon; typedef std::vector ExPolygons; class ModelObject; class PrintObject; -class GCodePreviewData; namespace GUI { diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 7f62d0901..7de065065 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -8,6 +8,7 @@ #include "libslic3r/Geometry.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/SLA/SLASupportTree.hpp" +#include "libslic3r/SLAPrint.hpp" #include #include @@ -859,14 +860,10 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const bool single_selection = single_instance || single_volume; Vec3f scale = 100.0f * Vec3f::Ones(); -#if ENABLE_MODELVOLUME_TRANSFORM if (single_instance) scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor().cast(); else if (single_volume) scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_scaling_factor().cast(); -#else - Vec3f scale = single_instance ? 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_scaling_factor().cast() : 100.0f * m_scale.cast(); -#endif // ENABLE_MODELVOLUME_TRANSFORM if ((single_selection && ((m_hover_id == 0) || (m_hover_id == 1))) || m_grabbers[0].dragging || m_grabbers[1].dragging) set_tooltip("X: " + format(scale(0), 4) + "%"); @@ -914,37 +911,22 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const // gets transform from first selected volume const GLVolume* v = selection.get_volume(*idxs.begin()); -#if ENABLE_MODELVOLUME_TRANSFORM transform = v->get_instance_transformation().get_matrix(); // gets angles from first selected volume angles = v->get_instance_rotation(); // consider rotation+mirror only components of the transform for offsets offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); grabber_size = v->get_instance_transformation().get_matrix(true, true, false, true) * box.size(); -#else - transform = v->world_matrix().cast(); - // gets angles from first selected volume - angles = v->get_rotation(); - // consider rotation+mirror only components of the transform for offsets - offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_mirror()); -#endif // ENABLE_MODELVOLUME_TRANSFORM } else if (single_volume) { const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); box = v->bounding_box; -#if ENABLE_MODELVOLUME_TRANSFORM transform = v->world_matrix(); angles = Geometry::extract_euler_angles(transform); // consider rotation+mirror only components of the transform for offsets offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); grabber_size = v->get_volume_transformation().get_matrix(true, true, false, true) * box.size(); -#else - transform = v->world_matrix().cast(); - angles = Geometry::extract_euler_angles(transform); - // consider rotation+mirror only components of the transform for offsets - offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_mirror()); -#endif // ENABLE_MODELVOLUME_TRANSFORM } else { @@ -1465,6 +1447,7 @@ void GLGizmoFlatten::on_render(const GLCanvas3D::Selection& selection) const const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); ::glPushMatrix(); ::glMultMatrixd(m.data()); + ::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z()); for (int i = 0; i < (int)m_planes.size(); ++i) { if (i == m_hover_id) @@ -1496,6 +1479,7 @@ void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selectio const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); ::glPushMatrix(); ::glMultMatrixd(m.data()); + ::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z()); for (int i = 0; i < (int)m_planes.size(); ++i) { ::glColor3f(1.0f, 1.0f, picking_color_component(i)); @@ -1526,7 +1510,6 @@ void GLGizmoFlatten::update_planes() { TriangleMesh ch; for (const ModelVolume* vol : m_model_object->volumes) -#if ENABLE_MODELVOLUME_TRANSFORM { if (vol->type() != ModelVolume::Type::MODEL_PART) continue; @@ -1534,9 +1517,6 @@ void GLGizmoFlatten::update_planes() vol_ch.transform(vol->get_matrix()); ch.merge(vol_ch); } -#else - ch.merge(vol->get_convex_hull()); -#endif // ENABLE_MODELVOLUME_TRANSFORM ch = ch.convex_hull_3d(); @@ -1786,6 +1766,18 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const G { if (is_mesh_update_necessary()) update_mesh(); + + // If there are no points, let's ask the backend if it calculated some. + if (model_object->sla_support_points.empty() && m_parent.sla_print()->is_step_done(slaposSupportPoints)) { + for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { + if (po->model_object()->id() == model_object->id()) { + const Eigen::MatrixXd& points = po->get_support_points(); + for (unsigned int i=0; isla_support_points.push_back(Vec3f(po->trafo().inverse().cast() * Vec3f(points(i,0), points(i,1), points(i,2)))); + break; + } + } + } } } #else @@ -2170,6 +2162,9 @@ void GLGizmoSlaSupports::render_tooltip_texture() const { #if ENABLE_IMGUI void GLGizmoSlaSupports::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) { + bool first_run = true; // This is a hack to redraw the button when all points are removed, + // so it is not delayed until the background process finishes. +RENDER_AGAIN: m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); m_imgui->set_next_window_bg_alpha(0.5f); m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); @@ -2184,22 +2179,11 @@ void GLGizmoSlaSupports::on_render_input_window(float x, float y, const GLCanvas m_imgui->end(); - if (remove_all_clicked) + if (remove_all_clicked) { delete_current_grabber(true); - - if (generate) { - const DynamicPrintConfig& cfg = *wxGetApp().get_tab(Preset::TYPE_SLA_PRINT)->get_config(); - SLAAutoSupports::Config config; - config.density_at_horizontal = cfg.opt_int("support_density_at_horizontal") / 10000.f; - config.density_at_45 = cfg.opt_int("support_density_at_45") / 10000.f; - config.minimal_z = cfg.opt_float("support_minimal_z"); - - SLAAutoSupports sas(*m_model_object, config); - sas.generate(); - m_grabbers.clear(); - for (const Vec3f& point : m_model_object->sla_support_points) { - m_grabbers.push_back(Grabber()); - m_grabbers.back().center = point.cast(); + if (first_run) { + first_run = false; + goto RENDER_AGAIN; } } diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index bfec0965e..2588080b2 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -436,7 +436,6 @@ protected: class GLGizmoSlaSupports : public GLGizmoBase { private: - SLAAutoSupports* m_sas = nullptr; ModelObject* m_model_object = nullptr; #if ENABLE_SLA_SUPPORT_GIZMO_MOD ModelObject* m_old_model_object = nullptr; diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index 235e3d93b..0ac7e983e 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -36,7 +36,7 @@ bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmap // Load a PNG with an alpha channel. wxImage image; - if (!image.LoadFile(filename, wxBITMAP_TYPE_PNG)) + if (!image.LoadFile(wxString::FromUTF8(filename), wxBITMAP_TYPE_PNG)) { reset(); return false; diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 43da81809..0efc19dee 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -75,12 +75,10 @@ bool GLToolbarItem::is_enabled() const return m_state != Disabled; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool GLToolbarItem::is_disabled() const { return m_state == Disabled; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool GLToolbarItem::is_hovered() const { @@ -131,7 +129,6 @@ GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int texture_size, unsigned i return uvs; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE ItemsIconsTexture::Metadata::Metadata() : filename("") , icon_size(0) @@ -139,18 +136,7 @@ ItemsIconsTexture::Metadata::Metadata() , icon_gap_size(0) { } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#if !ENABLE_TOOLBAR_BACKGROUND_TEXTURE -ItemsIconsTexture::ItemsIconsTexture() - : items_icon_size(0) - , items_icon_border_size(0) - , items_icon_gap_size(0) -{ -} -#endif // !ENABLE_TOOLBAR_BACKGROUND_TEXTURE - -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE BackgroundTexture::Metadata::Metadata() : filename("") , left(0) @@ -159,36 +145,24 @@ BackgroundTexture::Metadata::Metadata() , bottom(0) { } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLToolbar::Layout::Layout() : type(Horizontal) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE , orientation(Center) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE , top(0.0f) , left(0.0f) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE , border(0.0f) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE , separator_size(0.0f) , gap_size(0.0f) , icons_scale(1.0f) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE , width(0.0f) , height(0.0f) , dirty(true) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLToolbar::GLToolbar(GLToolbar::EType type) : m_type(type) -#else -GLToolbar::GLToolbar(GLCanvas3D& parent) - : m_parent(parent) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE , m_enabled(false) { } @@ -201,7 +175,6 @@ GLToolbar::~GLToolbar() } } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool GLToolbar::init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture) { if (m_icons_texture.texture.get_id() != 0) @@ -220,21 +193,6 @@ bool GLToolbar::init(const ItemsIconsTexture::Metadata& icons_texture, const Bac return res; } -#else -bool GLToolbar::init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size) -{ - std::string path = resources_dir() + "/icons/"; - bool res = !icons_texture_filename.empty() && m_icons_texture.texture.load_from_file(path + icons_texture_filename, false); - if (res) - { - m_icons_texture.items_icon_size = items_icon_size; - m_icons_texture.items_icon_border_size = items_icon_border_size; - m_icons_texture.items_icon_gap_size = items_icon_gap_size; - } - - return res; -} -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLToolbar::Layout::EType GLToolbar::get_layout_type() const { @@ -244,12 +202,9 @@ GLToolbar::Layout::EType GLToolbar::get_layout_type() const void GLToolbar::set_layout_type(GLToolbar::Layout::EType type) { m_layout.type = type; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_layout.dirty = true; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLToolbar::Layout::EOrientation GLToolbar::get_layout_orientation() const { return m_layout.orientation; @@ -259,7 +214,6 @@ void GLToolbar::set_layout_orientation(GLToolbar::Layout::EOrientation orientati { m_layout.orientation = orientation; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::set_position(float top, float left) { @@ -267,36 +221,28 @@ void GLToolbar::set_position(float top, float left) m_layout.left = left; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::set_border(float border) { m_layout.border = border; m_layout.dirty = true; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::set_separator_size(float size) { m_layout.separator_size = size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_layout.dirty = true; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } void GLToolbar::set_gap_size(float size) { m_layout.gap_size = size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_layout.dirty = true; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } void GLToolbar::set_icons_scale(float scale) { m_layout.icons_scale = scale; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_layout.dirty = true; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } bool GLToolbar::is_enabled() const @@ -316,9 +262,7 @@ bool GLToolbar::add_item(const GLToolbarItem::Data& data) return false; m_items.push_back(item); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_layout.dirty = true; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE return true; } @@ -330,56 +274,24 @@ bool GLToolbar::add_separator() return false; m_items.push_back(item); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_layout.dirty = true; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE return true; } float GLToolbar::get_width() const { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE if (m_layout.dirty) calc_layout(); return m_layout.width; -#else - switch (m_layout.type) - { - default: - case Layout::Horizontal: - { - return get_width_horizontal(); - } - case Layout::Vertical: - { - return get_width_vertical(); - } - } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } float GLToolbar::get_height() const { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE if (m_layout.dirty) calc_layout(); return m_layout.height; -#else - switch (m_layout.type) - { - default: - case Layout::Horizontal: - { - return get_height_horizontal(); - } - case Layout::Vertical: - { - return get_height_vertical(); - } - } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } void GLToolbar::enable_item(const std::string& name) @@ -406,7 +318,6 @@ void GLToolbar::disable_item(const std::string& name) } } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::select_item(const std::string& name) { if (is_item_disabled(name)) @@ -421,7 +332,6 @@ void GLToolbar::select_item(const std::string& name) } } } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool GLToolbar::is_item_pressed(const std::string& name) const { @@ -434,7 +344,6 @@ bool GLToolbar::is_item_pressed(const std::string& name) const return false; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool GLToolbar::is_item_disabled(const std::string& name) const { for (GLToolbarItem* item : m_items) @@ -445,58 +354,21 @@ bool GLToolbar::is_item_disabled(const std::string& name) const return false; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) -#else -std::string GLToolbar::update_hover_state(const Vec2d& mouse_pos) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE -void GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) -#else -void GLToolbar::update_hover_state(const Vec2d& mouse_pos) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#endif // ENABLE_REMOVE_TABS_FROM_PLATER { -#if ENABLE_REMOVE_TABS_FROM_PLATER if (!m_enabled) return ""; -#else - if (!m_enabled) - return; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER switch (m_layout.type) { default: -#if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE case Layout::Horizontal: { return update_hover_state_horizontal(mouse_pos, parent); } case Layout::Vertical: { return update_hover_state_vertical(mouse_pos, parent); } -#else - case Layout::Horizontal: { return update_hover_state_horizontal(mouse_pos); } - case Layout::Vertical: { return update_hover_state_vertical(mouse_pos); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE - case Layout::Horizontal: { update_hover_state_horizontal(mouse_pos, parent); break; } - case Layout::Vertical: { update_hover_state_vertical(mouse_pos, parent); break; } -#else - case Layout::Horizontal: { update_hover_state_horizontal(mouse_pos); break; } - case Layout::Vertical: { update_hover_state_vertical(mouse_pos); break; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE int GLToolbar::contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const -#else -int GLToolbar::contains_mouse(const Vec2d& mouse_pos) const -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { if (!m_enabled) return -1; @@ -504,21 +376,12 @@ int GLToolbar::contains_mouse(const Vec2d& mouse_pos) const switch (m_layout.type) { default: -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE case Layout::Horizontal: { return contains_mouse_horizontal(mouse_pos, parent); } case Layout::Vertical: { return contains_mouse_vertical(mouse_pos, parent); } -#else - case Layout::Horizontal: { return contains_mouse_horizontal(mouse_pos); } - case Layout::Vertical: { return contains_mouse_vertical(mouse_pos); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent) -#else -void GLToolbar::do_action(unsigned int item_id) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { if (item_id < (unsigned int)m_items.size()) { @@ -533,51 +396,30 @@ void GLToolbar::do_action(unsigned int item_id) else if (state == GLToolbarItem::HoverPressed) item->set_state(GLToolbarItem::Hover); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.render(); item->do_action(parent.get_wxglcanvas()); -#else - m_parent.render(); - item->do_action(m_parent.get_wxglcanvas()); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } else { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE if (m_type == Radio) select_item(item->get_name()); else -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::HoverPressed); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.render(); item->do_action(parent.get_wxglcanvas()); if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled)) -#else - m_parent.render(); - item->do_action(m_parent.get_wxglcanvas()); - if (item->get_state() != GLToolbarItem::Disabled) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { // the item may get disabled during the action, if not, set it back to hover state item->set_state(GLToolbarItem::Hover); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.render(); -#else - m_parent.render(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } } } } } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::render(const GLCanvas3D& parent) const -#else -void GLToolbar::render() const -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { if (!m_enabled || m_items.empty()) return; @@ -590,19 +432,13 @@ void GLToolbar::render() const switch (m_layout.type) { default: -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE case Layout::Horizontal: { render_horizontal(parent); break; } case Layout::Vertical: { render_vertical(parent); break; } -#else - case Layout::Horizontal: { render_horizontal(); break; } - case Layout::Vertical: { render_vertical(); break; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } ::glPopMatrix(); } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::calc_layout() const { switch (m_layout.type) @@ -624,7 +460,6 @@ void GLToolbar::calc_layout() const m_layout.dirty = false; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float GLToolbar::get_width_horizontal() const { @@ -633,20 +468,12 @@ float GLToolbar::get_width_horizontal() const float GLToolbar::get_width_vertical() const { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE return 2.0f * m_layout.border + m_icons_texture.metadata.icon_size * m_layout.icons_scale; -#else - return m_icons_texture.items_icon_size * m_layout.icons_scale; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } float GLToolbar::get_height_horizontal() const { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE return 2.0f * m_layout.border + m_icons_texture.metadata.icon_size * m_layout.icons_scale; -#else - return m_icons_texture.items_icon_size * m_layout.icons_scale; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } float GLToolbar::get_height_vertical() const @@ -656,21 +483,13 @@ float GLToolbar::get_height_vertical() const float GLToolbar::get_main_size() const { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float size = 2.0f * m_layout.border; -#else - float size = 0.0f; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) { if (m_items[i]->is_separator()) size += m_layout.separator_size; else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE size += (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale; -#else - size += (float)m_icons_texture.items_icon_size * m_layout.icons_scale; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } if (m_items.size() > 1) @@ -679,55 +498,24 @@ float GLToolbar::get_main_size() const return size; } -#if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent) -#else -std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE -void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent) -#else -void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#endif // ENABLE_REMOVE_TABS_FROM_PLATER { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float zoom = parent.get_camera_zoom(); -#else - float zoom = m_parent.get_camera_zoom(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE Size cnv_size = parent.get_canvas_size(); -#else - Size cnv_size = m_parent.get_canvas_size(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; -#else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_border = m_layout.border * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float left = m_layout.left + scaled_border; float top = m_layout.top - scaled_border; -#else - float left = m_layout.left; - float top = m_layout.top; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string tooltip = ""; @@ -748,14 +536,10 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) case GLToolbarItem::Normal: { if (inside) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Hover); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -764,28 +548,20 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) if (inside) tooltip = item->get_tooltip(); else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Normal); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } case GLToolbarItem::Pressed: { if (inside) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::HoverPressed); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -794,14 +570,10 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) if (inside) tooltip = item->get_tooltip(); else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Pressed); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -816,63 +588,27 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) } } -#if ENABLE_REMOVE_TABS_FROM_PLATER return tooltip; -#else - if (!tooltip.empty()) - m_parent.set_tooltip(tooltip); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } -#if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent) -#else -std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE -void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent) -#else -void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#endif // ENABLE_REMOVE_TABS_FROM_PLATER { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float zoom = parent.get_camera_zoom(); -#else - float zoom = m_parent.get_camera_zoom(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE Size cnv_size = parent.get_canvas_size(); -#else - Size cnv_size = m_parent.get_canvas_size(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; -#else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_border = m_layout.border * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float left = m_layout.left + scaled_border; float top = m_layout.top - scaled_border; -#else - float left = m_layout.left; - float top = m_layout.top; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string tooltip = ""; @@ -893,14 +629,10 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) case GLToolbarItem::Normal: { if (inside) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Hover); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -909,28 +641,20 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) if (inside) tooltip = item->get_tooltip(); else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Normal); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } case GLToolbarItem::Pressed: { if (inside) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::HoverPressed); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -939,14 +663,10 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) if (inside) tooltip = item->get_tooltip(); else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Pressed); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -961,54 +681,27 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) } } -#if ENABLE_REMOVE_TABS_FROM_PLATER return tooltip; -#else - m_parent.set_tooltip(tooltip); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const -#else -int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos) const -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float zoom = parent.get_camera_zoom(); -#else - float zoom = m_parent.get_camera_zoom(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE Size cnv_size = parent.get_canvas_size(); -#else - Size cnv_size = m_parent.get_canvas_size(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; -#else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_border = m_layout.border * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float left = m_layout.left + scaled_border; float top = m_layout.top - scaled_border; -#else - float left = m_layout.left; - float top = m_layout.top; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE int id = -1; @@ -1033,47 +726,24 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos) const return -1; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const -#else -int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos) const -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float zoom = parent.get_camera_zoom(); -#else - float zoom = m_parent.get_camera_zoom(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE Size cnv_size = parent.get_canvas_size(); -#else - Size cnv_size = m_parent.get_canvas_size(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; -#else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_border = m_layout.border * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float left = m_layout.left + scaled_border; float top = m_layout.top - scaled_border; -#else - float left = m_layout.left; - float top = m_layout.top; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE int id = -1; @@ -1098,11 +768,7 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos) const return -1; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::render_horizontal(const GLCanvas3D& parent) const -#else -void GLToolbar::render_horizontal() const -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { unsigned int tex_id = m_icons_texture.texture.get_id(); int tex_size = m_icons_texture.texture.get_width(); @@ -1110,30 +776,19 @@ void GLToolbar::render_horizontal() const if ((tex_id == 0) || (tex_size <= 0)) return; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float zoom = parent.get_camera_zoom(); -#else - float zoom = m_parent.get_camera_zoom(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; -#else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_border = m_layout.border * inv_zoom; float scaled_width = get_width() * inv_zoom; float scaled_height = get_height() * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float left = m_layout.left; float top = m_layout.top; float right = left + scaled_width; @@ -1224,10 +879,6 @@ void GLToolbar::render_horizontal() const left += scaled_border; top -= scaled_border; -#else - float left = m_layout.left; - float top = m_layout.top; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE // renders icons for (const GLToolbarItem* item : m_items) @@ -1236,21 +887,13 @@ void GLToolbar::render_horizontal() const left += separator_stride; else { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.metadata.icon_border_size, m_icons_texture.metadata.icon_size, m_icons_texture.metadata.icon_gap_size); -#else - item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.items_icon_border_size, m_icons_texture.items_icon_size, m_icons_texture.items_icon_gap_size); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE left += icon_stride; } } } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::render_vertical(const GLCanvas3D& parent) const -#else -void GLToolbar::render_vertical() const -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { unsigned int tex_id = m_icons_texture.texture.get_id(); int tex_size = m_icons_texture.texture.get_width(); @@ -1258,30 +901,19 @@ void GLToolbar::render_vertical() const if ((tex_id == 0) || (tex_size <= 0)) return; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float zoom = parent.get_camera_zoom(); -#else - float zoom = m_parent.get_camera_zoom(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; -#else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_border = m_layout.border * inv_zoom; float scaled_width = get_width() * inv_zoom; float scaled_height = get_height() * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float left = m_layout.left; float top = m_layout.top; float right = left + scaled_width; @@ -1372,10 +1004,6 @@ void GLToolbar::render_vertical() const left += scaled_border; top -= scaled_border; -#else - float left = m_layout.left; - float top = m_layout.top; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE // renders icons for (const GLToolbarItem* item : m_items) @@ -1384,321 +1012,11 @@ void GLToolbar::render_vertical() const top -= separator_stride; else { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.metadata.icon_border_size, m_icons_texture.metadata.icon_size, m_icons_texture.metadata.icon_gap_size); -#else - item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.items_icon_border_size, m_icons_texture.items_icon_size, m_icons_texture.items_icon_gap_size); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE top -= icon_stride; } } } -#if !ENABLE_TOOLBAR_BACKGROUND_TEXTURE -GLRadioToolbarItem::Data::Data() - : name("") - , tooltip("") - , sprite_id(-1) -{ -} - -GLRadioToolbarItem::GLRadioToolbarItem(const GLRadioToolbarItem::Data& data) - : m_state(Normal) - , m_data(data) -{ -} - -GLRadioToolbarItem::EState GLRadioToolbarItem::get_state() const -{ - return m_state; -} - -void GLRadioToolbarItem::set_state(GLRadioToolbarItem::EState state) -{ - m_state = state; -} - -const std::string& GLRadioToolbarItem::get_name() const -{ - return m_data.name; -} - -const std::string& GLRadioToolbarItem::get_tooltip() const -{ - return m_data.tooltip; -} - -bool GLRadioToolbarItem::is_hovered() const -{ - return (m_state == Hover) || (m_state == HoverPressed); -} - -bool GLRadioToolbarItem::is_pressed() const -{ - return (m_state == Pressed) || (m_state == HoverPressed); -} - -void GLRadioToolbarItem::do_action(wxEvtHandler *target) -{ - wxPostEvent(target, SimpleEvent(m_data.action_event)); -} - -void GLRadioToolbarItem::render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const -{ - GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(texture_size, border_size, icon_size, gap_size)); -} - -GLTexture::Quad_UVs GLRadioToolbarItem::get_uvs(unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const -{ - GLTexture::Quad_UVs uvs; - - float inv_texture_size = (texture_size != 0) ? 1.0f / (float)texture_size : 0.0f; - - float scaled_icon_size = (float)icon_size * inv_texture_size; - float scaled_border_size = (float)border_size * inv_texture_size; - float scaled_gap_size = (float)gap_size * inv_texture_size; - float stride = scaled_icon_size + scaled_gap_size; - - float left = scaled_border_size + (float)m_state * stride; - float right = left + scaled_icon_size; - float top = scaled_border_size + (float)m_data.sprite_id * stride; - float bottom = top + scaled_icon_size; - - uvs.left_top = { left, top }; - uvs.left_bottom = { left, bottom }; - uvs.right_bottom = { right, bottom }; - uvs.right_top = { right, top }; - - return uvs; -} - -GLRadioToolbar::GLRadioToolbar() - : m_top(0.0f) - , m_left(0.0f) -{ -} - -GLRadioToolbar::~GLRadioToolbar() -{ - for (GLRadioToolbarItem* item : m_items) - { - delete item; - } -} - -bool GLRadioToolbar::init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size) -{ - if (m_icons_texture.texture.get_id() != 0) - return true; - - std::string path = resources_dir() + "/icons/"; - bool res = !icons_texture_filename.empty() && m_icons_texture.texture.load_from_file(path + icons_texture_filename, false); - if (res) - { - m_icons_texture.items_icon_size = items_icon_size; - m_icons_texture.items_icon_border_size = items_icon_border_size; - m_icons_texture.items_icon_gap_size = items_icon_gap_size; - } - - return res; -} - -bool GLRadioToolbar::add_item(const GLRadioToolbarItem::Data& data) -{ - GLRadioToolbarItem* item = new GLRadioToolbarItem(data); - if (item == nullptr) - return false; - - m_items.push_back(item); - return true; -} - -float GLRadioToolbar::get_height() const -{ - return m_icons_texture.items_icon_size; -} - -void GLRadioToolbar::set_position(float top, float left) -{ - m_top = top; - m_left = left; -} - -void GLRadioToolbar::set_selection(const std::string& name) -{ - for (GLRadioToolbarItem* item : m_items) - { - item->set_state((item->get_name() == name) ? GLRadioToolbarItem::Pressed : GLRadioToolbarItem::Normal); - } -} - -int GLRadioToolbar::contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const -{ - float zoom = parent.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - Size cnv_size = parent.get_canvas_size(); - Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); - - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; - - float left = m_left; - float top = m_top; - - int id = -1; - - for (GLRadioToolbarItem* item : m_items) - { - ++id; - - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; - - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) - return id; - - left += scaled_icons_size; - } - - return -1; -} - -std::string GLRadioToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) -{ - float zoom = parent.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - Size cnv_size = parent.get_canvas_size(); - Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); - - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; - - float left = m_left; - float top = m_top; - - std::string tooltip = ""; - - for (GLRadioToolbarItem* item : m_items) - { - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; - - GLRadioToolbarItem::EState state = item->get_state(); - bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top); - - switch (state) - { - case GLRadioToolbarItem::Normal: - { - if (inside) - { - item->set_state(GLRadioToolbarItem::Hover); - parent.set_as_dirty(); - } - - break; - } - case GLRadioToolbarItem::Hover: - { - if (inside) - tooltip = item->get_tooltip(); - else - { - item->set_state(GLRadioToolbarItem::Normal); - parent.set_as_dirty(); - } - - break; - } - case GLRadioToolbarItem::Pressed: - { - if (inside) - { - item->set_state(GLRadioToolbarItem::HoverPressed); - parent.set_as_dirty(); - } - - break; - } - case GLRadioToolbarItem::HoverPressed: - { - if (inside) - tooltip = item->get_tooltip(); - else - { - item->set_state(GLRadioToolbarItem::Pressed); - parent.set_as_dirty(); - } - - break; - } - default: - { - break; - } - } - - left += scaled_icons_size; - } - - return tooltip; -} - -void GLRadioToolbar::do_action(unsigned int item_id, GLCanvas3D& parent) -{ - for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) - { - if (i != item_id) - m_items[i]->set_state(GLRadioToolbarItem::Normal); - } - - if (item_id < (unsigned int)m_items.size()) - { - GLRadioToolbarItem* item = m_items[item_id]; - if ((item != nullptr) && item->is_hovered() && !item->is_pressed()) - { - item->set_state(GLRadioToolbarItem::HoverPressed); - item->do_action(parent.get_wxglcanvas()); - } - } - - parent.set_as_dirty(); -} - -void GLRadioToolbar::render(const GLCanvas3D& parent) const -{ - if (m_items.empty()) - return; - - ::glDisable(GL_DEPTH_TEST); - - ::glPushMatrix(); - ::glLoadIdentity(); - - unsigned int tex_id = m_icons_texture.texture.get_id(); - int tex_size = m_icons_texture.texture.get_width(); - - if ((tex_id == 0) || (tex_size <= 0)) - return; - - float zoom = parent.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; - - float left = m_left; - float top = m_top; - - // renders icons - for (const GLRadioToolbarItem* item : m_items) - { - item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.items_icon_border_size, m_icons_texture.items_icon_size, m_icons_texture.items_icon_gap_size); - left += scaled_icons_size; - } - - ::glPopMatrix(); -} -#endif // !ENABLE_TOOLBAR_BACKGROUND_TEXTURE - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 430d4a844..37eef5708 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -77,9 +77,7 @@ public: void do_action(wxEvtHandler *target); bool is_enabled() const; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool is_disabled() const; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool is_hovered() const; bool is_pressed() const; @@ -97,7 +95,6 @@ private: // from left to right struct ItemsIconsTexture { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE struct Metadata { // path of the file containing the icons' texture @@ -111,23 +108,11 @@ struct ItemsIconsTexture Metadata(); }; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE - GLTexture texture; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE - Metadata metadata; -#else - // size of the square icons, in pixels - unsigned int items_icon_size; - // distance from the border, in pixels - unsigned int items_icon_border_size; - // distance between two adjacent icons (to avoid filtering artifacts), in pixels - unsigned int items_icon_gap_size; - ItemsIconsTexture(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + GLTexture texture; + Metadata metadata; }; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE struct BackgroundTexture { struct Metadata @@ -149,19 +134,16 @@ struct BackgroundTexture GLTexture texture; Metadata metadata; }; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE class GLToolbar { public: -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE enum EType : unsigned char { Normal, Radio, Num_Types }; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE struct Layout { @@ -183,23 +165,17 @@ public: }; EType type; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE EOrientation orientation; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float top; float left; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float border; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_size; float gap_size; float icons_scale; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float width; float height; bool dirty; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Layout(); }; @@ -207,47 +183,27 @@ public: private: typedef std::vector ItemsList; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE EType m_type; -#else - GLCanvas3D& m_parent; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool m_enabled; ItemsIconsTexture m_icons_texture; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE BackgroundTexture m_background_texture; mutable Layout m_layout; -#else - Layout m_layout; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE ItemsList m_items; public: -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE explicit GLToolbar(EType type); -#else - explicit GLToolbar(GLCanvas3D& parent); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE ~GLToolbar(); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture); -#else - bool init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Layout::EType get_layout_type() const; void set_layout_type(Layout::EType type); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE Layout::EOrientation get_layout_orientation() const; void set_layout_orientation(Layout::EOrientation orientation); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_position(float top, float left); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_border(float border); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_separator_size(float size); void set_gap_size(float size); void set_icons_scale(float scale); @@ -263,169 +219,36 @@ public: void enable_item(const std::string& name); void disable_item(const std::string& name); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void select_item(const std::string& name); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool is_item_pressed(const std::string& name) const; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool is_item_disabled(const std::string& name) const; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); -#else - std::string update_hover_state(const Vec2d& mouse_pos); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE - void update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); -#else - void update_hover_state(const Vec2d& mouse_pos); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#endif // ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE // returns the id of the item under the given mouse position or -1 if none int contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; void do_action(unsigned int item_id, GLCanvas3D& parent); -#else - // returns the id of the item under the given mouse position or -1 if none - int contains_mouse(const Vec2d& mouse_pos) const; - void do_action(unsigned int item_id); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE - -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void render(const GLCanvas3D& parent) const; -#else - void render() const; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE private: -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void calc_layout() const; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float get_width_horizontal() const; float get_width_vertical() const; float get_height_horizontal() const; float get_height_vertical() const; float get_main_size() const; -#if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent); std::string update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent); -#else - std::string update_hover_state_horizontal(const Vec2d& mouse_pos); - std::string update_hover_state_vertical(const Vec2d& mouse_pos); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE - void update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent); - void update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent); -#else - void update_hover_state_horizontal(const Vec2d& mouse_pos); - void update_hover_state_vertical(const Vec2d& mouse_pos); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#endif // ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE int contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; int contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; void render_horizontal(const GLCanvas3D& parent) const; void render_vertical(const GLCanvas3D& parent) const; -#else - int contains_mouse_horizontal(const Vec2d& mouse_pos) const; - int contains_mouse_vertical(const Vec2d& mouse_pos) const; - - void render_horizontal() const; - void render_vertical() const; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE }; -#if !ENABLE_TOOLBAR_BACKGROUND_TEXTURE -class GLRadioToolbarItem -{ -public: - struct Data - { - std::string name; - std::string tooltip; - unsigned int sprite_id; - wxEventType action_event; - - Data(); - }; - - enum EState : unsigned char - { - Normal, - Pressed, - Hover, - HoverPressed, - Num_States - }; - -private: - EState m_state; - Data m_data; - -public: - GLRadioToolbarItem(const Data& data); - - EState get_state() const; - void set_state(EState state); - - const std::string& get_name() const; - const std::string& get_tooltip() const; - - bool is_hovered() const; - bool is_pressed() const; - - void do_action(wxEvtHandler *target); - - void render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const; - -private: - GLTexture::Quad_UVs get_uvs(unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const; -}; - -class GLRadioToolbar -{ - typedef std::vector ItemsList; - - ItemsIconsTexture m_icons_texture; - - ItemsList m_items; - float m_top; - float m_left; - -public: - GLRadioToolbar(); - ~GLRadioToolbar(); - - bool init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size); - - bool add_item(const GLRadioToolbarItem::Data& data); - - float get_height() const; - - void set_position(float top, float left); - void set_selection(const std::string& name); - - // returns the id of the item under the given mouse position or -1 if none - int contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; - - std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); - - void do_action(unsigned int item_id, GLCanvas3D& parent); - - void render(const GLCanvas3D& parent) const; -}; -#endif // !ENABLE_TOOLBAR_BACKGROUND_TEXTURE - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 357b8bb4d..58fb0dc5e 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -4,6 +4,7 @@ #include "WipeTowerDialog.hpp" #include +#include #include #include @@ -316,6 +317,20 @@ std::string into_u8(const wxString &str) return std::string(buffer_utf8.data()); } +wxString from_path(const boost::filesystem::path &path) +{ +#ifdef _WIN32 + return wxString(path.string()); +#else + return wxString::FromUTF8(path.string()); +#endif +} + +boost::filesystem::path into_path(const wxString &str) +{ + return boost::filesystem::path(str.wx_str()); +} + bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height) { const auto idx = wxDisplay::GetFromWindow(window); @@ -379,7 +394,6 @@ void about() { AboutDialog dlg; dlg.ShowModal(); - dlg.Destroy(); } void desktop_open_datadir_folder() diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index e7ab0443d..79b21b503 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -1,6 +1,10 @@ #ifndef slic3r_GUI_hpp_ #define slic3r_GUI_hpp_ +#include + +#include + #include "libslic3r/Config.hpp" class wxWindow; @@ -8,7 +12,6 @@ class wxMenuBar; class wxNotebook; class wxComboCtrl; class wxFileDialog; -class wxString; class wxTopLevelWindow; namespace Slic3r { @@ -16,7 +19,6 @@ namespace Slic3r { class AppConfig; class DynamicPrintConfig; class Print; -class GCodePreviewData; namespace GUI { @@ -54,10 +56,16 @@ void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string // encoded inside an int. int combochecklist_get_flags(wxComboCtrl* comboCtrl); -// Return wxString from std::string in UTF8 +// wxString conversions: + +// wxString from std::string in UTF8 wxString from_u8(const std::string &str); -// Return std::string in UTF8 from wxString +// std::string in UTF8 from wxString std::string into_u8(const wxString &str); +// wxString from boost path +wxString from_path(const boost::filesystem::path &path); +// boost path from wxString +boost::filesystem::path into_path(const wxString &str); // Returns the dimensions of the screen on which the main frame is displayed bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index f4047ae3e..b1ecef7df 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -35,6 +35,7 @@ #include "Preferences.hpp" #include "Tab.hpp" #include "SysInfoDialog.hpp" +#include "KBShortcutsDialog.hpp" namespace Slic3r { namespace GUI { @@ -42,7 +43,7 @@ namespace GUI { wxString file_wildcards(FileType file_type, const std::string &custom_extension) { - static const wxString defaults[FT_SIZE] = { + static const std::string defaults[FT_SIZE] = { /* FT_STL */ "STL files (*.stl)|*.stl;*.STL", /* FT_OBJ */ "OBJ files (*.obj)|*.obj;*.OBJ", /* FT_AMF */ "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML", @@ -53,16 +54,20 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension) /* FT_INI */ "INI files (*.ini)|*.ini;*.INI", /* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG", - /* FT_PNGZIP */"Zipped PNG files (*.zip)|*.zip;*.ZIP", // This is lame, but that's what we use for SLA + /* FT_PNGZIP */"Zipped PNG files (*.dwz)|*.dwz;*.DWZ", // This is lame, but that's what we use for SLA }; - wxString out = defaults[file_type]; + std::string out = defaults[file_type]; if (! custom_extension.empty()) { - // Append the custom extension to the wildcards, so that the file dialog would not add the default extension to it. - out += ";*"; - out += from_u8(custom_extension); + // Find the custom extension in the template. + if (out.find(std::string("*") + custom_extension + ",") == std::string::npos && out.find(std::string("*") + custom_extension + ")") == std::string::npos) { + // The custom extension was not found in the template. + // Append the custom extension to the wildcards, so that the file dialog would not add the default extension to it. + boost::replace_first(out, ")|", std::string(", *") + custom_extension + ")|"); + out += std::string(";*") + custom_extension; + } } - return out; + return wxString::FromUTF8(out.c_str()); } static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); } @@ -73,7 +78,6 @@ GUI_App::GUI_App() : wxApp() #if ENABLE_IMGUI , m_imgui(new ImGuiWrapper()) - , m_printhost_queue(new PrintHostJobQueue()) #endif // ENABLE_IMGUI {} @@ -137,19 +141,23 @@ bool GUI_App::OnInit() std::cerr << "Creating main frame..." << std::endl; if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) wxImage::AddHandler(new wxPNGHandler()); - mainframe = new MainFrame(no_plater, false); + mainframe = new MainFrame(); sidebar().obj_list()->init_objects(); // propagate model objects to object list - update_mode(); +// update_mode(); // do that later SetTopWindow(mainframe); + m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg())); + CallAfter([this]() { // temporary workaround for the correct behavior of the Scrolled sidebar panel auto& panel = sidebar(); if (panel.obj_list()->GetMinHeight() > 200) { wxWindowUpdateLocker noUpdates_sidebar(&panel); panel.obj_list()->SetMinSize(wxSize(-1, 200)); - panel.Layout(); +// panel.Layout(); } + update_mode(); // update view mode after fix of the object_list size + // to correct later layouts }); // This makes CallAfter() work @@ -175,6 +183,11 @@ bool GUI_App::OnInit() if (app_config->dirty()) app_config->save(); + +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + if (this->plater() != nullptr) + this->obj_manipul()->update_if_dirty(); +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION }); // On OS X the UI tends to freeze in weird ways if modal dialogs(config wizard, update notifications, ...) @@ -275,16 +288,31 @@ void GUI_App::recreate_GUI() { std::cerr << "recreate_GUI" << std::endl; - auto topwindow = GetTopWindow(); - mainframe = new MainFrame(no_plater,false); + MainFrame* topwindow = dynamic_cast(GetTopWindow()); + mainframe = new MainFrame(); sidebar().obj_list()->init_objects(); // propagate model objects to object list - update_mode(); - +// update_mode(); // do that later if (topwindow) { SetTopWindow(mainframe); topwindow->Destroy(); } + m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg())); + + CallAfter([this]() { + // temporary workaround for the correct behavior of the Scrolled sidebar panel + auto& panel = sidebar(); + if (panel.obj_list()->GetMinHeight() > 200) { + wxWindowUpdateLocker noUpdates_sidebar(&panel); + panel.obj_list()->SetMinSize(wxSize(-1, 200)); +// panel.Layout(); + } + update_mode(); // update view mode after fix of the object_list size + // to correct later layouts + }); + + mainframe->Show(true); + // On OSX the UI was not initialized correctly if the wizard was called // before the UI was up and running. CallAfter([]() { @@ -300,6 +328,13 @@ void GUI_App::system_info() dlg.Destroy(); } +void GUI_App::keyboard_shortcuts() +{ + KBShortcutsDialog dlg; + dlg.ShowModal(); + dlg.Destroy(); +} + // static method accepting a wxWindow object as first parameter bool GUI_App::catch_error(std::function cb, // wxMessageDialog* message_dialog, @@ -347,7 +382,7 @@ void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) input_files.Clear(); wxFileDialog dialog(parent ? parent : GetTopWindow(), _(L("Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):")), - app_config->get_last_dir(), "", + from_u8(app_config->get_last_dir()), "", file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST); if (dialog.ShowModal() == wxID_OK) @@ -424,9 +459,12 @@ bool GUI_App::select_language( wxArrayString & names, { m_wxLocale = new wxLocale; m_wxLocale->Init(identifiers[index]); - m_wxLocale->AddCatalogLookupPathPrefix(localization_dir()); - m_wxLocale->AddCatalog(GetAppName()); + m_wxLocale->AddCatalogLookupPathPrefix(wxString::FromUTF8(localization_dir())); + m_wxLocale->AddCatalog(/*GetAppName()*/"Slic3rPE"); wxSetlocale(LC_NUMERIC, "C"); +#ifdef WIN32 + ::SetLocaleInfoA(LOCALE_CUSTOM_DEFAULT, LOCALE_SDECIMAL, "."); +#endif /* WIN32 */ Preset::update_suffix_modified(); return true; } @@ -451,10 +489,13 @@ bool GUI_App::load_language() { m_wxLocale = new wxLocale; m_wxLocale->Init(identifiers[i]); - m_wxLocale->AddCatalogLookupPathPrefix(localization_dir()); - m_wxLocale->AddCatalog(GetAppName()); + m_wxLocale->AddCatalogLookupPathPrefix(wxString::FromUTF8(localization_dir())); + m_wxLocale->AddCatalog(/*GetAppName()*/"Slic3rPE"); wxSetlocale(LC_NUMERIC, "C"); - Preset::update_suffix_modified(); +#ifdef WIN32 + ::SetLocaleInfoA(LOCALE_CUSTOM_DEFAULT, LOCALE_SDECIMAL, "."); +#endif /* WIN32 */ + Preset::update_suffix_modified(); return true; } } @@ -478,7 +519,7 @@ void GUI_App::get_installed_languages(wxArrayString & names, wxArrayLong & ident names.Clear(); identifiers.Clear(); - wxDir dir(localization_dir()); + wxDir dir(wxString::FromUTF8(localization_dir())); wxString filename; const wxLanguageInfo * langinfo; wxString name = wxLocale::GetLanguageName(wxLANGUAGE_DEFAULT); @@ -495,7 +536,8 @@ void GUI_App::get_installed_languages(wxArrayString & names, wxArrayLong & ident { auto full_file_name = dir.GetName() + wxFileName::GetPathSeparator() + filename + wxFileName::GetPathSeparator() + - GetAppName() + wxT(".mo"); + /*GetAppName()*/"Slic3rPE" + + wxT(".mo"); if (wxFileExists(full_file_name)) { names.Add(langinfo->Description); @@ -523,6 +565,13 @@ ConfigMenuIDs GUI_App::get_view_mode() mode == "simple" ? ConfigMenuModeSimple : ConfigMenuModeAdvanced; } +ConfigOptionMode GUI_App::get_opt_mode() { + const ConfigMenuIDs mode = wxGetApp().get_view_mode(); + + return mode == ConfigMenuModeSimple ? comSimple : + mode == ConfigMenuModeExpert ? comExpert : comAdvanced; +} + // Update view mode according to selected menu void GUI_App::update_mode() { @@ -537,10 +586,8 @@ void GUI_App::update_mode() sidebar().Layout(); - ConfigOptionMode opt_mode = mode == ConfigMenuModeSimple ? comSimple : - mode == ConfigMenuModeExpert ? comExpert : comAdvanced; for (auto tab : tabs_list) - tab->update_visibility(opt_mode); + tab->update_visibility(); plater()->update_object_menu(); } diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 875a92456..e388910d7 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -71,7 +71,6 @@ static wxString dots("…", wxConvUTF8); class GUI_App : public wxApp { - bool no_plater{ false }; bool app_conf_exists{ false }; // Lock to guard the callback stack @@ -92,7 +91,7 @@ class GUI_App : public wxApp std::unique_ptr m_imgui; #endif // ENABLE_IMGUI - std::unique_ptr m_printhost_queue; + std::unique_ptr m_printhost_job_queue; public: bool OnInit() override; @@ -115,6 +114,7 @@ public: void recreate_GUI(); void system_info(); + void keyboard_shortcuts(); void load_project(wxWindow *parent, wxString& input_file); void import_model(wxWindow *parent, wxArrayString& input_files); static bool catch_error(std::function cb, @@ -135,6 +135,7 @@ public: Tab* get_tab(Preset::Type type); ConfigMenuIDs get_view_mode(); + ConfigOptionMode get_opt_mode(); void update_mode(); void add_config_menu(wxMenuBar *menu); @@ -164,7 +165,7 @@ public: ImGuiWrapper* imgui() { return m_imgui.get(); } #endif // ENABLE_IMGUI - PrintHostJobQueue& printhost_queue() { return *m_printhost_queue.get(); } + PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); } }; DECLARE_APP(GUI_App) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index cba42470a..afeaca327 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -840,27 +840,17 @@ void ObjectList::load_part( ModelObject* model_object, if (model_object->origin_translation != Vec3d::Zero()) { object->center_around_origin(); -#if !ENABLE_MODELVOLUME_TRANSFORM - object->ensure_on_bed(); -#endif // !ENABLE_MODELVOLUME_TRANSFORM delta = model_object->origin_translation - object->origin_translation; } for (auto volume : object->volumes) { -#if ENABLE_MODELVOLUME_TRANSFORM volume->center_geometry(); volume->translate(delta); -#endif // ENABLE_MODELVOLUME_TRANSFORM auto new_volume = model_object->add_volume(*volume); new_volume->set_type(static_cast(type)); new_volume->name = boost::filesystem::path(input_file).filename().string(); part_names.Add(new_volume->name); -#if !ENABLE_MODELVOLUME_TRANSFORM - if (delta != Vec3d::Zero()) - new_volume->translate(delta); -#endif // !ENABLE_MODELVOLUME_TRANSFORM - // set a default extruder value, since user can't add it manually new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); @@ -903,10 +893,8 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const int auto new_volume = (*m_objects)[obj_idx]->add_volume(mesh); new_volume->set_type(static_cast(type)); -#if ENABLE_MODELVOLUME_TRANSFORM new_volume->set_offset(Vec3d(0.0, 0.0, (*m_objects)[obj_idx]->origin_translation(2) - mesh.stl.stats.min(2))); new_volume->center_geometry(); -#endif // ENABLE_MODELVOLUME_TRANSFORM new_volume->name = name; // set a default extruder value, since user can't add it manually @@ -982,6 +970,10 @@ void ObjectList::del_instances_from_object(const int obj_idx) bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, const int type) { + if (obj_idx == 1000) + // Cannot delete a wipe tower. + return false; + if (type == itVolume) { const auto volume = (*m_objects)[obj_idx]->volumes[idx]; @@ -1399,6 +1391,20 @@ void ObjectList::update_selections() auto& selection = wxGetApp().plater()->canvas3D()->get_selection(); wxDataViewItemArray sels; + // We doesn't update selection if SettingsItem for the current object/part is selected + if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings ) + { + const auto item = GetSelection(); + if (selection.is_single_full_object() && + m_objects_model->GetIdByItem(m_objects_model->GetParent(item)) == selection.get_object_idx()) + return; + if (selection.is_single_volume() || selection.is_modifier()) { + const auto gl_vol = selection.get_volume(*selection.get_volume_idxs().begin()); + if (m_objects_model->GetVolumeIdByItem(m_objects_model->GetParent(item)) == gl_vol->volume_idx()) + return; + } + } + if (selection.is_single_full_object()) { sels.Add(m_objects_model->GetItemById(selection.get_object_idx())); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index d193a11a9..32a38a77c 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -17,6 +17,12 @@ namespace GUI ObjectManipulation::ObjectManipulation(wxWindow* parent) : OG_Settings(parent, true) +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + , m_cache_position(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) + , m_cache_rotation(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) + , m_cache_scale(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) + , m_cache_size(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION { m_og->set_name(_(L("Object Manipulation"))); m_og->label_width = 100; @@ -25,18 +31,6 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : m_og->m_on_change = [this](const std::string& opt_key, const boost::any& value) { std::vector axes{ "_x", "_y", "_z" }; - if (opt_key == "scale_unit") { - const wxString& selection = boost::any_cast(value); - for (auto axis : axes) { - std::string key = "scale" + axis; - m_og->set_side_text(key, selection); - } - - m_is_percent_scale = selection == _("%"); - update_scale_values(); - return; - } - std::string param; std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param)); @@ -50,30 +44,73 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : else if (param == "rotation") change_rotation_value(new_value); else if (param == "scale") +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + { change_scale_value(new_value); + update_settings_value(wxGetApp().plater()->canvas3D()->get_selection()); + } +#else + change_scale_value(new_value); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + else if (param == "size") +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + { + change_size_value(new_value); + update_settings_value(wxGetApp().plater()->canvas3D()->get_selection()); + } +#else + change_size_value(new_value); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); }; m_og->m_fill_empty_value = [this](const std::string& opt_key) { - if (opt_key == "scale_unit") - return; +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + this->update_if_dirty(); +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION std::string param; std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param)); + + double value = 0.0; + if (param == "position") { int axis = opt_key.back() == 'x' ? 0 : opt_key.back() == 'y' ? 1 : 2; - m_og->set_value(opt_key, double_to_string(cache_position(axis))); - return; + value = m_cache_position(axis); + } + else if (param == "rotation") { + int axis = opt_key.back() == 'x' ? 0 : + opt_key.back() == 'y' ? 1 : 2; + + value = m_cache_rotation(axis); + } + else if (param == "scale") { + int axis = opt_key.back() == 'x' ? 0 : + opt_key.back() == 'y' ? 1 : 2; + + value = m_cache_scale(axis); + } + else if (param == "size") { + int axis = opt_key.back() == 'x' ? 0 : + opt_key.back() == 'y' ? 1 : 2; + + value = m_cache_size(axis); } - m_og->set_value(opt_key, double_to_string(0.0)); + m_og->set_value(opt_key, double_to_string(value)); + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); }; m_og->m_set_focus = [this](const std::string& opt_key) { - wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key); +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + this->update_if_dirty(); +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, true); }; ConfigOptionDef def; @@ -106,59 +143,37 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : auto add_og_to_object_settings = [](const std::string& option_name, const std::string& sidetext) { Line line = { _(option_name), "" }; - if (option_name == "Scale") { - line.near_label_widget = [](wxWindow* parent) { - auto btn = new PrusaLockButton(parent, wxID_ANY); - btn->Bind(wxEVT_BUTTON, [btn](wxCommandEvent &event) { - event.Skip(); - wxTheApp->CallAfter([btn]() { - wxGetApp().obj_manipul()->set_uniform_scaling(btn->IsLocked()); - }); - }); - return btn; - }; - } - ConfigOptionDef def; def.type = coFloat; def.default_value = new ConfigOptionFloat(0.0); def.width = 50; if (option_name == "Rotation") + { def.min = -360; + def.max = 360; + } const std::string lower_name = boost::algorithm::to_lower_copy(option_name); std::vector axes{ "x", "y", "z" }; for (auto axis : axes) { - if (axis == "z" && option_name != "Scale") + if (axis == "z") def.sidetext = sidetext; Option option = Option(def, lower_name + "_" + axis); option.opt.full_width = true; line.append_option(option); } - if (option_name == "Scale") - { - def.width = 45; - def.type = coStrings; - def.gui_type = "select_open"; - def.enum_labels.push_back(L("%")); - def.enum_labels.push_back(L("mm")); - def.default_value = new ConfigOptionStrings{ "mm" }; - - const Option option = Option(def, lower_name + "_unit"); - line.append_option(option); - } - return line; }; // Settings table m_og->append_line(add_og_to_object_settings(L("Position"), L("mm")), &m_move_Label); - m_og->append_line(add_og_to_object_settings(L("Rotation"), "°")); - m_og->append_line(add_og_to_object_settings(L("Scale"), "mm")); + m_og->append_line(add_og_to_object_settings(L("Rotation"), "°"), &m_rotate_Label); + m_og->append_line(add_og_to_object_settings(L("Scale"), "%"), &m_scale_Label); + m_og->append_line(add_og_to_object_settings(L("Size"), "mm")); /* Unused parameter at this time def.label = L("Place on bed"); @@ -191,289 +206,269 @@ bool ObjectManipulation::IsShown() void ObjectManipulation::UpdateAndShow(const bool show) { - if (show) + if (show) { update_settings_value(wxGetApp().plater()->canvas3D()->get_selection()); +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + update_if_dirty(); +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + } OG_Settings::UpdateAndShow(show); } -int ObjectManipulation::ol_selection() -{ - return wxGetApp().obj_list()->get_selected_obj_idx(); -} - void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection) { - wxString move_label = _(L("Position")); -#if ENABLE_MODELVOLUME_TRANSFORM - if (selection.is_single_full_instance() || selection.is_single_full_object()) -#else - if (selection.is_single_full_object()) + m_new_move_label_string = L("Position:"); + m_new_rotate_label_string = L("Rotation:"); + m_new_scale_label_string = L("Scale factors:"); + if (selection.is_single_full_instance()) { - auto obj_idx = selection.get_object_idx(); - if (obj_idx >=0 && !wxGetApp().model_objects()->empty() && (*wxGetApp().model_objects())[obj_idx]->instances.size() == 1) - { - // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); - update_position_value(volume->get_offset()); - update_rotation_value(volume->get_rotation()); - update_scale_value(volume->get_scaling_factor()); - m_og->enable(); - } + // all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + m_new_position = volume->get_instance_offset(); + m_new_rotation = volume->get_instance_rotation(); + m_new_scale = volume->get_instance_scaling_factor(); + int obj_idx = volume->object_idx(); + if ((0 <= obj_idx) && (obj_idx < (int)wxGetApp().model_objects()->size())) + m_new_size = volume->get_instance_transformation().get_matrix(true, true) * (*wxGetApp().model_objects())[obj_idx]->raw_mesh().bounding_box().size(); else - reset_settings_value(); + // this should never happen + m_new_size = Vec3d::Zero(); + + m_new_enabled = true; } - else if (selection.is_single_full_instance()) -#endif // ENABLE_MODELVOLUME_TRANSFORM + else if (selection.is_single_full_object()) { - // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); -#if ENABLE_MODELVOLUME_TRANSFORM - update_position_value(volume->get_instance_offset()); - update_rotation_value(volume->get_instance_rotation()); - update_scale_value(volume->get_instance_scaling_factor()); -#else - update_position_value(volume->get_offset()); - update_rotation_value(volume->get_rotation()); - update_scale_value(volume->get_scaling_factor()); -#endif // ENABLE_MODELVOLUME_TRANSFORM - m_og->enable(); - } - else if (selection.is_wipe_tower()) - { - // the selection contains a single volume - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); -#if ENABLE_MODELVOLUME_TRANSFORM - update_position_value(volume->get_volume_offset()); - update_rotation_value(volume->get_volume_rotation()); - update_scale_value(volume->get_volume_scaling_factor()); -#else - update_position_value(volume->get_offset()); - update_rotation_value(volume->get_rotation()); - update_scale_value(volume->get_scaling_factor()); -#endif // ENABLE_MODELVOLUME_TRANSFORM - m_og->enable(); + const BoundingBoxf3& box = selection.get_bounding_box(); + m_new_position = box.center(); + m_new_rotation = Vec3d::Zero(); + m_new_scale = Vec3d(1.0, 1.0, 1.0); + m_new_size = box.size(); + m_new_rotate_label_string = L("Rotate:"); + m_new_scale_label_string = L("Scale:"); + m_new_enabled = true; } else if (selection.is_single_modifier() || selection.is_single_volume()) { // the selection contains a single volume const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); -#if ENABLE_MODELVOLUME_TRANSFORM - update_position_value(volume->get_volume_offset()); - update_rotation_value(volume->get_volume_rotation()); - update_scale_value(volume->get_volume_scaling_factor()); -#else - update_position_value(volume->get_offset()); - update_rotation_value(volume->get_rotation()); - update_scale_value(volume->get_scaling_factor()); -#endif // ENABLE_MODELVOLUME_TRANSFORM - m_og->enable(); + m_new_position = volume->get_volume_offset(); + m_new_rotation = volume->get_volume_rotation(); + m_new_scale = volume->get_volume_scaling_factor(); + m_new_size = volume->get_instance_transformation().get_matrix(true, true) * volume->get_volume_transformation().get_matrix(true, true) * volume->bounding_box.size(); + m_new_enabled = true; } else if (wxGetApp().obj_list()->multiple_selection()) { reset_settings_value(); - move_label = _(L("Displacement")); - m_og->enable(); + m_new_move_label_string = L("Translate:"); + m_new_rotate_label_string = L("Rotate:"); + m_new_scale_label_string = L("Scale:"); + m_new_size = selection.get_bounding_box().size(); + m_new_enabled = true; } else reset_settings_value(); - m_move_Label->SetLabel(move_label); - m_og->get_field("scale_unit")->disable();// temporary decision +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + update_if_dirty(); +#else + m_dirty = true; +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION +} + +void ObjectManipulation::update_if_dirty() +{ +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + if (_(m_new_move_label_string) != m_move_Label->GetLabel()) + m_move_Label->SetLabel(_(m_new_move_label_string)); + + if (_(m_new_rotate_label_string) != m_rotate_Label->GetLabel()) + m_rotate_Label->SetLabel(_(m_new_rotate_label_string)); + + if (_(m_new_scale_label_string) != m_scale_Label->GetLabel()) + m_scale_Label->SetLabel(_(m_new_scale_label_string)); + + if (m_cache_position(0) != m_new_position(0)) + m_og->set_value("position_x", double_to_string(m_new_position(0), 2)); + + if (m_cache_position(1) != m_new_position(1)) + m_og->set_value("position_y", double_to_string(m_new_position(1), 2)); + + if (m_cache_position(2) != m_new_position(2)) + m_og->set_value("position_z", double_to_string(m_new_position(2), 2)); + + m_cache_position = m_new_position; + + auto scale = m_new_scale * 100.0; + if (m_cache_scale(0) != scale(0)) + m_og->set_value("scale_x", double_to_string(scale(0), 2)); + + if (m_cache_scale(1) != scale(1)) + m_og->set_value("scale_y", double_to_string(scale(1), 2)); + + if (m_cache_scale(2) != scale(2)) + m_og->set_value("scale_z", double_to_string(scale(2), 2)); + + m_cache_scale = scale; + + if (m_cache_size(0) != m_new_size(0)) + m_og->set_value("size_x", double_to_string(m_new_size(0), 2)); + + if (m_cache_size(1) != m_new_size(1)) + m_og->set_value("size_y", double_to_string(m_new_size(1), 2)); + + if (m_cache_size(2) != m_new_size(2)) + m_og->set_value("size_z", double_to_string(m_new_size(2), 2)); + + m_cache_size = m_new_size; + + if (m_cache_rotation(0) != m_new_rotation(0)) + m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(0)), 0), 2)); + + if (m_cache_rotation(1) != m_new_rotation(1)) + m_og->set_value("rotation_y", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(1)), 0), 2)); + + if (m_cache_rotation(2) != m_new_rotation(2)) + m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(2)), 0), 2)); + + m_cache_rotation = m_new_rotation; + + if (m_new_enabled) + m_og->enable(); + else + m_og->disable(); +#else + if (! m_dirty) + return; + + m_move_Label->SetLabel(_(m_new_move_label_string)); + m_rotate_Label->SetLabel(_(m_new_rotate_label_string)); + m_scale_Label->SetLabel(_(m_new_scale_label_string)); + + m_og->set_value("position_x", double_to_string(m_new_position(0), 2)); + m_og->set_value("position_y", double_to_string(m_new_position(1), 2)); + m_og->set_value("position_z", double_to_string(m_new_position(2), 2)); + m_cache_position = m_new_position; + + auto scale = m_new_scale * 100.0; + m_og->set_value("scale_x", double_to_string(scale(0), 2)); + m_og->set_value("scale_y", double_to_string(scale(1), 2)); + m_og->set_value("scale_z", double_to_string(scale(2), 2)); + m_cache_scale = scale; + + m_og->set_value("size_x", double_to_string(m_new_size(0), 2)); + m_og->set_value("size_y", double_to_string(m_new_size(1), 2)); + m_og->set_value("size_z", double_to_string(m_new_size(2), 2)); + m_cache_size = m_new_size; + + m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(0)), 0), 2)); + m_og->set_value("rotation_y", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(1)), 0), 2)); + m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(2)), 0), 2)); + m_cache_rotation = m_new_rotation; + + if (m_new_enabled) + m_og->enable(); + else + m_og->disable(); + + m_dirty = false; +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } void ObjectManipulation::reset_settings_value() { - reset_position_value(); - reset_rotation_value(); - reset_scale_value(); - m_og->disable(); + m_new_position = Vec3d::Zero(); + m_new_rotation = Vec3d::Zero(); + m_new_scale = Vec3d::Ones(); + m_new_size = Vec3d::Zero(); + m_new_enabled = false; +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + m_dirty = true; +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } -wxString def_0 {"0"}; -wxString def_100 {"100"}; - -void ObjectManipulation::reset_position_value() -{ - m_og->set_value("position_x", def_0); - m_og->set_value("position_y", def_0); - m_og->set_value("position_z", def_0); - - cache_position = { 0., 0., 0. }; -} - -void ObjectManipulation::reset_rotation_value() -{ - m_og->set_value("rotation_x", def_0); - m_og->set_value("rotation_y", def_0); - m_og->set_value("rotation_z", def_0); -} - -void ObjectManipulation::reset_scale_value() -{ - m_is_percent_scale = true; - m_og->set_value("scale_unit", _("%")); - m_og->set_value("scale_x", def_100); - m_og->set_value("scale_y", def_100); - m_og->set_value("scale_z", def_100); -} - -void ObjectManipulation::update_values() -{ - int selection = ol_selection(); - if (selection < 0 || wxGetApp().mainframe->m_plater->model().objects.size() <= selection) { - m_og->set_value("position_x", def_0); - m_og->set_value("position_y", def_0); - m_og->set_value("position_z", def_0); - m_og->set_value("scale_x" , def_0); - m_og->set_value("scale_y" , def_0); - m_og->set_value("scale_z" , def_0); - m_og->set_value("rotation_x", def_0); - m_og->set_value("rotation_y", def_0); - m_og->set_value("rotation_z", def_0); - m_og->disable(); - return; - } - m_is_percent_scale = boost::any_cast(m_og->get_value("scale_unit")) == _("%"); - - update_position_values(); - update_scale_values(); - update_rotation_values(); - m_og->enable(); -} - -void ObjectManipulation::update_scale_values() -{ - int selection = ol_selection(); - ModelObjectPtrs& objects = wxGetApp().mainframe->m_plater->model().objects; - - auto instance = objects[selection]->instances.front(); - auto size = objects[selection]->instance_bounding_box(0).size(); - - if (m_is_percent_scale) { - m_og->set_value("scale_x", double_to_string(instance->get_scaling_factor(X) * 100, 2)); - m_og->set_value("scale_y", double_to_string(instance->get_scaling_factor(Y) * 100, 2)); - m_og->set_value("scale_z", double_to_string(instance->get_scaling_factor(Z) * 100, 2)); - } - else { - m_og->set_value("scale_x", double_to_string(size(0), 2)); - m_og->set_value("scale_y", double_to_string(size(1), 2)); - m_og->set_value("scale_z", double_to_string(size(2), 2)); - } -} - -void ObjectManipulation::update_position_values() -{ - auto instance = wxGetApp().mainframe->m_plater->model().objects[ol_selection()]->instances.front(); - - m_og->set_value("position_x", double_to_string(instance->get_offset(X), 2)); - m_og->set_value("position_y", double_to_string(instance->get_offset(Y), 2)); - m_og->set_value("position_z", double_to_string(instance->get_offset(Z), 2)); -} - -void ObjectManipulation::update_position_value(const Vec3d& position) -{ - m_og->set_value("position_x", double_to_string(position(0), 2)); - m_og->set_value("position_y", double_to_string(position(1), 2)); - m_og->set_value("position_z", double_to_string(position(2), 2)); - - cache_position = position; -} - -void ObjectManipulation::update_scale_value(const Vec3d& scaling_factor) -{ - // this is temporary - // to be able to update the values as size - // we need to store somewhere the original size - // or have it passed as parameter - if (!m_is_percent_scale) { - m_is_percent_scale = true; - m_og->set_value("scale_unit", _("%")); - } - - auto scale = scaling_factor * 100.0; - m_og->set_value("scale_x", double_to_string(scale(0), 2)); - m_og->set_value("scale_y", double_to_string(scale(1), 2)); - m_og->set_value("scale_z", double_to_string(scale(2), 2)); -} - -void ObjectManipulation::update_rotation_values() -{ - update_rotation_value(wxGetApp().mainframe->m_plater->model().objects[ol_selection()]->instances.front()->get_rotation()); -} - -void ObjectManipulation::update_rotation_value(double angle, Axis axis) -{ - std::string axis_str; - switch (axis) { - case X: { - axis_str = "rotation_x"; - break; } - case Y: { - axis_str = "rotation_y"; - break; } - case Z: { - axis_str = "rotation_z"; - break; } - } - - m_og->set_value(axis_str, round_nearest(int(Geometry::rad2deg(angle)), 0)); -} - -void ObjectManipulation::update_rotation_value(const Vec3d& rotation) -{ - m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(rotation(0)), 0), 2)); - m_og->set_value("rotation_y", double_to_string(round_nearest(Geometry::rad2deg(rotation(1)), 0), 2)); - m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(rotation(2)), 0), 2)); -} - - void ObjectManipulation::change_position_value(const Vec3d& position) { - Vec3d displacement(position - cache_position); - auto canvas = wxGetApp().plater()->canvas3D(); - canvas->get_selection().start_dragging(); - canvas->get_selection().translate(displacement); + GLCanvas3D::Selection& selection = canvas->get_selection(); + selection.start_dragging(); + selection.translate(position - m_cache_position, selection.requires_local_axes()); canvas->do_move(); - cache_position = position; + m_cache_position = position; } void ObjectManipulation::change_rotation_value(const Vec3d& rotation) { + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + const GLCanvas3D::Selection& selection = canvas->get_selection(); + Vec3d rad_rotation; for (size_t i = 0; i < 3; ++i) + { rad_rotation(i) = Geometry::deg2rad(rotation(i)); - auto canvas = wxGetApp().plater()->canvas3D(); + } + canvas->get_selection().start_dragging(); - canvas->get_selection().rotate(rad_rotation, false); + canvas->get_selection().rotate(rad_rotation, selection.is_single_full_instance()); canvas->do_rotate(); + +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + m_cache_rotation = rotation; +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } void ObjectManipulation::change_scale_value(const Vec3d& scale) { - Vec3d scaling_factor; - if (m_is_percent_scale) - scaling_factor = scale*0.01; - else { - int selection = ol_selection(); - ModelObjectPtrs& objects = *wxGetApp().model_objects(); - - auto size = objects[selection]->instance_bounding_box(0).size(); - for (size_t i = 0; i < 3; ++i) - scaling_factor(i) = scale(i) / size(i); + Vec3d scaling_factor = scale; + const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + if (selection.requires_uniform_scale()) + { + Vec3d abs_scale_diff = (scale - m_cache_scale).cwiseAbs(); + double max_diff = abs_scale_diff(X); + Axis max_diff_axis = X; + if (max_diff < abs_scale_diff(Y)) + { + max_diff = abs_scale_diff(Y); + max_diff_axis = Y; + } + if (max_diff < abs_scale_diff(Z)) + { + max_diff = abs_scale_diff(Z); + max_diff_axis = Z; + } + scaling_factor = Vec3d(scale(max_diff_axis), scale(max_diff_axis), scale(max_diff_axis)); } + scaling_factor *= 0.01; + auto canvas = wxGetApp().plater()->canvas3D(); canvas->get_selection().start_dragging(); canvas->get_selection().scale(scaling_factor, false); canvas->do_scale(); + +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + m_cache_scale = scale; +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } -void ObjectManipulation::print_cashe_value(const std::string& label, const Vec3d& v) +void ObjectManipulation::change_size_value(const Vec3d& size) { - std::cout << label << " => " << " X:" << v(0) << " Y:" << v(1) << " Z:" << v(2) << std::endl; + const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + + Vec3d ref_size = m_cache_size; + if (selection.is_single_full_instance()) + { + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + ref_size = volume->bounding_box.size(); + } + + change_scale_value(100.0 * Vec3d(size(0) / ref_size(0), size(1) / ref_size(1), size(2) / ref_size(2))); + +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + m_cache_size = size; +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } } //namespace GUI diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 3a8df4111..ac80f56b3 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -14,12 +14,35 @@ namespace GUI { class ObjectManipulation : public OG_Settings { - bool m_is_percent_scale = false; // true -> percentage scale unit - // false -> uniform scale unit - bool m_is_uniform_scale = false; // It indicates if scale is uniform +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + Vec3d m_cache_position; + Vec3d m_cache_rotation; + Vec3d m_cache_scale; + Vec3d m_cache_size; +#else + Vec3d m_cache_position{ 0., 0., 0. }; + Vec3d m_cache_rotation{ 0., 0., 0. }; + Vec3d m_cache_scale{ 100., 100., 100. }; + Vec3d m_cache_size{ 0., 0., 0. }; +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION - Vec3d cache_position { 0., 0., 0. }; wxStaticText* m_move_Label = nullptr; + wxStaticText* m_scale_Label = nullptr; + wxStaticText* m_rotate_Label = nullptr; + +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + // Needs to be updated from OnIdle? + bool m_dirty = false; +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + // Cached labels for the delayed update, not localized! + std::string m_new_move_label_string; + std::string m_new_rotate_label_string; + std::string m_new_scale_label_string; + Vec3d m_new_position; + Vec3d m_new_rotation; + Vec3d m_new_scale; + Vec3d m_new_size; + bool m_new_enabled; public: ObjectManipulation(wxWindow* parent); @@ -29,38 +52,24 @@ public: bool IsShown() override; void UpdateAndShow(const bool show) override; - int ol_selection(); + void update_settings_value(const GLCanvas3D::Selection& selection); - void update_settings_value(const GLCanvas3D::Selection& selection); + // Called from the App to update the UI if dirty. + void update_if_dirty(); + +private: void reset_settings_value(); - void reset_position_value(); - void reset_rotation_value(); - void reset_scale_value(); - void update_values(); - // update position values displacements or "gizmos" - void update_position_values(); - void update_position_value(const Vec3d& position); - // update scale values after scale unit changing or "gizmos" - void update_scale_values(); - void update_scale_value(const Vec3d& scaling_factor); - // update rotation values object selection changing - void update_rotation_values(); + // update size values after scale unit changing or "gizmos" + void update_size_value(const Vec3d& size); // update rotation value after "gizmos" - void update_rotation_value(double angle, Axis axis); void update_rotation_value(const Vec3d& rotation); - void set_uniform_scaling(const bool uniform_scale) { m_is_uniform_scale = uniform_scale; } - - // change values void change_position_value(const Vec3d& position); void change_rotation_value(const Vec3d& rotation); void change_scale_value(const Vec3d& scale); - - -private: - void print_cashe_value(const std::string& label, const Vec3d& value); + void change_size_value(const Vec3d& size); }; }} diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 4e14aefe8..1092c761f 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -27,7 +27,6 @@ namespace Slic3r { namespace GUI { -#if ENABLE_REMOVE_TABS_FROM_PLATER View3D::View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process) : m_canvas_widget(nullptr) , m_canvas(nullptr) @@ -70,7 +69,6 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba m_canvas->set_config(config); m_canvas->enable_gizmos(true); m_canvas->enable_toolbar(true); - m_canvas->enable_shader(true); m_canvas->enable_force_zoom_to_bed(true); #if !ENABLE_IMGUI @@ -92,11 +90,7 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba return true; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void View3D::set_view_toolbar(GLToolbar* toolbar) -#else -void View3D::set_view_toolbar(GLRadioToolbar* toolbar) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { if (m_canvas != nullptr) m_canvas->set_view_toolbar(toolbar); @@ -189,13 +183,8 @@ void View3D::render() if (m_canvas != nullptr) m_canvas->render(); } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_REMOVE_TABS_FROM_PLATER Preview::Preview(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process_func) -#else -Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process_func) -#endif // ENABLE_REMOVE_TABS_FROM_PLATER : m_canvas_widget(nullptr) , m_canvas(nullptr) , m_double_slider_sizer(nullptr) @@ -214,51 +203,27 @@ Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundSli , m_preferred_color_mode("feature") , m_loaded(false) , m_enabled(false) - , m_force_sliders_full_range(false) , m_schedule_background_process(schedule_background_process_func) { -#if ENABLE_REMOVE_TABS_FROM_PLATER if (init(parent, config, process, gcode_preview_data)) { show_hide_ui_elements("none"); load_print(); } -#else - if (init(notebook, config, process, gcode_preview_data)) - { - notebook->AddPage(this, _(L("Preview"))); - show_hide_ui_elements("none"); - load_print(); - } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } -#if ENABLE_REMOVE_TABS_FROM_PLATER bool Preview::init(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data) -#else -bool Preview::init(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data) -#endif // ENABLE_REMOVE_TABS_FROM_PLATER { -#if ENABLE_REMOVE_TABS_FROM_PLATER if ((config == nullptr) || (process == nullptr) || (gcode_preview_data == nullptr)) return false; if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)) return false; -#else - if ((notebook == nullptr) || (config == nullptr) || (process == nullptr) || (gcode_preview_data == nullptr)) - return false; - - // creates this panel add append it to the given notebook as a new page - if (!Create(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize)) - return false; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(this); _3DScene::add_canvas(m_canvas_widget); m_canvas = _3DScene::get_canvas(this->m_canvas_widget); m_canvas->allow_multisample(GLCanvas3DManager::can_multisample()); - m_canvas->enable_shader(true); m_canvas->set_config(m_config); m_canvas->set_process(process); m_canvas->enable_legend_texture(true); @@ -368,43 +333,27 @@ Preview::~Preview() } } -#if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void Preview::set_view_toolbar(GLToolbar* toolbar) -#else -void Preview::set_view_toolbar(GLRadioToolbar* toolbar) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { if (m_canvas != nullptr) m_canvas->set_view_toolbar(toolbar); } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER void Preview::set_number_extruders(unsigned int number_extruders) { if (m_number_extruders != number_extruders) { m_number_extruders = number_extruders; - int type = 0; // color by a feature type - if (number_extruders > 1) - { - int tool_idx = m_choice_view_type->FindString(_(L("Tool"))); - int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type - m_choice_view_type->SetSelection(type); - if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) - m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; + int tool_idx = m_choice_view_type->FindString(_(L("Tool"))); + int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type + m_choice_view_type->SetSelection(type); + if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) + m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; - m_preferred_color_mode = (type == tool_idx) ? "tool_or_feature" : "feature"; - } + m_preferred_color_mode = (type == tool_idx) ? "tool_or_feature" : "feature"; } } -void Preview::reset_gcode_preview_data() -{ - m_gcode_preview_data->reset(); - m_canvas->reset_legend_texture(); -} - void Preview::set_canvas_as_dirty() { m_canvas->set_as_dirty(); @@ -455,6 +404,7 @@ void Preview::load_print() void Preview::reload_print(bool force) { m_canvas->reset_volumes(); + m_canvas->reset_legend_texture(); m_loaded = false; if (!IsShown() && !force) @@ -523,14 +473,14 @@ void Preview::show_hide_ui_elements(const std::string& what) void Preview::reset_sliders() { m_enabled = false; - reset_double_slider(); +// reset_double_slider(); m_double_slider_sizer->Hide((size_t)0); } void Preview::update_sliders(const std::vector& layers_z) { m_enabled = true; - update_double_slider(layers_z, m_force_sliders_full_range); + update_double_slider(layers_z); m_double_slider_sizer->Show((size_t)0); Layout(); } @@ -596,7 +546,8 @@ void Preview::create_double_slider() auto& config = wxGetApp().preset_bundle->project_config; ((config.option("colorprint_heights"))->values) = (m_slider->GetTicksValues()); m_schedule_background_process(); - int type = m_choice_view_type->FindString(_(L("Color Print"))); + bool color_print = !config.option("colorprint_heights")->values.empty(); + int type = m_choice_view_type->FindString(color_print ? _(L("Color Print")) : _(L("Feature type")) ); if (m_choice_view_type->GetSelection() != type) { m_choice_view_type->SetSelection(type); if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) @@ -606,26 +557,70 @@ void Preview::create_double_slider() }); } +// Find an index of a value in a sorted vector, which is in . +// Returns -1 if there is no such member. +static int find_close_layer_idx(const std::vector& zs, double &z, double eps) +{ + if (zs.empty()) + return -1; + auto it_h = std::lower_bound(zs.begin(), zs.end(), z); + if (it_h == zs.end()) { + auto it_l = it_h; + -- it_l; + if (z - *it_l < eps) + return int(zs.size() - 1); + } else if (it_h == zs.begin()) { + if (*it_h - z < eps) + return 0; + } else { + auto it_l = it_h; + -- it_l; + double dist_l = z - *it_l; + double dist_h = *it_h - z; + if (std::min(dist_l, dist_h) < eps) { + return (dist_l < dist_h) ? int(it_l - zs.begin()) : int(it_h - zs.begin()); + } + } + return -1; +} + void Preview::update_double_slider(const std::vector& layers_z, bool force_sliders_full_range) { + // Save the initial slider span. + double z_low = m_slider->GetLowerValueD(); + double z_high = m_slider->GetHigherValueD(); + bool was_empty = m_slider->GetMaxValue() == 0; + bool span_changed = layers_z.empty() || std::abs(layers_z.back() - m_slider->GetMaxValueD()) > 1e-6; + force_sliders_full_range |= was_empty | span_changed; + bool snap_to_min = force_sliders_full_range || m_slider->is_lower_at_min(); + bool snap_to_max = force_sliders_full_range || m_slider->is_higher_at_max(); + std::vector> values; fill_slider_values(values, layers_z); - - m_slider->SetMaxValue(layers_z.size() - 1); - if (force_sliders_full_range) - m_slider->SetHigherValue(layers_z.size() - 1); - m_slider->SetSliderValues(values); - const double z_low = m_slider->GetLowerValueD(); - const double z_high = m_slider->GetHigherValueD(); + assert(m_slider->GetMinValue() == 0); + m_slider->SetMaxValue(layers_z.empty() ? 0 : layers_z.size() - 1); + + int idx_low = 0; + int idx_high = m_slider->GetMaxValue(); + if (! layers_z.empty()) { + if (! snap_to_min) { + int idx_new = find_close_layer_idx(layers_z, z_low, 1e-6); + if (idx_new != -1) + idx_low = idx_new; + } + if (! snap_to_max) { + int idx_new = find_close_layer_idx(layers_z, z_high, 1e-6); + if (idx_new != -1) + idx_high = idx_new; + } + } + m_slider->SetSelectionSpan(idx_low, idx_high); const auto& config = wxGetApp().preset_bundle->project_config; const std::vector &ticks_from_config = (config.option("colorprint_heights"))->values; - m_slider->SetTicksValues(ticks_from_config); - set_double_slider_thumbs(layers_z, z_low, z_high); - bool color_print_enable = (wxGetApp().plater()->printer_technology() == ptFFF); if (color_print_enable) { const auto& config = wxGetApp().preset_bundle->full_config(); @@ -647,8 +642,7 @@ void Preview::fill_slider_values(std::vector> &values, // All ticks that would end up outside the slider range should be erased. // TODO: this should be placed into more appropriate part of code, // this function is e.g. not called when the last object is deleted - auto& config = wxGetApp().preset_bundle->project_config; - std::vector &ticks_from_config = (config.option("colorprint_heights"))->values; + std::vector &ticks_from_config = (wxGetApp().preset_bundle->project_config.option("colorprint_heights"))->values; unsigned int old_size = ticks_from_config.size(); ticks_from_config.erase(std::remove_if(ticks_from_config.begin(), ticks_from_config.end(), [values](double val) { return values.back().second < val; }), @@ -657,32 +651,6 @@ void Preview::fill_slider_values(std::vector> &values, m_schedule_background_process(); } -void Preview::set_double_slider_thumbs(const std::vector &layers_z, - const double z_low, - const double z_high) -{ - // Force slider full range only when slider is created. - // Support selected diapason on the all next steps - if (z_high == 0.0) { - m_slider->SetLowerValue(0); - m_slider->SetHigherValue(layers_z.size() - 1); - return; - } - - for (int i = layers_z.size() - 1; i >= 0; i--) -// if (z_low >= layers_z[i]) { - if (fabs(z_low - layers_z[i]) <= 1e-6) { - m_slider->SetLowerValue(i); - break; - } - for (int i = layers_z.size() - 1; i >= 0; i--) -// if (z_high >= layers_z[i]) { - if (fabs(z_high-layers_z[i]) <= 1e-6) { - m_slider->SetHigherValue(i); - break; - } -} - void Preview::reset_double_slider() { m_slider->SetHigherValue(0); @@ -701,7 +669,7 @@ void Preview::update_double_slider_from_canvas(wxKeyEvent& event) if (key == 'U' || key == 'D') { const int new_pos = key == 'U' ? m_slider->GetHigherValue() + 1 : m_slider->GetHigherValue() - 1; m_slider->SetHigherValue(new_pos); - if (event.ShiftDown()) m_slider->SetLowerValue(m_slider->GetHigherValue()); + if (event.ShiftDown() || m_slider->is_one_layer()) m_slider->SetLowerValue(m_slider->GetHigherValue()); } else if (key == 'S') m_slider->ChangeOneLayerLock(); @@ -763,7 +731,8 @@ void Preview::load_print_as_fff() // Collect colors per extruder. std::vector colors; - if (!m_gcode_preview_data->empty() || (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::Tool)) + bool gcode_preview_data_valid = print->is_step_done(psGCodeExport) && ! m_gcode_preview_data->empty(); + if (gcode_preview_data_valid || (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::Tool)) { const ConfigOptionStrings* extruders_opt = dynamic_cast(m_config->option("extruder_colour")); const ConfigOptionStrings* filamemts_opt = dynamic_cast(m_config->option("filament_colour")); @@ -786,18 +755,8 @@ void Preview::load_print_as_fff() if (IsShown()) { - // used to set the sliders to the extremes of the current zs range - m_force_sliders_full_range = false; - - if (m_gcode_preview_data->empty()) + if (gcode_preview_data_valid) { - // load skirt and brim - m_canvas->load_preview(colors); - show_hide_ui_elements("simple"); - } - else - { - m_force_sliders_full_range = (m_canvas->get_volumes_count() == 0); m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); show_hide_ui_elements("full"); @@ -809,8 +768,15 @@ void Preview::load_print_as_fff() reset_sliders(); m_canvas_widget->Refresh(); } + } + else + { + // load skirt and brim + m_canvas->load_preview(colors); + show_hide_ui_elements("simple"); } + if (n_layers > 0) update_sliders(m_canvas->get_current_print_zs(true)); @@ -856,7 +822,6 @@ void Preview::load_print_as_sla() { std::vector layer_zs; std::copy(zs.begin(), zs.end(), std::back_inserter(layer_zs)); - m_force_sliders_full_range = true; update_sliders(layer_zs); } diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 23e6a682f..ccff885f2 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -21,24 +21,13 @@ class DynamicPrintConfig; class Print; class BackgroundSlicingProcess; class GCodePreviewData; -#if ENABLE_REMOVE_TABS_FROM_PLATER class Model; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER namespace GUI { class GLCanvas3D; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#if ENABLE_REMOVE_TABS_FROM_PLATER class GLToolbar; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER -#else -#if ENABLE_REMOVE_TABS_FROM_PLATER -class GLRadioToolbar; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#if ENABLE_REMOVE_TABS_FROM_PLATER class View3D : public wxPanel { wxGLCanvas* m_canvas_widget; @@ -59,11 +48,7 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } GLCanvas3D* get_canvas3d() { return m_canvas; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_view_toolbar(GLToolbar* toolbar); -#else - void set_view_toolbar(GLRadioToolbar* toolbar); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_as_dirty(); void set_bed_shape(const Pointfs& shape); @@ -89,7 +74,6 @@ public: private: bool init(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process); }; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER class Preview : public wxPanel { @@ -117,30 +101,18 @@ class Preview : public wxPanel bool m_loaded; bool m_enabled; - bool m_force_sliders_full_range; PrusaDoubleSlider* m_slider {nullptr}; public: -#if ENABLE_REMOVE_TABS_FROM_PLATER Preview(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process = [](){}); -#else - Preview(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process = [](){}); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER virtual ~Preview(); wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } -#if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_view_toolbar(GLToolbar* toolbar); -#else - void set_view_toolbar(GLRadioToolbar* toolbar); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#endif // ENABLE_REMOVE_TABS_FROM_PLATER void set_number_extruders(unsigned int number_extruders); - void reset_gcode_preview_data(); void set_canvas_as_dirty(); void set_enabled(bool enabled); void set_bed_shape(const Pointfs& shape); @@ -154,11 +126,7 @@ public: void refresh_print(); private: -#if ENABLE_REMOVE_TABS_FROM_PLATER bool init(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data); -#else - bool init(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER void bind_event_handlers(); void unbind_event_handlers(); @@ -178,12 +146,9 @@ private: // Create/Update/Reset double slider on 3dPreview void create_double_slider(); - void update_double_slider(const std::vector& layers_z, bool force_sliders_full_range); + void update_double_slider(const std::vector& layers_z, bool force_sliders_full_range = false); void fill_slider_values(std::vector> &values, const std::vector &layers_z); - void set_double_slider_thumbs( const std::vector &layers_z, - const double z_low, - const double z_high); void reset_double_slider(); // update DoubleSlider after keyDown in canvas void update_double_slider_from_canvas(wxKeyEvent& event); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp new file mode 100644 index 000000000..303f3dae8 --- /dev/null +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -0,0 +1,167 @@ +#include "KBShortcutsDialog.hpp" +#include "I18N.hpp" +#include "libslic3r/Utils.hpp" +#include "GUI.hpp" +#include +#include "GUI_App.hpp" + +namespace Slic3r { +namespace GUI { + +KBShortcutsDialog::KBShortcutsDialog() + : wxDialog(NULL, wxID_ANY, _(L("Slic3r Prusa Edition - Keyboard Shortcuts")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) +{ + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + + auto main_sizer = new wxBoxSizer(wxVERTICAL); + + // logo + wxBitmap logo_bmp = wxBitmap(from_u8(Slic3r::var("Slic3r_32px.png")), wxBITMAP_TYPE_PNG); + + // fonts + wxFont head_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold(); +#ifdef __WXOSX__ + head_font.SetPointSize(14); +#else + head_font.SetPointSize(12); +#endif // __WXOSX__ + + const wxFont& font = wxGetApp().small_font(); + const wxFont& bold_font = wxGetApp().bold_font(); + + fill_shortcuts(); + + auto panel = new wxPanel(this); + auto main_grid_sizer = new wxFlexGridSizer(2, 10, 10); + panel->SetSizer(main_grid_sizer); + main_sizer->Add(panel, 1, wxEXPAND | wxALL, 0); + + wxBoxSizer* l_sizer = new wxBoxSizer(wxVERTICAL); + main_grid_sizer->Add(l_sizer, 0); + + wxBoxSizer* r_sizer = new wxBoxSizer(wxVERTICAL); + main_grid_sizer->Add(r_sizer, 0); + + for (auto& sc : m_full_shortcuts) + { + auto sizer = sc.first == _(L("Main Shortcuts")) ? l_sizer : r_sizer; + wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(hsizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10); + + // logo + auto *logo = new wxStaticBitmap(panel, wxID_ANY, logo_bmp); + hsizer->Add(logo, 0, wxEXPAND | wxLEFT | wxRIGHT, 15); + + // head + wxStaticText* head = new wxStaticText(panel, wxID_ANY, sc.first, wxDefaultPosition, wxSize(200,-1)); + head->SetFont(head_font); + hsizer->Add(head, 0, wxALIGN_CENTER_VERTICAL); + + // Shortcuts list + auto grid_sizer = new wxFlexGridSizer(2, 5, 15); + sizer->Add(grid_sizer, 0, wxEXPAND | wxLEFT| wxRIGHT, 15); + + for (auto pair : sc.second) + { + auto shortcut = new wxStaticText(panel, wxID_ANY, _(pair.first)); + shortcut->SetFont(bold_font); + grid_sizer->Add(shortcut, -1, wxALIGN_CENTRE_VERTICAL); + + auto description = new wxStaticText(panel, wxID_ANY, _(pair.second)); + description->SetFont(font); + grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL); + } + } + + wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK); + + this->SetEscapeId(wxID_OK); + this->Bind(wxEVT_BUTTON, &KBShortcutsDialog::onCloseDialog, this, wxID_OK); + main_sizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 15); + + this->Bind(wxEVT_LEFT_DOWN, &KBShortcutsDialog::onCloseDialog, this); + + SetSizer(main_sizer); + main_sizer->SetSizeHints(this); +} + +void KBShortcutsDialog::fill_shortcuts() +{ +#ifdef __WXOSX__ + const std::string ctrl = "⌘"; + const std::string alt = "⌥"; +#else + const std::string ctrl = "Ctrl+"; + const std::string alt = "Alt+"; +#endif // __WXOSX__ + + Shortcuts main_shortcuts; + main_shortcuts.reserve(25); + + main_shortcuts.push_back(Shortcut(ctrl+"O" ,L("Open project STL/OBJ/AMF/3MF with config, delete bed"))); + main_shortcuts.push_back(Shortcut(ctrl+"I" ,L("Import STL/OBJ/AMF/3MF without config, keep bed"))); + main_shortcuts.push_back(Shortcut(ctrl+"L" ,L("Load Config from .ini/amf/3mf/gcode"))); + main_shortcuts.push_back(Shortcut(ctrl+"G" ,L("Export Gcode"))); + main_shortcuts.push_back(Shortcut(ctrl+"S" ,L("Save project (3MF)"))); + main_shortcuts.push_back(Shortcut(ctrl+alt+"L" ,L("Load Config from .ini/amf/3mf/gcode and merge"))); + main_shortcuts.push_back(Shortcut(ctrl+"R" ,L("(Re)slice"))); + main_shortcuts.push_back(Shortcut(ctrl+"U" ,L("Quick slice"))); + main_shortcuts.push_back(Shortcut(ctrl+"Shift+U" ,L("Repeat last quick slice"))); + main_shortcuts.push_back(Shortcut(ctrl+"1" ,L("Select Plater Tab"))); + main_shortcuts.push_back(Shortcut(ctrl+alt+"U" ,L("Quick slice and Save as"))); + main_shortcuts.push_back(Shortcut(ctrl+"2" ,L("Select Print Settings Tab"))); + main_shortcuts.push_back(Shortcut(ctrl+"3" ,L("Select Filament Setting Tab"))); + main_shortcuts.push_back(Shortcut(ctrl+"4" ,L("Select Printer Setting Tab"))); + main_shortcuts.push_back(Shortcut(ctrl+"5" ,L("Switch to 3D"))); + main_shortcuts.push_back(Shortcut(ctrl+"6" ,L("Switch to Preview"))); + main_shortcuts.push_back(Shortcut(ctrl+"P" ,L("Preferences"))); + main_shortcuts.push_back(Shortcut("0-6" ,L("Camera view "))); + main_shortcuts.push_back(Shortcut("+" ,L("Add Instance to selected object "))); + main_shortcuts.push_back(Shortcut("-" ,L("Remove Instance from selected object"))); + main_shortcuts.push_back(Shortcut("?" ,L("Show keyboard shortcuts list"))); + main_shortcuts.push_back(Shortcut("PgUp/PgDn" ,L("Switch between 3D and Preview"))); + main_shortcuts.push_back(Shortcut("Shift+LeftMouse" ,L("Select multiple object/Move multiple object"))); + + m_full_shortcuts.emplace(_(L("Main Shortcuts")), main_shortcuts); + + + Shortcuts plater_shortcuts; + plater_shortcuts.reserve(20); + + plater_shortcuts.push_back(Shortcut("A", L("Arrange"))); + plater_shortcuts.push_back(Shortcut(ctrl+"A", L("Select All objects"))); + plater_shortcuts.push_back(Shortcut("Del", L("Delete selected"))); + plater_shortcuts.push_back(Shortcut(ctrl+"Del", L("Delete all"))); + plater_shortcuts.push_back(Shortcut("M", L("Gizmo move"))); + plater_shortcuts.push_back(Shortcut("S", L("Gizmo scale"))); + plater_shortcuts.push_back(Shortcut("R", L("Gizmo rotate"))); + plater_shortcuts.push_back(Shortcut("C", L("Gizmo cut"))); + plater_shortcuts.push_back(Shortcut("F", L("Gizmo Place face on bed"))); + plater_shortcuts.push_back(Shortcut("L", L("Gizmo SLA support points"))); + plater_shortcuts.push_back(Shortcut("B", L("Zoom to Bed"))); + plater_shortcuts.push_back(Shortcut("Z", L("Zoom to all objects in scene, if none selected"))); + plater_shortcuts.push_back(Shortcut("Z", L("Zoom to selected object"))); + plater_shortcuts.push_back(Shortcut("I", L("Zoom in"))); + plater_shortcuts.push_back(Shortcut("O", L("Zoom out"))); + plater_shortcuts.push_back(Shortcut("ESC", L("Unselect gizmo, keep object selection"))); + + m_full_shortcuts.emplace(_(L("Plater Shortcuts")), plater_shortcuts); + + + Shortcuts preview_shortcuts; + preview_shortcuts.reserve(2); + + preview_shortcuts.push_back(Shortcut(L("Arrow Up"), L("Upper Layer"))); + preview_shortcuts.push_back(Shortcut(L("Arrow Down"), L("Lower Layer"))); + + m_full_shortcuts.emplace(_(L("Preview Shortcuts")), preview_shortcuts); +} + +void KBShortcutsDialog::onCloseDialog(wxEvent &) +{ + this->EndModal(wxID_CLOSE); + this->Close(); +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/KBShortcutsDialog.hpp b/src/slic3r/GUI/KBShortcutsDialog.hpp new file mode 100644 index 000000000..8517544b5 --- /dev/null +++ b/src/slic3r/GUI/KBShortcutsDialog.hpp @@ -0,0 +1,32 @@ +#ifndef slic3r_GUI_KBShortcutsDialog_hpp_ +#define slic3r_GUI_KBShortcutsDialog_hpp_ + +#include +#include + +namespace Slic3r { +namespace GUI { + +class KBShortcutsDialog : public wxDialog +{ + typedef std::pair Shortcut; + typedef std::vector< Shortcut > Shortcuts; + typedef std::map ShortcutsMap; + + wxString text_info {wxEmptyString}; + + ShortcutsMap m_full_shortcuts; + +public: + KBShortcutsDialog(); + + void fill_shortcuts(); + +private: + void onCloseDialog(wxEvent &); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 67151c11a..b4d779dfe 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -18,6 +18,7 @@ #include "ProgressStatusBar.hpp" #include "3DScene.hpp" #include "AppConfig.hpp" +#include "PrintHostDialogs.hpp" #include "wxExtensions.hpp" #include "I18N.hpp" @@ -27,10 +28,9 @@ namespace Slic3r { namespace GUI { -MainFrame::MainFrame(const bool no_plater, const bool loaded) : +MainFrame::MainFrame() : wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE), - m_no_plater(no_plater), - m_loaded(loaded) + m_printhost_queue_dlg(new PrintHostQueueDialog(this)) { // Load the icon either from the exe, or from the ico file. #if _WIN32 @@ -123,11 +123,9 @@ void MainFrame::init_tabpanel() } }); - if (!m_no_plater) { - m_plater = new Slic3r::GUI::Plater(m_tabpanel, this); - wxGetApp().plater_ = m_plater; - m_tabpanel->AddPage(m_plater, _(L("Plater"))); - } + m_plater = new Slic3r::GUI::Plater(m_tabpanel, this); + wxGetApp().plater_ = m_plater; + m_tabpanel->AddPage(m_plater, _(L("Plater"))); // The following event is emited by Tab implementation on config value change. Bind(EVT_TAB_VALUE_CHANGED, &MainFrame::on_value_changed, this); @@ -226,7 +224,7 @@ void MainFrame::init_menubar() wxMenuItem* item_open = append_menu_item(fileMenu, wxID_ANY, _(L("Open…\tCtrl+O")), _(L("Open a project file")), [this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "brick_add.png"); wxMenuItem* item_save = append_menu_item(fileMenu, wxID_ANY, _(L("Save\tCtrl+S")), _(L("Save current project file")), - [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(m_plater->get_project_filename().wx_str()); }, "disk.png"); + [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename())); }, "disk.png"); wxMenuItem* item_save_as = append_menu_item(fileMenu, wxID_ANY, _(L("Save as…\tCtrl+Alt+S")), _(L("Save current project file as")), [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, "disk.png"); @@ -325,43 +323,46 @@ void MainFrame::init_menubar() { size_t tab_offset = 0; if (m_plater) { -#if ENABLE_REMOVE_TABS_FROM_PLATER - append_menu_item(windowMenu, wxID_ANY, L("Plater Tab\tCtrl+1"), L("Show the plater"), + append_menu_item(windowMenu, wxID_HIGHEST + 1, L("Plater Tab\tCtrl+1"), L("Show the plater"), [this](wxCommandEvent&) { select_tab(0); }, "application_view_tile.png"); -#else - append_menu_item(windowMenu, wxID_ANY, L("Select Plater Tab\tCtrl+1"), L("Show the plater"), - [this](wxCommandEvent&) { select_tab(0); }, "application_view_tile.png"); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER tab_offset += 1; } if (tab_offset > 0) { windowMenu->AppendSeparator(); } -#if ENABLE_REMOVE_TABS_FROM_PLATER - append_menu_item(windowMenu, wxID_ANY, L("Print Settings Tab\tCtrl+2"), L("Show the print settings"), + append_menu_item(windowMenu, wxID_HIGHEST + 2, L("Print Settings Tab\tCtrl+2"), L("Show the print settings"), [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png"); - append_menu_item(windowMenu, wxID_ANY, L("Filament Settings Tab\tCtrl+3"), L("Show the filament settings"), + append_menu_item(windowMenu, wxID_HIGHEST + 3, L("Filament Settings Tab\tCtrl+3"), L("Show the filament settings"), [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool.png"); - append_menu_item(windowMenu, wxID_ANY, L("Printer Settings Tab\tCtrl+4"), L("Show the printer settings"), + append_menu_item(windowMenu, wxID_HIGHEST + 4, L("Printer Settings Tab\tCtrl+4"), L("Show the printer settings"), [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png"); if (m_plater) { windowMenu->AppendSeparator(); - wxMenuItem* item_3d = append_menu_item(windowMenu, wxID_ANY, L("3D\tCtrl+5"), L("Show the 3D editing view"), - [this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, ""); - wxMenuItem* item_preview = append_menu_item(windowMenu, wxID_ANY, L("Preview\tCtrl+6"), L("Show the 3D slices preview"), - [this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, ""); + wxMenuItem* item_3d = append_menu_item(windowMenu, wxID_HIGHEST + 5, L("3D\tCtrl+5"), L("Show the 3D editing view"), + [this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, ""); + wxMenuItem* item_preview = append_menu_item(windowMenu, wxID_HIGHEST + 6, L("Preview\tCtrl+6"), L("Show the 3D slices preview"), + [this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, ""); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_3d->GetId()); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_preview->GetId()); } -#else - append_menu_item(windowMenu, wxID_ANY, L("Select Print Settings Tab\tCtrl+2"), L("Show the print settings"), - [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png"); - append_menu_item(windowMenu, wxID_ANY, L("Select Filament Settings Tab\tCtrl+3"), L("Show the filament settings"), - [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool.png"); - append_menu_item(windowMenu, wxID_ANY, L("Select Printer Settings Tab\tCtrl+4"), L("Show the printer settings"), - [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png"); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER + +#if _WIN32 + // This is needed on Windows to fake the CTRL+# of the window menu when using the numpad + wxAcceleratorEntry entries[6]; + entries[0].Set(wxACCEL_CTRL, WXK_NUMPAD1, wxID_HIGHEST + 1); + entries[1].Set(wxACCEL_CTRL, WXK_NUMPAD2, wxID_HIGHEST + 2); + entries[2].Set(wxACCEL_CTRL, WXK_NUMPAD3, wxID_HIGHEST + 3); + entries[3].Set(wxACCEL_CTRL, WXK_NUMPAD4, wxID_HIGHEST + 4); + entries[4].Set(wxACCEL_CTRL, WXK_NUMPAD5, wxID_HIGHEST + 5); + entries[5].Set(wxACCEL_CTRL, WXK_NUMPAD6, wxID_HIGHEST + 6); + wxAcceleratorTable accel(6, entries); + SetAcceleratorTable(accel); +#endif // _WIN32 + + windowMenu->AppendSeparator(); + append_menu_item(windowMenu, wxID_ANY, L("Print Host Upload Queue"), L("Display the Print Host Upload Queue window"), + [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "arrow_up.png"); } // View menu @@ -415,6 +416,9 @@ void MainFrame::init_menubar() [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://github.com/prusa3d/supermerill/issues/new"); }); append_menu_item(helpMenu, wxID_ANY, _(L("About Slic3r")), _(L("Show about dialog")), [this](wxCommandEvent&) { Slic3r::GUI::about(); }); + helpMenu->AppendSeparator(); + append_menu_item(helpMenu, wxID_ANY, _(L("Keyboard Shortcuts")) + "\t?", _(L("Show the list of the keyboard shortcuts")), + [this](wxCommandEvent&) { wxGetApp().keyboard_shortcuts(); }); } // menubar @@ -795,7 +799,7 @@ void MainFrame::on_value_changed(wxCommandEvent& event) void MainFrame::update_ui_from_settings() { bool bp_on = wxGetApp().app_config->get("background_processing") == "1"; - m_menu_item_reslice_now->Enable(bp_on); + m_menu_item_reslice_now->Enable(!bp_on); m_plater->sidebar().show_reslice(!bp_on); m_plater->sidebar().Layout(); if (m_plater) diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 2559b5ed1..e0411b6da 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -21,7 +21,9 @@ class ProgressStatusBar; namespace GUI { + class Tab; +class PrintHostQueueDialog; enum QuickSlice { @@ -40,10 +42,7 @@ struct PresetTab { class MainFrame : public wxFrame { - bool m_no_plater; - bool m_loaded; - int m_lang_ch_event; - int m_preferences_event; + bool m_loaded {false}; wxString m_qs_last_input_file = wxEmptyString; wxString m_qs_last_output_file = wxEmptyString; @@ -52,6 +51,8 @@ class MainFrame : public wxFrame wxMenuItem* m_menu_item_repeat { nullptr }; wxMenuItem* m_menu_item_reslice_now { nullptr }; + PrintHostQueueDialog *m_printhost_queue_dlg; + std::string get_base_name(const wxString full_name) const ; std::string get_dir_name(const wxString full_name) const ; @@ -67,8 +68,7 @@ class MainFrame : public wxFrame bool can_delete_all() const; public: - MainFrame() {} - MainFrame(const bool no_plater, const bool loaded); + MainFrame(); ~MainFrame() {} Plater* plater() { return m_plater; } @@ -93,6 +93,8 @@ public: void select_tab(size_t tab) const; void select_view(const std::string& direction); + PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; } + Plater* m_plater { nullptr }; wxNotebook* m_tabpanel { nullptr }; wxProgressDialog* m_progress_dialog { nullptr }; diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index ae7b40484..d6b8b438e 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" @@ -61,8 +62,11 @@ MsgDialog::~MsgDialog() {} // ErrorDialog -ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) : - MsgDialog(parent, _(L("Slic3r error")), _(L("Slic3r has encountered an error")), wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG)) +ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) + : MsgDialog(parent, _(L("Slic3r error")), _(L("Slic3r has encountered an error")), + wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG), + wxID_NONE) + , msg(msg) { auto *panel = new wxScrolledWindow(this); auto *p_sizer = new wxBoxSizer(wxVERTICAL); @@ -77,6 +81,20 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) : content_sizer->Add(panel, 1, wxEXPAND); + auto *btn_copy = new wxButton(this, wxID_ANY, _(L("Copy to clipboard"))); + btn_copy->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) { + if (wxTheClipboard->Open()) { + wxTheClipboard->SetData(new wxTextDataObject(this->msg)); // Note: the clipboard takes ownership of the pointer + wxTheClipboard->Close(); + } + }); + + auto *btn_ok = new wxButton(this, wxID_OK); + btn_ok->SetFocus(); + + btn_sizer->Add(btn_copy, 0, wxRIGHT, HORIZ_SPACING); + btn_sizer->Add(btn_ok); + SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT)); Fit(); } diff --git a/src/slic3r/GUI/MsgDialog.hpp b/src/slic3r/GUI/MsgDialog.hpp index ca349eb5c..6064d2a9f 100644 --- a/src/slic3r/GUI/MsgDialog.hpp +++ b/src/slic3r/GUI/MsgDialog.hpp @@ -50,14 +50,18 @@ protected: // Generic error dialog, used for displaying exceptions -struct ErrorDialog : MsgDialog +class ErrorDialog : public MsgDialog { +public: ErrorDialog(wxWindow *parent, const wxString &msg); ErrorDialog(ErrorDialog &&) = delete; ErrorDialog(const ErrorDialog &) = delete; ErrorDialog &operator=(ErrorDialog &&) = delete; ErrorDialog &operator=(const ErrorDialog &) = delete; virtual ~ErrorDialog(); + +private: + wxString msg; }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8db804c12..c9440a9d0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -224,7 +224,8 @@ PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) : if (marker == LABEL_ITEM_MARKER) { this->SetSelection(this->last_selected); evt.StopPropagation(); - } else if (this->last_selected != selected_item) { + } else if ( this->last_selected != selected_item || + wxGetApp().get_tab(this->preset_type)->get_presets()->current_is_dirty() ) { this->last_selected = selected_item; evt.SetInt(this->preset_type); evt.Skip(); @@ -245,7 +246,8 @@ PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) : // Swallow the mouse click and open the color picker. auto data = new wxColourData(); data->SetChooseFull(1); - auto dialog = new wxColourDialog(wxGetApp().mainframe, data); + auto dialog = new wxColourDialog(/* wxGetApp().mainframe */this, data); + dialog->CenterOnParent(); if (dialog->ShowModal() == wxID_OK) { DynamicPrintConfig cfg = *wxGetApp().get_tab(Preset::TYPE_PRINTER)->get_config(); @@ -484,7 +486,7 @@ Sidebar::Sidebar(Plater *parent) : wxPanel(parent), p(new priv(parent)) { p->scrolled = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(400, -1)); - p->scrolled->SetScrollbars(0, 1, 1, 1); + p->scrolled->SetScrollbars(0, 20, 1, 2); // Sizer in the scrolled area auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL); @@ -732,8 +734,7 @@ void Sidebar::show_info_sizer() p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast(model_object->materials_count()))); auto& stats = model_object->volumes.front()->mesh.stl.stats; - auto sf = model_instance->get_scaling_factor(); - p->object_info->info_volume->SetLabel(wxString::Format("%.2f", size(0) * size(1) * size(2) * sf(0) * sf(1) * sf(2))); + p->object_info->info_volume->SetLabel(wxString::Format("%.2f", size(0) * size(1) * size(2))); p->object_info->info_facets->SetLabel(wxString::Format(_(L("%d (%d shells)")), static_cast(model_object->facets_count()), stats.number_of_parts)); int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + @@ -863,7 +864,7 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi std::vector paths; for (const auto &filename : filenames) { - fs::path path(filename); + fs::path path(into_path(filename)); if (std::regex_match(path.string(), pattern_drop)) { paths.push_back(std::move(path)); @@ -899,32 +900,12 @@ struct Plater::priv Slic3r::GCodePreviewData gcode_preview_data; // GUI elements -#if ENABLE_REMOVE_TABS_FROM_PLATER wxSizer* panel_sizer; wxPanel* current_panel; std::vector panels; -#else - wxNotebook *notebook; - EventGuard guard_on_notebook_changed; - // Note: ^ The on_notebook_changed is guarded here because the wxNotebook d-tor tends to generate - // wxEVT_NOTEBOOK_PAGE_CHANGED events on some platforms, which causes them to be received by a freed Plater. - // EventGuard unbinds the handler in its d-tor. -#endif // ENABLE_REMOVE_TABS_FROM_PLATER Sidebar *sidebar; -#if ENABLE_REMOVE_TABS_FROM_PLATER View3D* view3D; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLToolbar view_toolbar; -#else - GLRadioToolbar view_toolbar; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#else -#if !ENABLE_IMGUI - wxPanel *panel3d; -#endif // not ENABLE_IMGUI - wxGLCanvas *canvas3Dwidget; // TODO: Use GLCanvas3D when we can - GLCanvas3D *canvas3D; -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER Preview *preview; wxString project_filename; @@ -939,14 +920,14 @@ struct Plater::priv static const std::regex pattern_bundle; static const std::regex pattern_3mf; static const std::regex pattern_zip_amf; + static const std::regex pattern_any_amf; priv(Plater *q, MainFrame *main_frame); void update(bool force_full_scene_refresh = false); void select_view(const std::string& direction); -#if ENABLE_REMOVE_TABS_FROM_PLATER void select_view_3D(const std::string& name); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER + void select_next_view_3D(); void update_ui_from_settings(); ProgressStatusBar* statusbar(); std::string get_config(const std::string &key) const; @@ -986,19 +967,22 @@ struct Plater::priv // update_background_process() reports, that the Print / SLAPrint is invalid, and the error message // was sent to the status line. UPDATE_BACKGROUND_PROCESS_INVALID = 4, + // Restart even if the background processing is disabled. + UPDATE_BACKGROUND_PROCESS_FORCE_RESTART = 8, + // Restart for G-code (or SLA zip) export or upload. + UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT = 16, }; // returns bit mask of UpdateBackgroundProcessReturnState unsigned int update_background_process(); + // Restart background processing thread based on a bitmask of UpdateBackgroundProcessReturnState. + bool restart_background_process(unsigned int state); + void update_restart_background_process(bool force_scene_update, bool force_preview_update); void export_gcode(fs::path output_path, PrintHostJob upload_job); - void async_apply_config(); void reload_from_disk(); void fix_through_netfabb(const int obj_idx); -#if ENABLE_REMOVE_TABS_FROM_PLATER void set_current_panel(wxPanel* panel); -#else - void on_notebook_changed(wxBookCtrlEvent&); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER + void on_select_preset(wxCommandEvent&); void on_slicing_update(SlicingStatusEvent&); void on_slicing_completed(wxCommandEvent&); @@ -1025,15 +1009,14 @@ private: bool complit_init_object_menu(); bool complit_init_sla_object_menu(); bool complit_init_part_menu(); -#if ENABLE_REMOVE_TABS_FROM_PLATER void init_view_toolbar(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER bool can_delete_object() const; bool can_increase_instances() const; bool can_decrease_instances() const; bool can_split_to_objects() const; bool can_split_to_volumes() const; + bool can_split() const; bool layers_height_allowed() const; bool can_delete_all() const; bool can_arrange() const; @@ -1046,6 +1029,8 @@ private: const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase); const std::regex Plater::priv::pattern_3mf(".*3mf", std::regex::icase); const std::regex Plater::priv::pattern_zip_amf(".*[.]zip[.]amf", std::regex::icase); +const std::regex Plater::priv::pattern_any_amf(".*[.](amf|amf[.]xml|zip[.]amf)", std::regex::icase); + Plater::priv::priv(Plater *q, MainFrame *main_frame) : q(q) , main_frame(main_frame) @@ -1056,25 +1041,10 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology" })) -#if !ENABLE_REMOVE_TABS_FROM_PLATER - , notebook(new wxNotebook(q, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_BOTTOM)) - , guard_on_notebook_changed(notebook, wxEVT_NOTEBOOK_PAGE_CHANGED, &priv::on_notebook_changed, this) -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER , sidebar(new Sidebar(q)) -#if !ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_IMGUI - , canvas3Dwidget(GLCanvas3DManager::create_wxglcanvas(notebook)) -#else - , panel3d(new wxPanel(notebook, wxID_ANY)) - , canvas3Dwidget(GLCanvas3DManager::create_wxglcanvas(panel3d)) -#endif // ENABLE_IMGUI - , canvas3D(nullptr) -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER , delayed_scene_refresh(false) , project_filename(wxEmptyString) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE , view_toolbar(GLToolbar::Radio) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { arranging.store(false); rotoptimizing.store(false); @@ -1094,78 +1064,32 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) sla_print.set_status_callback(statuscb); this->q->Bind(EVT_SLICING_UPDATE, &priv::on_slicing_update, this); -#if !ENABLE_REMOVE_TABS_FROM_PLATER - _3DScene::add_canvas(canvas3Dwidget); - this->canvas3D = _3DScene::get_canvas(this->canvas3Dwidget); - this->canvas3D->allow_multisample(GLCanvas3DManager::can_multisample()); -#if ENABLE_IMGUI - notebook->AddPage(canvas3Dwidget, _(L("3D"))); -#else - auto *panel3dsizer = new wxBoxSizer(wxVERTICAL); - panel3dsizer->Add(canvas3Dwidget, 1, wxEXPAND); - auto *panel_gizmo_widgets = new wxPanel(panel3d, wxID_ANY); - panel_gizmo_widgets->SetSizer(new wxBoxSizer(wxVERTICAL)); - panel3dsizer->Add(panel_gizmo_widgets, 0, wxEXPAND); - - panel3d->SetSizer(panel3dsizer); - notebook->AddPage(panel3d, _(L("3D"))); - - canvas3D->set_external_gizmo_widgets_parent(panel_gizmo_widgets); -#endif // ENABLE_IMGUI -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER - -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D = new View3D(q, &model, config, &background_process); preview = new Preview(q, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); }); + // Let the Tab key switch between the 3D view and the layer preview. + view3D->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->select_next_view_3D(); }); + preview->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->select_next_view_3D(); }); panels.push_back(view3D); panels.push_back(preview); -#else - preview = new GUI::Preview(notebook, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); }); - - // XXX: If have OpenGL - this->canvas3D->enable_picking(true); - this->canvas3D->enable_moving(true); - // XXX: more config from 3D.pm - this->canvas3D->set_model(&model); - this->canvas3D->set_process(&background_process); - this->canvas3D->set_config(config); - this->canvas3D->enable_gizmos(true); - this->canvas3D->enable_toolbar(true); - this->canvas3D->enable_shader(true); - this->canvas3D->enable_force_zoom_to_bed(true); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER this->background_process_timer.SetOwner(this->q, 0); - this->q->Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->async_apply_config(); }); + this->q->Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->update_restart_background_process(false, false); }); auto *bed_shape = config->opt("bed_shape"); -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->set_bed_shape(bed_shape->values); -#else - this->canvas3D->set_bed_shape(bed_shape->values); - this->canvas3D->zoom_to_bed(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER preview->set_bed_shape(bed_shape->values); update(); auto *hsizer = new wxBoxSizer(wxHORIZONTAL); -#if ENABLE_REMOVE_TABS_FROM_PLATER panel_sizer = new wxBoxSizer(wxHORIZONTAL); panel_sizer->Add(view3D, 1, wxEXPAND | wxALL, 0); panel_sizer->Add(preview, 1, wxEXPAND | wxALL, 0); hsizer->Add(panel_sizer, 1, wxEXPAND | wxALL, 0); -#else - hsizer->Add(notebook, 1, wxEXPAND | wxTOP, 1); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER hsizer->Add(sidebar, 0, wxEXPAND | wxLEFT | wxRIGHT, 0); q->SetSizer(hsizer); -#if ENABLE_REMOVE_TABS_FROM_PLATER - set_current_panel(view3D); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER - init_object_menu(); // Events: @@ -1176,7 +1100,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) sidebar->Bind(EVT_OBJ_LIST_OBJECT_SELECT, [this](wxEvent&) { priv::selection_changed(); }); sidebar->Bind(EVT_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); }); -#if ENABLE_REMOVE_TABS_FROM_PLATER wxGLCanvas* view3D_canvas = view3D->get_wxglcanvas(); // 3DScene events: view3D_canvas->Bind(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); }); @@ -1186,9 +1109,15 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_MODEL_UPDATE, [this](SimpleEvent&) { this->schedule_background_process(); }); 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_INCREASE_INSTANCES, [q](Event &evt) { evt.data == 1 ? q->increase_instances() : q->decrease_instances(); }); + view3D_canvas->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](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(); }); view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_ROTATED, [this](SimpleEvent&) { update(); }); + view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_SCALED, [this](SimpleEvent&) { update(); }); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION view3D_canvas->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event &evt) { this->sidebar->enable_buttons(evt.data); }); view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_GEOMETRY, &priv::on_update_geometry, this); view3D_canvas->Bind(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, &priv::on_3dcanvas_mouse_dragging_finished, this); @@ -1203,51 +1132,25 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this); view3D_canvas->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this); view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); }); -#else - // 3DScene events: - canvas3Dwidget->Bind(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this); - canvas3Dwidget->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); - canvas3Dwidget->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this); - canvas3Dwidget->Bind(EVT_GLCANVAS_MODEL_UPDATE, [this](SimpleEvent&) { this->schedule_background_process(); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [q](Event &evt) { evt.data == 1 ? q->increase_instances() : q->decrease_instances(); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); - canvas3Dwidget->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event &evt) { this->sidebar->enable_buttons(evt.data); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_UPDATE_GEOMETRY, &priv::on_update_geometry, this); - canvas3Dwidget->Bind(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, &priv::on_3dcanvas_mouse_dragging_finished, this); - // 3DScene/Toolbar: - canvas3Dwidget->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); } ); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_DELETE_ALL, [this](SimpleEvent&) { reset(); }); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { arrange(); }); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_MORE, [q](SimpleEvent&) { q->increase_instances(); }); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_FEWER, [q](SimpleEvent&) { q->decrease_instances(); }); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_SPLIT_OBJECTS, &priv::on_action_split_objects, this); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER // Preview events: preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); -#if ENABLE_REMOVE_TABS_FROM_PLATER + preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); + view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); }); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this); q->Bind(EVT_PROCESS_COMPLETED, &priv::on_process_completed, this); -#if ENABLE_REMOVE_TABS_FROM_PLATER q->Bind(EVT_GLVIEWTOOLBAR_3D, [q](SimpleEvent&) { q->select_view_3D("3D"); }); q->Bind(EVT_GLVIEWTOOLBAR_PREVIEW, [q](SimpleEvent&) { q->select_view_3D("Preview"); }); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER // Drop target: q->SetDropTarget(new PlaterDropTarget(q)); // if my understanding is right, wxWindow takes the owenership update_ui_from_settings(); q->Layout(); + + set_current_panel(view3D); } void Plater::priv::update(bool force_full_scene_refresh) @@ -1261,23 +1164,19 @@ void Plater::priv::update(bool force_full_scene_refresh) model.center_instances_around_point(bed_center); } - if (this->printer_technology == ptSLA) { + unsigned int update_status = 0; + if (this->printer_technology == ptSLA) // Update the SLAPrint from the current Model, so that the reload_scene() // pulls the correct data. - this->update_background_process(); - } -#if ENABLE_REMOVE_TABS_FROM_PLATER - view3D->reload_scene(false, force_full_scene_refresh); -#else - this->canvas3D->reload_scene(false, force_full_scene_refresh); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER - preview->reset_gcode_preview_data(); - preview->reload_print(); - - this->schedule_background_process(); + update_status = this->update_background_process(); + this->view3D->reload_scene(false, force_full_scene_refresh); + this->preview->reload_print(); + if (this->printer_technology == ptSLA) + this->restart_background_process(update_status); + else + this->schedule_background_process(); } -#if ENABLE_REMOVE_TABS_FROM_PLATER void Plater::priv::select_view(const std::string& direction) { if (current_panel == view3D) @@ -1292,25 +1191,15 @@ void Plater::priv::select_view_3D(const std::string& name) set_current_panel(view3D); else if (name == "Preview") set_current_panel(preview); +} -#if !ENABLE_TOOLBAR_BACKGROUND_TEXTURE - view_toolbar.set_selection(name); -#endif // !ENABLE_TOOLBAR_BACKGROUND_TEXTURE -} -#else -void Plater::priv::select_view(const std::string& direction) +void Plater::priv::select_next_view_3D() { - int page_id = notebook->GetSelection(); - if (page_id != wxNOT_FOUND) - { - const wxString& page_text = notebook->GetPageText(page_id); - if (page_text == _(L("3D"))) - this->canvas3D->select_view(direction); - else if (page_text == _(L("Preview"))) - preview->select_view(direction); - } + if (current_panel == view3D) + set_current_panel(preview); + else if (current_panel == preview) + set_current_panel(view3D); } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER // Called after the Preferences dialog is closed and the program settings are saved. // Update the UI based on the current preferences. @@ -1373,11 +1262,12 @@ std::vector Plater::priv::load_files(const std::vector& input_ for (size_t i = 0; i < input_files.size(); i++) { const auto &path = input_files[i]; const auto filename = path.filename(); - const auto dlg_info = wxString::Format(_(L("Processing input file %s\n")), filename.string()); + const auto dlg_info = wxString::Format(_(L("Processing input file %s\n")), from_path(filename)); dlg.Update(100 * i / input_files.size(), dlg_info); const bool type_3mf = std::regex_match(path.string(), pattern_3mf); const bool type_zip_amf = !type_3mf && std::regex_match(path.string(), pattern_zip_amf); + const bool type_any_amf = !type_3mf && std::regex_match(path.string(), pattern_any_amf); Slic3r::Model model; try { @@ -1433,7 +1323,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ } } - if (type_3mf) { + if (type_3mf || type_any_amf) { for (ModelObject* model_object : model.objects) { model_object->center_around_origin(); model_object->ensure_on_bed(); @@ -1447,7 +1337,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ if ( obj->volumes.size()>1 ) { Slic3r::GUI::show_error(nullptr, wxString::Format(_(L("You can't to add the object(s) from %s because of one or some of them is(are) multi-part")), - filename.string())); + from_path(filename))); return std::vector(); } } @@ -1485,16 +1375,24 @@ std::vector Plater::priv::load_files(const std::vector& input_ statusbar()->set_status_text(_(L("Loaded"))); } + // automatic selection of added objects + if (!obj_idxs.empty() && (view3D != nullptr)) + { + GLCanvas3D::Selection& selection = view3D->get_canvas3d()->get_selection(); + selection.clear(); + for (size_t idx : obj_idxs) + { + selection.add_object((unsigned int)idx, false); + } + } + return obj_idxs; } std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects) { const BoundingBoxf bed_shape = bed_shape_bb(); -#if !ENABLE_MODELVOLUME_TRANSFORM - const Vec3d bed_center = Slic3r::to_3d(bed_shape.center().cast(), 0.0); -#endif // !ENABLE_MODELVOLUME_TRANSFORM - const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast(), 1.0); + const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast(), 1.0) - 2.0 * Vec3d::Ones(); bool need_arrange = false; bool scaled_down = false; @@ -1513,11 +1411,7 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode // add a default instance and center object around origin object->center_around_origin(); // also aligns object to Z = 0 ModelInstance* instance = object->add_instance(); -#if ENABLE_MODELVOLUME_TRANSFORM instance->set_offset(Slic3r::to_3d(bed_shape.center().cast(), -object->origin_translation(2))); -#else - instance->set_offset(bed_center); -#endif // ENABLE_MODELVOLUME_TRANSFORM } const Vec3d size = object->bounding_box().size(); @@ -1526,9 +1420,10 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode if (max_ratio > 10000) { // the size of the object is too big -> this could lead to overflow when moving to clipper coordinates, // so scale down the mesh - // const Vec3d inverse = ratio.cwiseInverse(); - // object->scale(inverse); - object->scale(ratio.cwiseInverse()); + double inv = 1. / max_ratio; + object->scale_mesh(Vec3d(inv, inv, inv)); + object->origin_translation = Vec3d::Zero(); + object->center_around_origin(); scaled_down = true; } else if (max_ratio > 5) { const Vec3d inverse = ratio.cwiseInverse(); @@ -1554,9 +1449,6 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode } update(); -#if !ENABLE_MODIFIED_CAMERA_TARGET - this->canvas3D->zoom_to_volumes(); -#endif // !ENABLE_MODIFIED_CAMERA_TARGET object_list_changed(); this->schedule_background_process(); @@ -1595,8 +1487,8 @@ std::unique_ptr Plater::priv::get_export_file(GUI::FileType ((file_type == FT_AMF) || (file_type == FT_3MF)) ? _(L("Export print config")) : "", true, _(L("Save file as:")), - output_file.parent_path().string(), - output_file.filename().string(), + from_path(output_file.parent_path()), + from_path(output_file.filename()), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); @@ -1605,7 +1497,7 @@ std::unique_ptr Plater::priv::get_export_file(GUI::FileType return nullptr; } - fs::path path(dlg->GetPath()); + fs::path path(into_path(dlg->GetPath())); wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); return dlg; @@ -1613,20 +1505,12 @@ std::unique_ptr Plater::priv::get_export_file(GUI::FileType const GLCanvas3D::Selection& Plater::priv::get_selection() const { -#if ENABLE_REMOVE_TABS_FROM_PLATER return view3D->get_canvas3d()->get_selection(); -#else - return canvas3D->get_selection(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } GLCanvas3D::Selection& Plater::priv::get_selection() { -#if ENABLE_REMOVE_TABS_FROM_PLATER return view3D->get_canvas3d()->get_selection(); -#else - return canvas3D->get_selection(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } int Plater::priv::get_selected_object_idx() const @@ -1649,57 +1533,32 @@ int Plater::priv::get_selected_volume_idx() const void Plater::priv::selection_changed() { -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->enable_toolbar_item("delete", can_delete_object()); view3D->enable_toolbar_item("more", can_increase_instances()); view3D->enable_toolbar_item("fewer", can_decrease_instances()); - view3D->enable_toolbar_item("splitobjects", can_split_to_objects()); - view3D->enable_toolbar_item("splitvolumes", can_split_to_volumes()); + view3D->enable_toolbar_item("splitobjects", can_split/*_to_objects*/()); + view3D->enable_toolbar_item("splitvolumes", can_split/*_to_volumes*/()); view3D->enable_toolbar_item("layersediting", layers_height_allowed()); // forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears) view3D->render(); -#else - this->canvas3D->enable_toolbar_item("delete", can_delete_object()); - this->canvas3D->enable_toolbar_item("more", can_increase_instances()); - this->canvas3D->enable_toolbar_item("fewer", can_decrease_instances()); - this->canvas3D->enable_toolbar_item("splitobjects", can_split_to_objects()); - this->canvas3D->enable_toolbar_item("splitvolumes", can_split_to_volumes()); - this->canvas3D->enable_toolbar_item("layersediting", layers_height_allowed()); - // forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears) - this->canvas3D->render(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::priv::object_list_changed() { -#if ENABLE_REMOVE_TABS_FROM_PLATER // Enable/disable buttons depending on whether there are any objects on the platter. view3D->enable_toolbar_item("deleteall", can_delete_all()); view3D->enable_toolbar_item("arrange", can_arrange()); -#else - // Enable/disable buttons depending on whether there are any objects on the platter. - this->canvas3D->enable_toolbar_item("deleteall", can_delete_all()); - this->canvas3D->enable_toolbar_item("arrange", can_arrange()); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER const bool export_in_progress = this->background_process.is_export_scheduled(); // || ! send_gcode_file.empty()); // XXX: is this right? -#if ENABLE_REMOVE_TABS_FROM_PLATER const bool model_fits = view3D->check_volumes_outside_state() == ModelInstance::PVS_Inside; -#else - const bool model_fits = this->canvas3D->check_volumes_outside_state(config) == ModelInstance::PVS_Inside; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER sidebar->enable_buttons(!model.objects.empty() && !export_in_progress && model_fits); } void Plater::priv::select_all() { -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->select_all(); -#else - this->canvas3D->select_all(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER this->sidebar->obj_list()->update_selections(); } @@ -1708,13 +1567,8 @@ void Plater::priv::remove(size_t obj_idx) // Prevent toolpaths preview from rendering while we modify the Print object preview->set_enabled(false); -#if ENABLE_REMOVE_TABS_FROM_PLATER if (view3D->is_layers_editing_enabled()) view3D->enable_layers_editing(false); -#else - if (this->canvas3D->is_layers_editing_enabled()) - this->canvas3D->enable_layers_editing(false); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER model.delete_object(obj_idx); // Delete object from Sidebar list @@ -1739,13 +1593,8 @@ void Plater::priv::reset() // Prevent toolpaths preview from rendering while we modify the Print object preview->set_enabled(false); -#if ENABLE_REMOVE_TABS_FROM_PLATER if (view3D->is_layers_editing_enabled()) view3D->enable_layers_editing(false); -#else - if (this->canvas3D->is_layers_editing_enabled()) - this->canvas3D->enable_layers_editing(false); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER // Stop and reset the Print content. this->background_process.reset(); @@ -1763,11 +1612,7 @@ void Plater::priv::reset() void Plater::priv::mirror(Axis axis) { -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->mirror_selection(axis); -#else - this->canvas3D->mirror_selection(axis); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::priv::arrange() @@ -1779,11 +1624,7 @@ void Plater::priv::arrange() arranging.store(true); // Disable the arrange button (to prevent reentrancies, we will call wxYied) -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->enable_toolbar_item("arrange", can_arrange()); -#else - this->canvas3D->enable_toolbar_item("arrange", can_arrange()); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER this->background_process.stop(); unsigned count = 0; @@ -1853,11 +1694,7 @@ void Plater::priv::arrange() arranging.store(false); // We enable back the arrange button -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->enable_toolbar_item("arrange", can_arrange()); -#else - this->canvas3D->enable_toolbar_item("arrange", can_arrange()); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER // Do a full refresh of scene tree, including regenerating all the GLVolumes. //FIXME The update function shall just reload the modified matrices. @@ -1984,22 +1821,18 @@ unsigned int Plater::priv::update_background_process() // bitmap of enum UpdateBackgroundProcessReturnState unsigned int return_state = 0; - // If the update_background_process() was not called by the timer, kill the timer, so the async_apply_config() - // will not be called again in vain. + // If the update_background_process() was not called by the timer, kill the timer, + // so the update_restart_background_process() will not be called again in vain. this->background_process_timer.Stop(); // Update the "out of print bed" state of ModelInstances. this->update_print_volume_state(); // Apply new config to the possibly running background task. + bool was_running = this->background_process.running(); Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config()); // Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile. -#if ENABLE_REMOVE_TABS_FROM_PLATER if (view3D->is_layers_editing_enabled()) view3D->get_wxglcanvas()->Refresh(); -#else - if (this->canvas3D->is_layers_editing_enabled()) - this->canvas3Dwidget->Refresh(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER if (invalidated == Print::APPLY_STATUS_INVALIDATED) { // Some previously calculated data on the Print was invalidated. @@ -2007,7 +1840,6 @@ unsigned int Plater::priv::update_background_process() this->sidebar->show_sliced_info_sizer(false); // Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared. // Otherwise they will be just refreshed. - this->gcode_preview_data.reset(); switch (this->printer_technology) { case ptFFF: if (this->preview != nullptr) @@ -2024,18 +1856,10 @@ unsigned int Plater::priv::update_background_process() } } - if (this->background_process.empty()) { - if (invalidated != Print::APPLY_STATUS_UNCHANGED) { - // The background processing will not be restarted, because the Print / SLAPrint is empty. - // Simulate a "canceled" callback message. - wxCommandEvent evt; - evt.SetInt(-1); // canceled - this->on_process_completed(evt); - } - } else { + if (! this->background_process.empty()) { std::string err = this->background_process.validate(); if (err.empty()) { - if (invalidated != Print::APPLY_STATUS_UNCHANGED) + if (invalidated != Print::APPLY_STATUS_UNCHANGED && this->background_processing_enabled()) return_state |= UPDATE_BACKGROUND_PROCESS_RESTART; } else { // The print is not valid. @@ -2043,9 +1867,39 @@ unsigned int Plater::priv::update_background_process() return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; } } + + if (invalidated != Print::APPLY_STATUS_UNCHANGED && was_running && ! this->background_process.running() && + (return_state & UPDATE_BACKGROUND_PROCESS_RESTART) == 0) { + // The background processing was killed and it will not be restarted. + wxCommandEvent evt(EVT_PROCESS_COMPLETED); + evt.SetInt(-1); + // Post the "canceled" callback message, so that it will be processed after any possible pending status bar update messages. + wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone()); + } + return return_state; } +// Restart background processing thread based on a bitmask of UpdateBackgroundProcessReturnState. +bool Plater::priv::restart_background_process(unsigned int state) +{ + if ( ! this->background_process.empty() && + (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 && + ( ((state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART) != 0 && ! this->background_process.finished()) || + (state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) != 0 || + (state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ) ) { + // The print is valid and it can be started. + if (this->background_process.start()) { + this->statusbar()->set_cancel_callback([this]() { + this->statusbar()->set_status_text(L("Cancelling")); + this->background_process.stop(); + }); + return true; + } + } + return false; +} + void Plater::priv::export_gcode(fs::path output_path, PrintHostJob upload_job) { wxCHECK_RET(!(output_path.empty() && upload_job.empty()), "export_gcode: output_path and upload_job empty"); @@ -2061,11 +1915,8 @@ void Plater::priv::export_gcode(fs::path output_path, PrintHostJob upload_job) // bitmask of UpdateBackgroundProcessReturnState unsigned int state = update_background_process(); if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->reload_scene(false); -#else - canvas3D->reload_scene(false); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER + if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0) return; @@ -2075,34 +1926,19 @@ void Plater::priv::export_gcode(fs::path output_path, PrintHostJob upload_job) background_process.schedule_upload(std::move(upload_job)); } - if (! background_process.running()) { - // The print is valid and it should be started. - if (background_process.start()) - statusbar()->set_cancel_callback([this]() { - statusbar()->set_status_text(L("Cancelling")); - background_process.stop(); - }); - } + this->restart_background_process(priv::UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT); } -void Plater::priv::async_apply_config() +void Plater::priv::update_restart_background_process(bool force_update_scene, bool force_update_preview) { // bitmask of UpdateBackgroundProcessReturnState unsigned int state = this->update_background_process(); - if (state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) -#if ENABLE_REMOVE_TABS_FROM_PLATER + if (force_update_scene || (state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0) view3D->reload_scene(false); -#else - this->canvas3D->reload_scene(false); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER - if ((state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 && this->background_processing_enabled()) { - // The print is valid and it can be started. - if (this->background_process.start()) - this->statusbar()->set_cancel_callback([this]() { - this->statusbar()->set_status_text(L("Cancelling")); - this->background_process.stop(); - }); - } + + if (force_update_preview) + this->preview->reload_print(); + this->restart_background_process(state); } void Plater::priv::update_fff_scene() @@ -2110,26 +1946,15 @@ void Plater::priv::update_fff_scene() if (this->preview != nullptr) this->preview->reload_print(); // In case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth: -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->reload_scene(true); -#else - this->canvas3D->reload_scene(true); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::priv::update_sla_scene() { // Update the SLAPrint from the current Model, so that the reload_scene() // pulls the correct data. - if (this->update_background_process() & UPDATE_BACKGROUND_PROCESS_RESTART) - this->schedule_background_process(); -#if ENABLE_REMOVE_TABS_FROM_PLATER - view3D->reload_scene(true); -#else - this->canvas3D->reload_scene(true); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER delayed_scene_refresh = false; - this->preview->reload_print(); + this->update_restart_background_process(true, true); } void Plater::priv::reload_from_disk() @@ -2194,7 +2019,6 @@ void Plater::priv::fix_through_netfabb(const int obj_idx) remove(obj_idx); } -#if ENABLE_REMOVE_TABS_FROM_PLATER void Plater::priv::set_current_panel(wxPanel* panel) { if (std::find(panels.begin(), panels.end(), panel) == panels.end()) @@ -2228,50 +2052,24 @@ void Plater::priv::set_current_panel(wxPanel* panel) { // Update the SLAPrint from the current Model, so that the reload_scene() // pulls the correct data. - if (this->update_background_process() & UPDATE_BACKGROUND_PROCESS_RESTART) - this->schedule_background_process(); - } - view3D->reload_scene(true); + this->update_restart_background_process(true, false); + } else + view3D->reload_scene(true); } // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) view3D->set_as_dirty(); + view_toolbar.select_item("3D"); } else if (current_panel == preview) { this->q->reslice(); preview->reload_print(); preview->set_canvas_as_dirty(); + view_toolbar.select_item("Preview"); } -} -#else -void Plater::priv::on_notebook_changed(wxBookCtrlEvent&) -{ - wxCHECK_RET(canvas3D != nullptr, "on_notebook_changed on freed Plater"); - const auto current_id = notebook->GetCurrentPage()->GetId(); -#if ENABLE_IMGUI - if (current_id == canvas3Dwidget->GetId()) { -#else - if (current_id == panel3d->GetId()) { -#endif // ENABLE_IMGUI - if (this->canvas3D->is_reload_delayed()) { - // Delayed loading of the 3D scene. - if (this->printer_technology == ptSLA) { - // Update the SLAPrint from the current Model, so that the reload_scene() - // pulls the correct data. - if (this->update_background_process() & UPDATE_BACKGROUND_PROCESS_RESTART) - this->schedule_background_process(); - } - this->canvas3D->reload_scene(true); - } - // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) - this->canvas3D->set_as_dirty(); - } else if (current_id == preview->GetId()) { - preview->reload_print(); - preview->set_canvas_as_dirty(); - } + current_panel->SetFocusFromKbd(); } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER void Plater::priv::on_select_preset(wxCommandEvent &evt) { @@ -2323,11 +2121,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) this->update_fff_scene(); break; case ptSLA: -#if ENABLE_REMOVE_TABS_FROM_PLATER if (view3D->is_dragging()) -#else - if (this->canvas3D->is_dragging()) -#endif // ENABLE_REMOVE_TABS_FROM_PLATER delayed_scene_refresh = true; else this->update_sla_scene(); @@ -2343,11 +2137,7 @@ void Plater::priv::on_slicing_completed(wxCommandEvent &) this->update_fff_scene(); break; case ptSLA: -#if ENABLE_REMOVE_TABS_FROM_PLATER if (view3D->is_dragging()) -#else - if (this->canvas3D->is_dragging()) -#endif // ENABLE_REMOVE_TABS_FROM_PLATER delayed_scene_refresh = true; else this->update_sla_scene(); @@ -2390,11 +2180,7 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) this->update_fff_scene(); break; case ptSLA: -#if ENABLE_REMOVE_TABS_FROM_PLATER if (view3D->is_dragging()) -#else - if (this->canvas3D->is_dragging()) -#endif // ENABLE_REMOVE_TABS_FROM_PLATER delayed_scene_refresh = true; else this->update_sla_scene(); @@ -2404,22 +2190,12 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) void Plater::priv::on_layer_editing_toggled(bool enable) { -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->enable_layers_editing(enable); if (enable && !view3D->is_layers_editing_enabled()) { // Initialization of the OpenGL shaders failed. Disable the tool. view3D->enable_toolbar_item("layersediting", false); } view3D->set_as_dirty(); -#else - this->canvas3D->enable_layers_editing(enable); - if (enable && !this->canvas3D->is_layers_editing_enabled()) { - // Initialization of the OpenGL shaders failed. Disable the tool. - this->canvas3D->enable_toolbar_item("layersediting", false); - } - canvas3Dwidget->Refresh(); - canvas3Dwidget->Update(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::priv::on_action_add(SimpleEvent&) @@ -2440,39 +2216,25 @@ void Plater::priv::on_action_split_volumes(SimpleEvent&) void Plater::priv::on_action_layersediting(SimpleEvent&) { -#if ENABLE_REMOVE_TABS_FROM_PLATER bool enable = !view3D->is_layers_editing_enabled(); view3D->enable_layers_editing(enable); if (enable && !view3D->is_layers_editing_enabled()) view3D->enable_toolbar_item("layersediting", false); -#else - bool enable = !this->canvas3D->is_layers_editing_enabled(); - this->canvas3D->enable_layers_editing(enable); - if (enable && !this->canvas3D->is_layers_editing_enabled()) - this->canvas3D->enable_toolbar_item("layersediting", false); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::priv::on_object_select(SimpleEvent& evt) { - selection_changed(); wxGetApp().obj_list()->update_selections(); + selection_changed(); } void Plater::priv::on_viewport_changed(SimpleEvent& evt) { wxObject* o = evt.GetEventObject(); -#if ENABLE_REMOVE_TABS_FROM_PLATER if (o == preview->get_wxglcanvas()) preview->set_viewport_into_scene(view3D->get_canvas3d()); else if (o == view3D->get_wxglcanvas()) preview->set_viewport_from_scene(view3D->get_canvas3d()); -#else - if (o == preview->get_wxglcanvas()) - preview->set_viewport_into_scene(canvas3D); - else if (o == canvas3Dwidget) - preview->set_viewport_from_scene(canvas3D); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::priv::on_right_click(Vec2dEvent& evt) @@ -2606,9 +2368,9 @@ bool Plater::priv::complit_init_object_menu() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects() || can_split_to_volumes()); }, item_split->GetId()); - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects()); }, item_split_objects->GetId()); - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_volumes()); }, item_split_volumes->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects() || can_split_to_volumes*/()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split_objects->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split_volumes->GetId()); } return true; } @@ -2627,7 +2389,7 @@ bool Plater::priv::complit_init_sla_object_menu() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split->GetId()); } return true; @@ -2646,16 +2408,14 @@ bool Plater::priv::complit_init_part_menu() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_volumes()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split->GetId()); } return true; } -#if ENABLE_REMOVE_TABS_FROM_PLATER void Plater::priv::init_view_toolbar() { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE ItemsIconsTexture::Metadata icons_data; icons_data.filename = "view_toolbar.png"; icons_data.icon_size = 64; @@ -2670,12 +2430,8 @@ void Plater::priv::init_view_toolbar() background_data.bottom = 16; if (!view_toolbar.init(icons_data, background_data)) -#else - if (!view_toolbar.init("view_toolbar.png", 64, 0, 0)) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE return; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE view_toolbar.set_layout_orientation(GLToolbar::Layout::Bottom); view_toolbar.set_border(5.0f); view_toolbar.set_gap_size(1.0f); @@ -2706,30 +2462,7 @@ void Plater::priv::init_view_toolbar() view3D->set_view_toolbar(&view_toolbar); preview->set_view_toolbar(&view_toolbar); -#else - GLRadioToolbarItem::Data item; - - item.name = "3D"; - item.tooltip = GUI::L_str("3D editor view"); - item.sprite_id = 0; - item.action_event = EVT_GLVIEWTOOLBAR_3D; - if (!view_toolbar.add_item(item)) - return; - - item.name = "Preview"; - item.tooltip = GUI::L_str("Preview"); - item.sprite_id = 1; - item.action_event = EVT_GLVIEWTOOLBAR_PREVIEW; - if (!view_toolbar.add_item(item)) - return; - - view3D->set_view_toolbar(&view_toolbar); - preview->set_view_toolbar(&view_toolbar); - - view_toolbar.set_selection("3D"); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER bool Plater::priv::can_delete_object() const { @@ -2764,14 +2497,17 @@ bool Plater::priv::can_split_to_volumes() const return sidebar->obj_list()->is_splittable(); } +bool Plater::priv::can_split() const +{ + if (printer_technology == ptSLA) + return false; + return sidebar->obj_list()->is_splittable(); +} + bool Plater::priv::layers_height_allowed() const { int obj_idx = get_selected_object_idx(); -#if ENABLE_REMOVE_TABS_FROM_PLATER return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed(); -#else - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && config->opt_bool("variable_layer_height") && this->canvas3D->is_layers_editing_allowed(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } bool Plater::priv::can_delete_all() const @@ -2804,10 +2540,6 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame) Plater::~Plater() { -#if !ENABLE_REMOVE_TABS_FROM_PLATER - _3DScene::remove_canvas(p->canvas3Dwidget); - p->canvas3D = nullptr; -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER } Sidebar& Plater::sidebar() { return *p->sidebar; } @@ -2829,7 +2561,7 @@ void Plater::load_project() p->project_filename = input_file; std::vector input_paths; - input_paths.push_back(input_file.wx_str()); + input_paths.push_back(into_path(input_file)); load_files(input_paths); } @@ -2842,7 +2574,7 @@ void Plater::add_model() std::vector input_paths; for (const auto &file : input_files) { - input_paths.push_back(file.wx_str()); + input_paths.push_back(into_path(file)); } load_files(input_paths, true, false); } @@ -2856,7 +2588,7 @@ void Plater::extract_config_from_project() return; std::vector input_paths; - input_paths.push_back(input_file.wx_str()); + input_paths.push_back(into_path(input_file)); load_files(input_paths, false, true); } @@ -2868,9 +2600,7 @@ void Plater::update_ui_from_settings() { p->update_ui_from_settings(); } void Plater::select_view(const std::string& direction) { p->select_view(direction); } -#if ENABLE_REMOVE_TABS_FROM_PLATER void Plater::select_view_3D(const std::string& name) { p->select_view_3D(name); } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER void Plater::select_all() { p->select_all(); } @@ -2881,11 +2611,7 @@ void Plater::delete_object_from_model(size_t obj_idx) { p->delete_object_from_mo void Plater::remove_selected() { -#if ENABLE_REMOVE_TABS_FROM_PLATER this->p->view3D->delete_selected(); -#else - this->p->canvas3D->delete_selected(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::increase_instances(size_t num) @@ -3007,15 +2733,15 @@ void Plater::export_gcode(fs::path output_path) wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save Zip file as:")), start_dir, - default_output_file.filename().string(), + from_path(default_output_file.filename()), GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_PNGZIP, default_output_file.extension().string()), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); if (dlg.ShowModal() == wxID_OK) { - fs::path path(dlg.GetPath()); + fs::path path = into_path(dlg.GetPath()); wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); - output_path = path; + output_path = std::move(path); } } else { try { @@ -3039,8 +2765,8 @@ void Plater::export_stl(bool selection_only) if (! dialog) { return; } // Store a binary STL - wxString path = dialog->GetPath(); - auto path_cstr = path.c_str(); + const wxString path = dialog->GetPath(); + const std::string path_u8 = into_u8(path); TriangleMesh mesh; if (selection_only) { @@ -3051,10 +2777,10 @@ void Plater::export_stl(bool selection_only) if (obj_idx == -1) { return; } mesh = p->model.objects[obj_idx]->mesh(); } else { - auto mesh = p->model.mesh(); + mesh = p->model.mesh(); } - Slic3r::store_stl(path_cstr, &mesh, true); + Slic3r::store_stl(path_u8.c_str(), &mesh, true); p->statusbar()->set_status_text(wxString::Format(_(L("STL file exported to %s")), path)); } @@ -3065,11 +2791,11 @@ void Plater::export_amf() auto dialog = p->get_export_file(FT_AMF); if (! dialog) { return; } - wxString path = dialog->GetPath(); - auto path_cstr = path.c_str(); + const wxString path = dialog->GetPath(); + const std::string path_u8 = into_u8(path); DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); - if (Slic3r::store_amf(path_cstr, &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { + if (Slic3r::store_amf(path_u8.c_str(), &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { // Success p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path)); } else { @@ -3092,13 +2818,14 @@ void Plater::export_3mf(const boost::filesystem::path& output_path) export_config = dialog->get_checkbox_value(); } else - path = output_path.string(); + path = from_path(output_path); if (!path.Lower().EndsWith(".3mf")) return; DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); - if (Slic3r::store_3mf(path.c_str(), &p->model, export_config ? &cfg : nullptr)) { + const std::string path_u8 = into_u8(path); + if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) { // Success p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path)); } else { @@ -3113,19 +2840,9 @@ void Plater::reslice() // bitmask of UpdateBackgroundProcessReturnState unsigned int state = this->p->update_background_process(); if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) -#if ENABLE_REMOVE_TABS_FROM_PLATER this->p->view3D->reload_scene(false); -#else - this->p->canvas3D->reload_scene(false); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER - if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 && !this->p->background_process.running() && !this->p->background_process.finished()) { - // The print is valid and it can be started. - if (this->p->background_process.start()) - this->p->statusbar()->set_cancel_callback([this]() { - this->p->statusbar()->set_status_text(L("Cancelling")); - this->p->background_process.stop(); - }); - } + // Only restarts if the state is valid. + this->p->restart_background_process(state | priv::UPDATE_BACKGROUND_PROCESS_FORCE_RESTART); } void Plater::send_gcode() @@ -3145,7 +2862,7 @@ void Plater::send_gcode() } default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string())); - Slic3r::PrintHostSendDialog dlg(default_output_file); + PrintHostSendDialog dlg(default_output_file); if (dlg.ShowModal() == wxID_OK) { upload_job.upload_data.upload_path = dlg.filename(); upload_job.upload_data.start_print = dlg.start_print(); @@ -3188,11 +2905,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) if (opt_key == "printer_technology") this->set_printer_technology(config.opt_enum(opt_key)); else if (opt_key == "bed_shape") { -#if ENABLE_REMOVE_TABS_FROM_PLATER if (p->view3D) p->view3D->set_bed_shape(p->config->option(opt_key)->values); -#else - this->p->canvas3D->set_bed_shape(p->config->option(opt_key)->values); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER if (p->preview) p->preview->set_bed_shape(p->config->option(opt_key)->values); update_scheduled = true; } @@ -3203,24 +2916,12 @@ void Plater::on_config_change(const DynamicPrintConfig &config) } else if(opt_key == "variable_layer_height") { if (p->config->opt_bool("variable_layer_height") != true) { -#if ENABLE_REMOVE_TABS_FROM_PLATER p->view3D->enable_toolbar_item("layersediting", false); p->view3D->enable_layers_editing(false); p->view3D->set_as_dirty(); -#else - p->canvas3D->enable_toolbar_item("layersediting", false); - p->canvas3D->enable_layers_editing(0); - p->canvas3Dwidget->Refresh(); - p->canvas3Dwidget->Update(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } -#if ENABLE_REMOVE_TABS_FROM_PLATER else if (p->view3D->is_layers_editing_allowed()) { p->view3D->enable_toolbar_item("layersediting", true); -#else - else if (p->canvas3D->is_layers_editing_allowed()) { - p->canvas3D->enable_toolbar_item("layersediting", true); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } } else if(opt_key == "extruder_colour") { @@ -3230,13 +2931,11 @@ void Plater::on_config_change(const DynamicPrintConfig &config) update_scheduled = true; } else if(opt_key == "printer_model") { // update to force bed selection(for texturing) -#if ENABLE_REMOVE_TABS_FROM_PLATER if (p->view3D) p->view3D->set_bed_shape(p->config->option("bed_shape")->values); -#else - p->canvas3D->set_bed_shape(p->config->option("bed_shape")->values); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER if (p->preview) p->preview->set_bed_shape(p->config->option("bed_shape")->values); update_scheduled = true; + } else if (opt_key == "host_type" && this->p->printer_technology == ptSLA) { + p->config->option>(opt_key)->value = htSL1; } } @@ -3274,11 +2973,7 @@ bool Plater::is_single_full_object_selection() const GLCanvas3D* Plater::canvas3D() { -#if ENABLE_REMOVE_TABS_FROM_PLATER return p->view3D->get_canvas3d(); -#else - return p->canvas3D; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } PrinterTechnology Plater::printer_technology() const @@ -3308,29 +3003,17 @@ void Plater::changed_object(int obj_idx) if (list->is_parts_changed()) { // recenter and re - align to Z = 0 auto model_object = p->model.objects[obj_idx]; -#if !ENABLE_MODELVOLUME_TRANSFORM - model_object->center_around_origin(); -#endif // !ENABLE_MODELVOLUME_TRANSFORM model_object->ensure_on_bed(); if (this->p->printer_technology == ptSLA) { // Update the SLAPrint from the current Model, so that the reload_scene() - // pulls the correct data. - this->p->update_background_process(); - } -#if ENABLE_REMOVE_TABS_FROM_PLATER - p->view3D->reload_scene(false); -#else - p->canvas3D->reload_scene(false); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER + // pulls the correct data, update the 3D scene. + this->p->update_restart_background_process(true, false); + } else + p->view3D->reload_scene(false); } // update print this->p->schedule_background_process(); - if (list->is_parts_changed() || list->is_part_settings_changed()) { -#if !ENABLE_MODIFIED_CAMERA_TARGET - p->canvas3D->zoom_to_volumes(); -#endif // !ENABLE_MODIFIED_CAMERA_TARGET - } } void Plater::fix_through_netfabb(const int obj_idx) { p->fix_through_netfabb(obj_idx); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 334ab740a..f433d655d 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -119,9 +119,8 @@ public: void update(); void select_view(const std::string& direction); -#if ENABLE_REMOVE_TABS_FROM_PLATER void select_view_3D(const std::string& name); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER + // Called after the Preferences dialog is closed and the program settings are saved. // Update the UI based on the current preferences. void update_ui_from_settings(); diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index cee1dee5c..e2abde519 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -133,7 +133,12 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Invalid printer technology field: `%2%`") % id % technology_field; model.technology = ptFFF; } - section.second.get("variants", ""); +#if 0 + // Remove SLA printers from the initial alpha. + if (model.technology == ptSLA) + continue; +#endif + section.second.get("variants", ""); const auto variants_field = section.second.get("variants", ""); std::vector variants; if (Slic3r::unescape_strings_cstyle(variants_field, variants)) { @@ -469,6 +474,7 @@ const std::vector& Preset::sla_printer_options() "display_width", "display_height", "display_pixels_x", "display_pixels_y", "display_orientation", "printer_correction", + "print_host", "printhost_apikey", "printhost_cafile", "printer_notes", "inherits" }; diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index a5de7c3c6..c2a94181e 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -1,20 +1,29 @@ #include "PrintHostDialogs.hpp" +#include + #include -#include #include #include #include #include #include +#include +#include +#include +#include -#include "slic3r/GUI/GUI.hpp" -#include "slic3r/GUI/MsgDialog.hpp" -#include "slic3r/GUI/I18N.hpp" +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "MsgDialog.hpp" +#include "I18N.hpp" +#include "../Utils/PrintHost.hpp" namespace fs = boost::filesystem; namespace Slic3r { +namespace GUI { + PrintHostSendDialog::PrintHostSendDialog(const fs::path &path) : MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE) @@ -40,10 +49,185 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path) fs::path PrintHostSendDialog::filename() const { - return fs::path(txt_filename->GetValue().wx_str()); + return into_path(txt_filename->GetValue()); } bool PrintHostSendDialog::start_print() const { - return box_print->GetValue(); } + return box_print->GetValue(); } + + + +wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event); +wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event); +wxDEFINE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event); + +PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id) + : wxEvent(winid, eventType) + , job_id(job_id) +{} + +PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id, int progress) + : wxEvent(winid, eventType) + , job_id(job_id) + , progress(progress) +{} + +PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id, wxString error) + : wxEvent(winid, eventType) + , job_id(job_id) + , error(std::move(error)) +{} + +wxEvent *PrintHostQueueDialog::Event::Clone() const +{ + return new Event(*this); +} + +PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) + : wxDialog(parent, wxID_ANY, _(L("Print host upload queue")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) + , on_progress_evt(this, EVT_PRINTHOST_PROGRESS, &PrintHostQueueDialog::on_progress, this) + , on_error_evt(this, EVT_PRINTHOST_ERROR, &PrintHostQueueDialog::on_error, this) + , on_cancel_evt(this, EVT_PRINTHOST_CANCEL, &PrintHostQueueDialog::on_cancel, this) +{ + enum { HEIGHT = 800, WIDTH = 400, SPACING = 5 }; + + SetMinSize(wxSize(HEIGHT, WIDTH)); + + auto *topsizer = new wxBoxSizer(wxVERTICAL); + + job_list = new wxDataViewListCtrl(this, wxID_ANY); + // Note: Keep these in sync with Column + job_list->AppendTextColumn("ID", wxDATAVIEW_CELL_INERT); + job_list->AppendProgressColumn("Progress", wxDATAVIEW_CELL_INERT); + job_list->AppendTextColumn("Status", wxDATAVIEW_CELL_INERT); + job_list->AppendTextColumn("Host", wxDATAVIEW_CELL_INERT); + job_list->AppendTextColumn("Filename", wxDATAVIEW_CELL_INERT); + job_list->AppendTextColumn("error_message", wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER, wxDATAVIEW_COL_HIDDEN); + + auto *btnsizer = new wxBoxSizer(wxHORIZONTAL); + btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected"))); + btn_cancel->Disable(); + btn_error = new wxButton(this, wxID_ANY, _(L("Show error message"))); + btn_error->Disable(); + auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close"))); + btnsizer->Add(btn_cancel, 0, wxRIGHT, SPACING); + btnsizer->Add(btn_error, 0); + btnsizer->AddStretchSpacer(); + btnsizer->Add(btn_close); + + topsizer->Add(job_list, 1, wxEXPAND | wxBOTTOM, SPACING); + topsizer->Add(btnsizer, 0, wxEXPAND); + SetSizer(topsizer); + + job_list->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent&) { on_list_select(); }); + + btn_cancel->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { + int selected = job_list->GetSelectedRow(); + if (selected == wxNOT_FOUND) { return; } + + const JobState state = get_state(selected); + if (state < ST_ERROR) { + // TODO: cancel + GUI::wxGetApp().printhost_job_queue().cancel(selected); + } + }); + + btn_error->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { + int selected = job_list->GetSelectedRow(); + if (selected == wxNOT_FOUND) { return; } + GUI::show_error(nullptr, job_list->GetTextValue(selected, COL_ERRORMSG)); + }); +} + +void PrintHostQueueDialog::append_job(const PrintHostJob &job) +{ + wxCHECK_RET(!job.empty(), "PrintHostQueueDialog: Attempt to append an empty job"); + + wxVector fields; + fields.push_back(wxVariant(wxString::Format("%d", job_list->GetItemCount() + 1))); + fields.push_back(wxVariant(0)); + fields.push_back(wxVariant(_(L("Enqueued")))); + fields.push_back(wxVariant(job.printhost->get_host())); + fields.push_back(wxVariant(job.upload_data.upload_path.string())); + fields.push_back(wxVariant("")); + job_list->AppendItem(fields, static_cast(ST_NEW)); +} + +PrintHostQueueDialog::JobState PrintHostQueueDialog::get_state(int idx) +{ + wxCHECK_MSG(idx >= 0 && idx < job_list->GetItemCount(), ST_ERROR, "Out of bounds access to job list"); + return static_cast(job_list->GetItemData(job_list->RowToItem(idx))); +} + +void PrintHostQueueDialog::set_state(int idx, JobState state) +{ + wxCHECK_RET(idx >= 0 && idx < job_list->GetItemCount(), "Out of bounds access to job list"); + job_list->SetItemData(job_list->RowToItem(idx), static_cast(state)); + + switch (state) { + case ST_NEW: job_list->SetValue(_(L("Enqueued")), idx, COL_STATUS); break; + case ST_PROGRESS: job_list->SetValue(_(L("Uploading")), idx, COL_STATUS); break; + case ST_ERROR: job_list->SetValue(_(L("Error")), idx, COL_STATUS); break; + case ST_CANCELLING: job_list->SetValue(_(L("Cancelling")), idx, COL_STATUS); break; + case ST_CANCELLED: job_list->SetValue(_(L("Cancelled")), idx, COL_STATUS); break; + case ST_COMPLETED: job_list->SetValue(_(L("Completed")), idx, COL_STATUS); break; + } +} + +void PrintHostQueueDialog::on_list_select() +{ + int selected = job_list->GetSelectedRow(); + if (selected != wxNOT_FOUND) { + const JobState state = get_state(selected); + btn_cancel->Enable(state < ST_ERROR); + btn_error->Enable(state == ST_ERROR); + Layout(); + } else { + btn_cancel->Disable(); + } +} + +void PrintHostQueueDialog::on_progress(Event &evt) +{ + wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list"); + + if (evt.progress < 100) { + set_state(evt.job_id, ST_PROGRESS); + job_list->SetValue(wxVariant(evt.progress), evt.job_id, COL_PROGRESS); + } else { + set_state(evt.job_id, ST_COMPLETED); + job_list->SetValue(wxVariant(100), evt.job_id, COL_PROGRESS); + } + + on_list_select(); +} + +void PrintHostQueueDialog::on_error(Event &evt) +{ + wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list"); + + set_state(evt.job_id, ST_ERROR); + + auto errormsg = wxString::Format("%s\n%s", _(L("Error uploading to print host:")), evt.error); + job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS); + job_list->SetValue(wxVariant(errormsg), evt.job_id, COL_ERRORMSG); // Stashes the error message into a hidden column for later + + on_list_select(); + + GUI::show_error(nullptr, std::move(errormsg)); +} + +void PrintHostQueueDialog::on_cancel(Event &evt) +{ + wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list"); + + set_state(evt.job_id, ST_CANCELLED); + job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS); + + on_list_select(); +} + + +}} diff --git a/src/slic3r/GUI/PrintHostDialogs.hpp b/src/slic3r/GUI/PrintHostDialogs.hpp index d27fbe576..ee3fe26d8 100644 --- a/src/slic3r/GUI/PrintHostDialogs.hpp +++ b/src/slic3r/GUI/PrintHostDialogs.hpp @@ -2,24 +2,29 @@ #define slic3r_PrintHostSendDialog_hpp_ #include - #include #include -#include #include -#include -#include -#include -#include -#include +#include -#include "slic3r/GUI/GUI.hpp" -#include "slic3r/GUI/MsgDialog.hpp" +#include "GUI.hpp" +#include "GUI_Utils.hpp" +#include "MsgDialog.hpp" +#include "../Utils/PrintHost.hpp" + +class wxButton; +class wxTextCtrl; +class wxCheckBox; +class wxDataViewListCtrl; namespace Slic3r { +struct PrintHostJob; + +namespace GUI { + class PrintHostSendDialog : public GUI::MsgDialog { @@ -38,12 +43,64 @@ private: class PrintHostQueueDialog : public wxDialog { public: - PrintHostQueueDialog(); + class Event : public wxEvent + { + public: + size_t job_id; + int progress = 0; // in percent + wxString error; + Event(wxEventType eventType, int winid, size_t job_id); + Event(wxEventType eventType, int winid, size_t job_id, int progress); + Event(wxEventType eventType, int winid, size_t job_id, wxString error); + + virtual wxEvent *Clone() const; + }; + + + PrintHostQueueDialog(wxWindow *parent); + + void append_job(const PrintHostJob &job); private: + enum Column { + COL_ID, + COL_PROGRESS, + COL_STATUS, + COL_HOST, + COL_FILENAME, + COL_ERRORMSG, + }; + + enum JobState { + ST_NEW, + ST_PROGRESS, + ST_ERROR, + ST_CANCELLING, + ST_CANCELLED, + ST_COMPLETED, + }; + + wxButton *btn_cancel; + wxButton *btn_error; + wxDataViewListCtrl *job_list; + // Note: EventGuard prevents delivery of progress evts to a freed PrintHostQueueDialog + EventGuard on_progress_evt; + EventGuard on_error_evt; + EventGuard on_cancel_evt; + + JobState get_state(int idx); + void set_state(int idx, JobState); + void on_list_select(); + void on_progress(Event&); + void on_error(Event&); + void on_cancel(Event&); }; +wxDECLARE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event); +wxDECLARE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event); +wxDECLARE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event); -} + +}} #endif diff --git a/src/slic3r/GUI/SysInfoDialog.cpp b/src/slic3r/GUI/SysInfoDialog.cpp index f7f1cfedb..110bfaf44 100644 --- a/src/slic3r/GUI/SysInfoDialog.cpp +++ b/src/slic3r/GUI/SysInfoDialog.cpp @@ -116,7 +116,7 @@ SysInfoDialog::SysInfoDialog() buttons->Insert(0, btn_copy_to_clipboard, 0, wxLEFT, 5); btn_copy_to_clipboard->Bind(wxEVT_BUTTON, &SysInfoDialog::onCopyToClipboard, this); - this->SetEscapeId(wxID_CLOSE); + this->SetEscapeId(wxID_OK); this->Bind(wxEVT_BUTTON, &SysInfoDialog::onCloseDialog, this, wxID_OK); main_sizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index f5792ca4b..0f779984d 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -270,7 +270,7 @@ Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::str auto panel = this; #endif PageShp page(new Page(panel, title, icon_idx)); - page->SetScrollbars(1, 1, 1, 2); + page->SetScrollbars(1, 20, 1, 2); page->Hide(); m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5); @@ -667,8 +667,9 @@ void Tab::reload_config() Thaw(); } -void Tab::update_visibility(ConfigOptionMode mode) +void Tab::update_visibility() { + const ConfigOptionMode mode = wxGetApp().get_opt_mode(); Freeze(); for (auto page : m_pages) @@ -1618,6 +1619,122 @@ bool Tab::current_preset_is_dirty() return m_presets->current_is_dirty(); } +void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) +{ + const bool sla = m_presets->get_selected_preset().printer_technology() == ptSLA; + + // Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment) + if (! sla) { + optgroup->append_single_option_line("host_type"); + } else { + m_config->option>("host_type", true)->value = htSL1; + } + + auto printhost_browse = [this, optgroup] (wxWindow* parent) { + + // TODO: SLA Bonjour + + auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); + btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(btn); + + btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) { + BonjourDialog dialog(parent); + if (dialog.show_and_lookup()) { + optgroup->set_value("print_host", std::move(dialog.get_selected()), true); + // FIXME: emit killfocus on the edit widget + } + }); + + return sizer; + }; + + auto print_host_test = [this](wxWindow* parent) { + auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")), + wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); + btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG)); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(btn); + + btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) { + std::unique_ptr host(PrintHost::get_print_host(m_config)); + if (! host) { + const auto text = wxString::Format("%s", + _(L("Could not get a valid Printer Host reference"))); + show_error(this, text); + return; + } + wxString msg; + if (host->test(msg)) { + show_info(this, host->get_test_ok_msg(), _(L("Success!"))); + } else { + show_error(this, host->get_test_failed_msg(msg)); + } + }); + + return sizer; + }; + + Line host_line = optgroup->create_single_option_line("print_host"); + host_line.append_widget(printhost_browse); + host_line.append_widget(print_host_test); + optgroup->append_line(host_line); + optgroup->append_single_option_line("printhost_apikey"); + + const auto ca_file_hint = _(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.")); + + if (Http::ca_file_supported()) { + Line cafile_line = optgroup->create_single_option_line("printhost_cafile"); + + auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) { + auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); + btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(btn); + + btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e) { + static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*")); + wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (openFileDialog.ShowModal() != wxID_CANCEL) { + optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true); + } + }); + + return sizer; + }; + + cafile_line.append_widget(printhost_cafile_browse); + optgroup->append_line(cafile_line); + + Line cafile_hint { "", "" }; + cafile_hint.full_width = 1; + cafile_hint.widget = [this, ca_file_hint](wxWindow* parent) { + auto txt = new wxStaticText(parent, wxID_ANY, ca_file_hint); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(txt); + return sizer; + }; + optgroup->append_line(cafile_hint); + } else { + Line line { "", "" }; + line.full_width = 1; + + line.widget = [this, ca_file_hint] (wxWindow* parent) { + auto txt = new wxStaticText(parent, wxID_ANY, wxString::Format("%s\n\n\t%s", + _(L("HTTPS CA File:\n\ +\tOn this system, Slic3r uses HTTPS certificates from the system Certificate Store or Keychain.\n\ +\tTo use a custom CA file, please import your CA file into Certificate Store / Keychain.")), + ca_file_hint)); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(txt); + return sizer; + }; + + optgroup->append_line(line); + } +} + void TabPrinter::build() { m_presets = &m_preset_bundle->printers; @@ -1686,6 +1803,7 @@ void TabPrinter::build_fff() wxTheApp->CallAfter([this, opt_key, value, extruders_count]() { if (opt_key == "extruders_count" || opt_key == "single_extruder_multi_material") { extruders_count_changed(extruders_count); + init_options_list(); // m_options_list should be updated before UI updating update_dirty(); if (opt_key == "single_extruder_multi_material") // the single_extruder_multimaterial was added to force pages on_value_change(opt_key, value); // rebuild - let's make sure the on_value_change is not skipped @@ -1745,96 +1863,8 @@ void TabPrinter::build_fff() } #endif - optgroup = page->new_optgroup(_(L("Printer Host upload"))); - - optgroup->append_single_option_line("host_type"); - - auto printhost_browse = [this, optgroup] (wxWindow* parent) { - auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) { - BonjourDialog dialog(parent); - if (dialog.show_and_lookup()) { - optgroup->set_value("print_host", std::move(dialog.get_selected()), true); - } - }); - - return sizer; - }; - - auto print_host_test = [this](wxWindow* parent) { - auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")), - wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG)); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) { - std::unique_ptr host(PrintHost::get_print_host(m_config)); - if (! host) { - const auto text = wxString::Format("%s", - _(L("Could not get a valid Printer Host reference"))); - show_error(this, text); - return; - } - wxString msg; - if (host->test(msg)) { - show_info(this, host->get_test_ok_msg(), _(L("Success!"))); - } else { - show_error(this, host->get_test_failed_msg(msg)); - } - }); - - return sizer; - }; - - Line host_line = optgroup->create_single_option_line("print_host"); - host_line.append_widget(printhost_browse); - host_line.append_widget(print_host_test); - optgroup->append_line(host_line); - optgroup->append_single_option_line("printhost_apikey"); - - if (Http::ca_file_supported()) { - - Line cafile_line = optgroup->create_single_option_line("printhost_cafile"); - - auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) { - auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e) { - static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*")); - wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (openFileDialog.ShowModal() != wxID_CANCEL) { - optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true); - } - }); - - return sizer; - }; - - cafile_line.append_widget(printhost_cafile_browse); - optgroup->append_line(cafile_line); - - auto printhost_cafile_hint = [this, optgroup] (wxWindow* parent) { - auto txt = new wxStaticText(parent, wxID_ANY, - _(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate."))); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(txt); - return sizer; - }; - - Line cafile_hint { "", "" }; - cafile_hint.full_width = 1; - cafile_hint.widget = std::move(printhost_cafile_hint); - optgroup->append_line(cafile_hint); - - } + optgroup = page->new_optgroup(_(L("Print Host upload"))); + build_printhost(optgroup.get()); optgroup = page->new_optgroup(_(L("Firmware"))); optgroup->append_single_option_line("gcode_flavor"); @@ -1977,6 +2007,9 @@ void TabPrinter::build_sla() } optgroup->append_line(line); + optgroup = page->new_optgroup(_(L("Print Host upload"))); + build_printhost(optgroup.get()); + page = add_options_page(_(L("Notes")), "note.png"); optgroup = page->new_optgroup(_(L("Notes")), 0); option = optgroup->get_option("printer_notes"); @@ -2037,6 +2070,7 @@ PageShp TabPrinter::build_kinematics_page() def.type = coString; def.width = 150; def.gui_type = "legend"; + def.mode = comAdvanced; def.tooltip = L("Values in this column are for Full Power mode"); def.default_value = new ConfigOptionString{ L("Full Power") }; @@ -2367,7 +2401,13 @@ void Tab::load_current_preset() if (tab->type() == Preset::TYPE_PRINTER) // Printer tab is shown every time continue; if (tab->supports_printer_technology(printer_technology)) + { wxGetApp().tab_panel()->InsertPage(wxGetApp().tab_panel()->FindPage(this), tab, tab->title()); + #ifdef __linux__ // the tabs apparently need to be explicitly shown on Linux (pull request #1563) + int page_id = wxGetApp().tab_panel()->FindPage(tab); + wxGetApp().tab_panel()->GetPage(page_id)->Show(true); + #endif // __linux__ + } else { int page_id = wxGetApp().tab_panel()->FindPage(tab); wxGetApp().tab_panel()->GetPage(page_id)->Show(false); @@ -2392,6 +2432,7 @@ void Tab::load_current_preset() m_opt_status_value = (m_presets->get_selected_preset_parent() ? osSystemValue : 0) | osInitValue; init_options_list(); + update_visibility(); update_changed_ui(); }); } @@ -2400,6 +2441,7 @@ void Tab::load_current_preset() void Tab::rebuild_page_tree(bool tree_sel_change_event /*= false*/) { Freeze(); + // get label of the currently selected item const auto sel_item = m_treectrl->GetSelection(); const auto selected = sel_item ? m_treectrl->GetItemText(sel_item) : ""; @@ -2412,8 +2454,8 @@ void Tab::rebuild_page_tree(bool tree_sel_change_event /*= false*/) auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID()); m_treectrl->SetItemTextColour(itemId, p->get_item_colour()); if (p->title() == selected) { - if (!(p->title() == _(L("Machine limits")) || p->title() == _(L("Single extruder MM setup")))) // These Pages have to be updated inside OnTreeSelChange - m_disable_tree_sel_changed_event = !tree_sel_change_event; +// if (!(p->title() == _(L("Machine limits")) || p->title() == _(L("Single extruder MM setup")))) // These Pages have to be updated inside OnTreeSelChange +// m_disable_tree_sel_changed_event = !tree_sel_change_event; m_treectrl->SelectItem(itemId); m_disable_tree_sel_changed_event = false; have_selection = 1; @@ -2973,14 +3015,15 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la auto extra_column = [](wxWindow* parent, const Line& line) { std::string bmp_name; - if (line.get_options().size() == 0) - bmp_name = "error.png"; + const std::vector