Merge remote-tracking branch 'remotes/prusa/master' into masterPE

This commit is contained in:
supermerill 2019-01-03 14:47:34 +01:00
commit 72d828cf74
119 changed files with 6602 additions and 3679 deletions

View File

@ -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/)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -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

View File

@ -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
exposure_time = 10
initial_exposure_time = 15
material_correction_printing = 1, 1, 1
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:Material 1]
inherits = *common*
[sla_material:*common 0.025*]
inherits = *common 0.05*
compatible_prints_condition = layer_height == 0.025
exposure_time = 10
initial_exposure_time = 35
[sla_material:Material 2]
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:*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"

View File

@ -0,0 +1,53 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Gael Guennebaud <g.gael@free.fr>
//
// 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 <vector>
#include <map>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <fstream>
#include <sstream>
#ifdef EIGEN_GOOGLEHASH_SUPPORT
#include <google/dense_hash_map>
#endif
/**
* \defgroup SparseExtra_Module SparseExtra module
*
* This module contains some experimental features extending the sparse module.
*
* \code
* #include <Eigen/SparseExtra>
* \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 <dirent.h>
#include "src/SparseExtra/MatrixMarketIterator.h"
#endif
#include "../../Eigen/src/Core/util/ReenableStupidWarnings.h"
#endif // EIGEN_SPARSE_EXTRA_MODULE_H

View File

@ -0,0 +1,122 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// 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<typename _Scalar, int _Options, typename _Index, int Size>
class SparseInnerVectorSet<DynamicSparseMatrix<_Scalar, _Options, _Index>, Size>
: public SparseMatrixBase<SparseInnerVectorSet<DynamicSparseMatrix<_Scalar, _Options, _Index>, Size> >
{
typedef DynamicSparseMatrix<_Scalar, _Options, _Index> MatrixType;
public:
enum { IsRowMajor = internal::traits<SparseInnerVectorSet>::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<matrix.outerSize()) );
}
template<typename OtherDerived>
inline SparseInnerVectorSet& operator=(const SparseMatrixBase<OtherDerived>& other)
{
if (IsRowMajor != ((OtherDerived::Flags&RowMajorBit)==RowMajorBit))
{
// need to transpose => perform a block evaluation followed by a big swap
DynamicSparseMatrix<Scalar,IsRowMajor?RowMajorBit:0> aux(other);
*this = aux.markAsRValue();
}
else
{
// evaluate/copy vector per vector
for (Index j=0; j<m_outerSize.value(); ++j)
{
SparseVector<Scalar,IsRowMajor ? RowMajorBit : 0> 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=<SparseInnerVectorSet>(other);
}
Index nonZeros() const
{
Index count = 0;
for (Index j=0; j<m_outerSize.value(); ++j)
count += m_matrix._data()[m_outerStart+j].size();
return count;
}
const Scalar& lastCoeff() const
{
EIGEN_STATIC_ASSERT_VECTOR_ONLY(SparseInnerVectorSet);
eigen_assert(m_matrix.data()[m_outerStart].size()>0);
return m_matrix.data()[m_outerStart].vale(m_matrix.data()[m_outerStart].size()-1);
}
// template<typename Sparse>
// inline SparseInnerVectorSet& operator=(const SparseMatrixBase<OtherDerived>& 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<Index, Size> m_outerSize;
};
#endif
} // end namespace Eigen
#endif // EIGEN_SPARSE_BLOCKFORDYNAMICMATRIX_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,392 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// 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<typename _Scalar, int _Options, typename _StorageIndex>
struct traits<DynamicSparseMatrix<_Scalar, _Options, _StorageIndex> >
{
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<Scalar>::ReadCost,
SupportedAccessPatterns = OuterRandomAccessPattern
};
};
}
template<typename _Scalar, int _Options, typename _StorageIndex>
class DynamicSparseMatrix
: public SparseMatrixBase<DynamicSparseMatrix<_Scalar, _Options, _StorageIndex> >
{
typedef SparseMatrixBase<DynamicSparseMatrix> 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<Scalar,Flags> Map;
using Base::IsRowMajor;
using Base::operator=;
enum {
Options = _Options
};
protected:
typedef DynamicSparseMatrix<Scalar,(Flags&~RowMajorBit)|(IsRowMajor?RowMajorBit:0), StorageIndex> TransposedSparseMatrix;
Index m_innerSize;
std::vector<internal::CompressedStorage<Scalar,StorageIndex> > 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<internal::CompressedStorage<Scalar,StorageIndex> >& _data() { return m_data; }
const std::vector<internal::CompressedStorage<Scalar,StorageIndex> >& _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; j<outerSize(); ++j)
m_data[j].clear();
}
/** \returns the number of non zero coefficients */
Index nonZeros() const
{
Index res = 0;
for (Index j=0; j<outerSize(); ++j)
res += m_data[j].size();
return res;
}
void reserve(Index reserveSize = 1000)
{
if (outerSize()>0)
{
Index reserveSizePerVector = (std::max)(reserveSize/outerSize(),Index(4));
for (Index j=0; j<outerSize(); ++j)
{
m_data[j].reserve(reserveSizePerVector);
}
}
}
/** Does nothing: provided for compatibility with SparseMatrix */
inline void startVec(Index /*outer*/) {}
/** \returns a reference to the non zero coefficient at position \a row, \a col assuming that:
* - the nonzero does not already exist
* - the new coefficient is the last one of the given inner vector.
*
* \sa insert, insertBackByOuterInner */
inline Scalar& insertBack(Index row, Index col)
{
return insertBackByOuterInner(IsRowMajor?row:col, IsRowMajor?col:row);
}
/** \sa insertBack */
inline Scalar& insertBackByOuterInner(Index outer, Index inner)
{
eigen_assert(outer<Index(m_data.size()) && inner<m_innerSize && "out of range");
eigen_assert(((m_data[outer].size()==0) || (m_data[outer].index(m_data[outer].size()-1)<inner))
&& "wrong sorted insertion");
m_data[outer].append(0, inner);
return m_data[outer].value(m_data[outer].size()-1);
}
inline Scalar& insert(Index row, Index col)
{
const Index outer = IsRowMajor ? row : col;
const Index inner = IsRowMajor ? col : row;
Index startId = 0;
Index id = static_cast<Index>(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<RealScalar>::dummy_precision())
{
for (Index j=0; j<outerSize(); ++j)
m_data[j].prune(reference,epsilon);
}
/** Resize the matrix without preserving the data (the matrix is set to zero)
*/
void resize(Index rows, Index cols)
{
const Index outerSize = IsRowMajor ? rows : cols;
m_innerSize = convert_index(IsRowMajor ? cols : rows);
setZero();
if (Index(m_data.size()) != outerSize)
{
m_data.resize(outerSize);
}
}
void resizeAndKeepData(Index rows, Index cols)
{
const Index outerSize = IsRowMajor ? rows : cols;
const Index innerSize = IsRowMajor ? cols : rows;
if (m_innerSize>innerSize)
{
// 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<typename OtherDerived>
EIGEN_DEPRECATED explicit inline DynamicSparseMatrix(const SparseMatrixBase<OtherDerived>& 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<typename Scalar, int _Options, typename _StorageIndex>
class DynamicSparseMatrix<Scalar,_Options,_StorageIndex>::InnerIterator : public SparseVector<Scalar,_Options,_StorageIndex>::InnerIterator
{
typedef typename SparseVector<Scalar,_Options,_StorageIndex>::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<typename Scalar, int _Options, typename _StorageIndex>
class DynamicSparseMatrix<Scalar,_Options,_StorageIndex>::ReverseInnerIterator : public SparseVector<Scalar,_Options,_StorageIndex>::ReverseInnerIterator
{
typedef typename SparseVector<Scalar,_Options,_StorageIndex>::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<typename _Scalar, int _Options, typename _StorageIndex>
struct evaluator<DynamicSparseMatrix<_Scalar,_Options,_StorageIndex> >
: evaluator_base<DynamicSparseMatrix<_Scalar,_Options,_StorageIndex> >
{
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

View File

@ -0,0 +1,275 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2011 Gael Guennebaud <gael.guennebaud@inria.fr>
// Copyright (C) 2012 Desire NUENTSA WAKAM <desire.nuentsa_wakam@inria.fr>
//
// 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 <iostream>
namespace Eigen {
namespace internal
{
template <typename Scalar>
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<M && j<N)
{
return true;
}
else
return false;
}
template <typename Scalar>
inline bool GetMarketLine (std::stringstream& line, Index& M, Index& N, Index& i, Index& j, std::complex<Scalar>& value)
{
Scalar valR, valI;
line >> i >> j >> valR >> valI;
i--;
j--;
if(i>=0 && j>=0 && i<M && j<N)
{
value = std::complex<Scalar>(valR, valI);
return true;
}
else
return false;
}
template <typename RealScalar>
inline void GetVectorElt (const std::string& line, RealScalar& val)
{
std::istringstream newline(line);
newline >> val;
}
template <typename RealScalar>
inline void GetVectorElt (const std::string& line, std::complex<RealScalar>& val)
{
RealScalar valR, valI;
std::istringstream newline(line);
newline >> valR >> valI;
val = std::complex<RealScalar>(valR, valI);
}
template<typename Scalar>
inline void putMarketHeader(std::string& header,int sym)
{
header= "%%MatrixMarket matrix coordinate ";
if(internal::is_same<Scalar, std::complex<float> >::value || internal::is_same<Scalar, std::complex<double> >::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<typename Scalar>
inline void PutMatrixElt(Scalar value, int row, int col, std::ofstream& out)
{
out << row << " "<< col << " " << value << "\n";
}
template<typename Scalar>
inline void PutMatrixElt(std::complex<Scalar> value, int row, int col, std::ofstream& out)
{
out << row << " " << col << " " << value.real() << " " << value.imag() << "\n";
}
template<typename Scalar>
inline void putVectorElt(Scalar value, std::ofstream& out)
{
out << value << "\n";
}
template<typename Scalar>
inline void putVectorElt(std::complex<Scalar> 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<typename SparseMatrixType>
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<Scalar,Index> T;
std::vector<T> 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<typename VectorType>
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<typename SparseMatrixType>
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<Scalar>(header, sym);
out << header << std::endl;
out << mat.rows() << " " << mat.cols() << " " << mat.nonZeros() << "\n";
int count = 0;
for(int j=0; j<mat.outerSize(); ++j)
for(typename SparseMatrixType::InnerIterator it(mat,j); it; ++it)
{
++ count;
internal::PutMatrixElt(it.value(), it.row()+1, it.col()+1, out);
// out << it.row()+1 << " " << it.col()+1 << " " << it.value() << "\n";
}
out.close();
return true;
}
template<typename VectorType>
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<Scalar, std::complex<float> >::value || internal::is_same<Scalar, std::complex<double> >::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

View File

@ -0,0 +1,247 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2012 Desire NUENTSA WAKAM <desire.nuentsa_wakam@inria.fr>
//
// 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 <typename Scalar>
class MatrixMarketIterator
{
typedef typename NumTraits<Scalar>::Real RealScalar;
public:
typedef Matrix<Scalar,Dynamic,1> VectorType;
typedef SparseMatrix<Scalar,ColMajor> 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<Lower>().norm();
RealScalar upper_norm = m_mat.template triangularView<Upper>().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<Lower>();
}
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<Upper>();
}
}
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<Scalar, std::complex<float> >::value || internal::is_same<Scalar, std::complex<double> >::value)
continue;
}
if (iscomplex)
{
if(internal::is_same<Scalar, float>::value || internal::is_same<Scalar, double>::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

View File

@ -0,0 +1,327 @@
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// 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<typename Scalar> struct StdMapTraits
{
typedef int KeyType;
typedef std::map<KeyType,Scalar> 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 <tr1/unordered_map>
* #define EIGEN_UNORDERED_MAP_SUPPORT
* namespace std {
* using std::tr1::unordered_map;
* }
* \endcode
*
* \see RandomSetter
*/
template<typename Scalar> struct StdUnorderedMapTraits
{
typedef int KeyType;
typedef std::unordered_map<KeyType,Scalar> 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<typename Scalar> struct GoogleDenseHashMapTraits
{
typedef int KeyType;
typedef google::dense_hash_map<KeyType,Scalar> 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<typename Scalar> struct GoogleSparseHashMapTraits
{
typedef int KeyType;
typedef google::sparse_hash_map<KeyType,Scalar> 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<double> m(rows,cols);
* {
* RandomSetter<SparseMatrix<double> > 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 <google/dense_hash_map> yourself \b before Eigen/Sparse header
* - define EIGEN_GOOGLEHASH_SUPPORT
* In the later case the inclusion of <google/dense_hash_map> is made for you.
*
* \see http://code.google.com/p/google-sparsehash/
*/
template<typename SparseMatrixType,
template <typename T> 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<ScalarWrapper>::KeyType KeyType;
typedef typename MapTraits<ScalarWrapper>::Type HashMapType;
static const int OuterPacketMask = (1 << OuterPacketBits) - 1;
enum {
SwapStorage = 1 - MapTraits<ScalarWrapper>::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<m_outerPackets; ++k)
MapTraits<ScalarWrapper>::setInvalidKey(m_hashmaps[k],ik);
// insert current coeffs
for (Index j=0; j<mp_target->outerSize(); ++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<<m_keyBitsOffset)-1;
if (!SwapStorage) // also means the map is sorted
{
mp_target->setZero();
mp_target->makeCompressed();
mp_target->reserve(nonZeros());
Index prevOuter = -1;
for (Index k=0; k<m_outerPackets; ++k)
{
const Index outerOffset = (1<<OuterPacketBits) * k;
typename HashMapType::iterator end = m_hashmaps[k].end();
for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it!=end; ++it)
{
const Index outer = (it->first >> 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; k<m_outerPackets; ++k)
{
typename HashMapType::iterator end = m_hashmaps[k].end();
for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it!=end; ++it)
{
const Index outer = it->first & keyBitsMask;
++positions[outer];
}
}
// prefix sum
Index count = 0;
for (Index j=0; j<mp_target->outerSize(); ++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; k<m_outerPackets; ++k)
{
const Index outerOffset = (1<<OuterPacketBits) * k;
typename HashMapType::iterator end = m_hashmaps[k].end();
for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it!=end; ++it)
{
const Index inner = (it->first >> 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<KeyType>((outerMinor<<m_keyBitsOffset) | inner);
return m_hashmaps[outerMajor][key].value;
}
/** \returns the number of non zero coefficients
*
* \note According to the underlying map/hash_map implementation,
* this function might be quite expensive.
*/
Index nonZeros() const
{
Index nz = 0;
for (Index k=0; k<m_outerPackets; ++k)
nz += static_cast<Index>(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

View File

@ -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 <typename S_type> IGL_INLINE S_type EPS();
template <typename S_type> IGL_INLINE S_type EPS_SQ();

View File

@ -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) {

View File

@ -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()

View File

@ -1,6 +1,7 @@
#ifndef slic3r_Channel_hpp_
#define slic3r_Channel_hpp_
#include <memory>
#include <deque>
#include <condition_variable>
#include <mutex>
@ -13,32 +14,26 @@ namespace Slic3r {
template<class T> class Channel
{
private:
using UniqueLock = std::unique_lock<std::mutex>;
using Queue = std::deque<T>;
public:
class Guard
using UniqueLock = std::unique_lock<std::mutex>;
template<class Ptr> 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<T>;
using LockedConstPtr = std::unique_ptr<const Queue, Unlocker<const Queue>>;
using LockedPtr = std::unique_ptr<Queue, Unlocker<Queue>>;
Channel() {}
~Channel() {}
@ -56,7 +51,7 @@ public:
{
{
UniqueLock lock(m_mutex);
m_queue.push_back(std::forward(item));
m_queue.push_back(std::forward<T>(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<const Queue>(UniqueLock(m_mutex)));
}
LockedPtr lock_rw()
{
return LockedPtr(&m_queue, Unlocker<Queue>(UniqueLock(m_mutex)));
}
private:
Queue m_queue;
std::mutex m_mutex;
mutable std::mutex m_mutex;
std::condition_variable m_condition;
};

View File

@ -483,7 +483,8 @@ 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)
if (it1->first != it2->first || *it1->second != *it2->second)
// key or value differ
return false;
return it1 == it1_end && it2 == it2_end;
}

View File

@ -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<double, 3, 3, Eigen::DontAlign> 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<double>();
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
}
}

View File

@ -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 = "<zero>";
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 = "<group>";
@ -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;

View File

@ -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)
@ -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)

View File

@ -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

View File

@ -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<GCodeMove::EType, GCodeMovesList> &kvp : m_moves_map)
out += sizeof(kvp) + SLIC3R_STDVEC_MEMSIZE(kvp.second, GCodeMove);
out += m_process_output.size();
return out;
}
} // namespace Slic3r

View File

@ -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:

View File

@ -2,6 +2,7 @@
#include "PreviewData.hpp"
#include <float.h>
#include <I18N.hpp>
#include "Utils.hpp"
#include <boost/format.hpp>
@ -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

View File

@ -22,6 +22,7 @@ public:
static const Color Dummy;
};
// Color mapping from a <min, max> 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<float>& tool_colors, const std::vector</*double*/std::pair<double, double>>& 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);

View File

@ -290,6 +290,7 @@ namespace Slic3r {
// buffer line to export only when greater than 64K to reduce writing calls
std::string export_line;
char time_line[64];
G1LineIdToBlockIdMap::const_iterator it_line_id = _g1_line_ids.begin();
while (std::getline(in, gcode_line))
{
if (!in.good())
@ -310,32 +311,32 @@ 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)
{
float block_remaining_time = _time - block.elapsed_time;
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)
{
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());
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;
}
}
}
}
});
export_line += gcode_line;
@ -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)

View File

@ -209,7 +209,8 @@ namespace Slic3r {
typedef std::map<Block::EMoveType, MoveStats> MovesStatsMap;
#endif // ENABLE_MOVE_STATS
typedef std::map<unsigned int, unsigned int> G1LineIdToBlockIdMap;
typedef std::pair<unsigned int, unsigned int> G1LineIdToBlockId;
typedef std::vector<G1LineIdToBlockId> 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();

View File

@ -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
} }

View File

@ -167,7 +167,6 @@ Vec3d extract_euler_angles(const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>&
// 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
} }

View File

@ -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<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[2].cast<double>()));
}
#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<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[1].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[2].cast<double>()));
}
#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;
}

View File

@ -11,9 +11,7 @@
#include <string>
#include <utility>
#include <vector>
#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;

View File

@ -135,11 +135,6 @@ objfunc(const PointImpl& bincenter,
const ItemGroup& remaining
)
{
using Coord = TCoord<PointImpl>;
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)));
@ -631,10 +627,11 @@ BedShapeHint bedShape(const Polyline &bed) {
Circle ret(center, avg_dist);
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;
}
}
return ret;
};
@ -665,8 +662,6 @@ bool arrange(Model &model,
std::function<void (unsigned)> progressind,
std::function<bool ()> stopcondition)
{
using ArrangeResult = _IndexedPackGroup<PolygonImpl>;
bool ret = true;
// Get the 2D projected shapes with their 3D model instance pointers

View File

@ -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<Point> MultiPoint::_douglas_peucker(const std::vector<Point>& pts, const double tolerance)
{
std::vector<Point> 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<Point> MultiPoint::_douglas_peucker(const std::vector<Point>& 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);
}
}

View File

@ -8,13 +8,14 @@
#include "SupportMaterial.hpp"
#include "GCode.hpp"
#include "GCode/WipeTowerPrusaMM.hpp"
#include <algorithm>
#include <unordered_set>
#include <boost/log/trivial.hpp>
#include "Utils.hpp"
#include "PrintExport.hpp"
#include <algorithm>
#include <unordered_set>
#include <boost/filesystem/path.hpp>
#include <boost/log/trivial.hpp>
//! macro used to mark string used at localization,
//! return same string
@ -762,6 +763,7 @@ 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);
update_apply_status(object->invalidate_all_steps());
delete object;
}
m_objects.clear();
@ -992,12 +994,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
const_cast<PrintObjectStatus*>(*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<PrintObjectStatus*>(*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))
for (size_t i = 0; i < region_id; ++i) {
const PrintRegion &region_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())
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)
if (m_regions[i]->m_refcnt == 0) {
if (idx_empty_slot == -1)
idx_empty_slot = i;
else if (config.equals(m_regions[i]->config())) {
} 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,6 +1635,8 @@ void Print::_make_skirt()
{
Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, scale_(0.1));
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
if (loops.empty())
break;
loop = loops.front();
}
// Extrude the skirt loop.

View File

@ -108,7 +108,7 @@ public:
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[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.

View File

@ -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<SLADisplayOrientation>::get_enum_values();
def->enum_values.push_back("landscape");

View File

@ -36,7 +36,7 @@ enum GCodeFlavor {
};
enum PrintHostType {
htOctoPrint, htDuet,
htOctoPrint, htDuet, htSL1,
};
enum InfillPattern {

View File

@ -5,6 +5,7 @@
#include "SupportMaterial.hpp"
#include "Surface.hpp"
#include "Slicing.hpp"
#include "Utils.hpp"
#include <utility>
#include <boost/log/trivial.hpp>
@ -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<Point> 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 &region = *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 &region = *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<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &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) {

View File

@ -3,23 +3,50 @@
#include "SLAAutoSupports.hpp"
#include "Model.hpp"
#include "ExPolygon.hpp"
#include "SVG.hpp"
#include "Point.hpp"
#include "ClipperUtils.hpp"
#include <iostream>
#include <random>
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<ExPolygons>& slices, const std::vector<float>& heights,
const Config& config, std::function<void(void)> 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<std::pair<ExPolygon, coord_t>> islands = find_islands(slices, heights);
// Uniformly cover each of the islands with support points.
for (const auto& island : islands) {
std::vector<Vec3d> 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<Vec3d> 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<float>() * point;
//for (Vec3f& point : m_model_object.sla_support_points)
// point = transformation_matrix.cast<float>() * 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; i<stl.stats.number_of_facets; ++i) {
const stl_facet* facet = stl.facet_start+i;
V(3*i+0, 0) = facet->vertex[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<Vec3f> facets_normals;
std::vector<Vec3d> facets_normals;
// Only points belonging to islands were added so far - they all lie on horizontal surfaces:
for (unsigned int i=0; i<m_output.size(); ++i)
facets_normals.push_back(Vec3d(0,0,-1));
// The AABB hierarchy will be used to find normals of already placed points.
// The points added automatically will just push_back the new normal on the fly.
igl::AABB<Eigen::MatrixXf,3> aabb;
/*igl::AABB<Eigen::MatrixXf,3> aabb;
aabb.init(V, F);
for (unsigned int i=0; i<m_model_object.sla_support_points.size(); ++i) {
int facet_idx = 0;
@ -83,61 +101,69 @@ void SLAAutoSupports::generate()
Vec3f normal = a1.cross(a2);
normal.normalize();
facets_normals.push_back(normal);
}
}*/
// New potential support point is randomly generated on the mesh and distance to all already placed points is calculated.
// In case it is never smaller than certain limit (depends on the new point's facet normal), the point is accepted.
// The process stops after certain number of points is refused in a row.
Vec3f point;
Vec3f normal;
Vec3d point;
Vec3d normal;
int added_points = 0;
int refused_points = 0;
const int refused_limit = 30;
// Angle at which the density reaches zero:
const float threshold_angle = std::min(M_PI_2, M_PI_4 * acos(0.f/m_config.density_at_horizontal) / acos(m_config.density_at_45/m_config.density_at_horizontal));
srand(time(NULL)); // rand() is used by igl::random_point_on_mesh
size_t cancel_test_cntr = 0;
while (refused_points < refused_limit) {
if (++ cancel_test_cntr == 500) {
// Don't call the cancellation routine too often as the multi-core cache synchronization
// may be pretty expensive.
m_throw_on_cancel();
cancel_test_cntr = 0;
}
// Place a random point on the mesh and calculate corresponding facet's normal:
Eigen::VectorXi FI;
Eigen::MatrixXf B;
igl::random_points_on_mesh(1, V, F, B, FI);
point = B(0,0)*V.row(F(FI(0),0)) +
B(0,1)*V.row(F(FI(0),1)) +
B(0,2)*V.row(F(FI(0),2));
Eigen::MatrixXd B;
igl::random_points_on_mesh(1, m_V, m_F, B, FI);
point = B(0,0)*m_V.row(m_F(FI(0),0)) +
B(0,1)*m_V.row(m_F(FI(0),1)) +
B(0,2)*m_V.row(m_F(FI(0),2));
if (point(2) - bb.min(2) < m_config.minimal_z)
continue;
Vec3f a1 = V.row(F(FI(0),1)) - V.row(F(FI(0),0));
Vec3f a2 = V.row(F(FI(0),2)) - V.row(F(FI(0),0));
Vec3d a1 = m_V.row(m_F(FI(0),1)) - m_V.row(m_F(FI(0),0));
Vec3d a2 = m_V.row(m_F(FI(0),2)) - m_V.row(m_F(FI(0),0));
normal = a1.cross(a2);
normal.normalize();
// calculate angle between the normal and vertical:
float angle = angle_from_normal(normal);
float angle = angle_from_normal(normal.cast<float>());
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<m_model_object.sla_support_points.size(); ++i) {
if (approximate_geodesic_distance(m_model_object.sla_support_points[i], point, facets_normals[i], normal) < distance_limit) {
for (unsigned int i=0; i<points.size(); ++i) {
if (approximate_geodesic_distance(points[i], point, facets_normals[i], normal) < limit) {
add_it = false;
++refused_points;
break;
}
}
if (add_it) {
m_model_object.sla_support_points.push_back(point);
points.push_back(point.cast<double>());
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<float>() * point;
//for (Vec3f& point : m_model_object.sla_support_points)
// point = transformation_matrix.inverse().cast<float>() * 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<std::pair<ExPolygon, coord_t>> SLAAutoSupports::find_islands(const std::vector<ExPolygons>& slices, const std::vector<float>& heights) const
{
std::vector<std::pair<ExPolygon, coord_t>> islands;
struct PointAccessor {
const Point* operator()(const Point &pt) const { return &pt; }
};
typedef ClosestPointInRadiusLookup<Point, PointAccessor> ClosestPointLookupType;
for (unsigned int i = 0; i<slices.size(); ++i) {
const ExPolygons& expolys_top = slices[i];
const ExPolygons& expolys_bottom = (i == 0 ? ExPolygons() : slices[i-1]);
std::string layer_num_str = std::string((i<10 ? "0" : "")) + std::string((i<100 ? "0" : "")) + std::to_string(i);
#ifdef SLA_AUTOSUPPORTS_DEBUG
output_expolygons(expolys_top, "top" + layer_num_str + ".svg");
#endif /* SLA_AUTOSUPPORTS_DEBUG */
ExPolygons diff = diff_ex(expolys_top, expolys_bottom);
#ifdef SLA_AUTOSUPPORTS_DEBUG
output_expolygons(diff, "diff" + layer_num_str + ".svg");
#endif /* SLA_AUTOSUPPORTS_DEBUG */
ClosestPointLookupType cpl(SCALED_EPSILON);
for (const ExPolygon& expol : expolys_top) {
for (const Point& p : expol.contour.points)
cpl.insert(p);
for (const Polygon& hole : expol.holes)
for (const Point& p : hole.points)
cpl.insert(p);
// the lookup structure now contains all points from the top slice
}
for (const ExPolygon& polygon : diff) {
// we want to check all boundary points of the diff polygon
bool island = true;
for (const Point& p : polygon.contour.points) {
if (cpl.find(p).second != 0) { // the point belongs to the bottom slice - this cannot be an island
island = false;
goto NO_ISLAND;
}
}
for (const Polygon& hole : polygon.holes)
for (const Point& p : hole.points)
if (cpl.find(p).second != 0) {
island = false;
goto NO_ISLAND;
}
if (island) { // all points of the diff polygon are from the top slice
islands.push_back(std::make_pair(polygon, scale_(i!=0 ? heights[i-1] : heights[0]-(heights[1]-heights[0]))));
}
NO_ISLAND: ;// continue with next ExPolygon
}
#ifdef SLA_AUTOSUPPORTS_DEBUG
//if (!islands.empty())
// output_expolygons(islands, "islands" + layer_num_str + ".svg");
#endif /* SLA_AUTOSUPPORTS_DEBUG */
m_throw_on_cancel();
}
return islands;
}
std::vector<Vec3d> SLAAutoSupports::uniformly_cover(const std::pair<ExPolygon, coord_t>& 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<Vec3d>{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<Vec3d> 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<Vec3d>& 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<float>(), 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<double>();
}
}
} // namespace Slic3r

View File

@ -3,15 +3,12 @@
#include <libslic3r/Point.hpp>
#include <libslic3r/TriangleMesh.hpp>
#include <libslic3r/SLA/SLASupportTree.hpp>
// #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<ExPolygons>& slices,
const std::vector<float>& heights, const Config& config, std::function<void(void)> throw_on_cancel);
const std::vector<Vec3d>& output() { return m_output; }
private:
std::vector<Vec3d> m_output;
std::vector<Vec3d> 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<std::pair<ExPolygon, coord_t>> find_islands(const std::vector<ExPolygons>& slices, const std::vector<float>& heights) const;
void sprinkle_mesh(const TriangleMesh& mesh);
std::vector<Vec3d> uniformly_cover(const std::pair<ExPolygon, coord_t>& island);
void project_upward_onto_mesh(std::vector<Vec3d>& 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<void(void)> m_throw_on_cancel;
const Eigen::MatrixXd& m_V;
const Eigen::MatrixXi& m_F;
};
} // namespace Slic3r

View File

@ -8,11 +8,12 @@
#include "SLABoilerPlate.hpp"
#include "SLASpatIndex.hpp"
#include "SLABasePool.hpp"
#include <libnest2d/tools/benchmark.h>
#include "ClipperUtils.hpp"
#include "ClipperUtils.hpp"
#include "Model.hpp"
#include <boost/log/trivial.hpp>
/**
* 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<void()> 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,

View File

@ -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();
};
}

View File

@ -1,3 +1,4 @@
#include <cmath>
#include "SLA/SLASupportTree.hpp"
#include "SLA/SLABoilerPlate.hpp"
#include "SLA/SLASpatIndex.hpp"
@ -9,15 +10,8 @@
#include "boost/geometry/index/rtree.hpp"
#include <igl/ray_mesh_intersect.h>
//#if !defined(_MSC_VER) || defined(_WIN64)
#if 1
#define IGL_COMPATIBLE
#endif
#ifdef IGL_COMPATIBLE
#include <igl/point_mesh_squared_distance.h>
#endif
#include <igl/remove_duplicate_vertices.h>
#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<double, 3>;
auto line = Line3D::Through(e1, e2);
double d = line.distance(p);
return std::abs(d) < eps;
}
template<class Vec> 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<void()> 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));
// 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<Vec3i> 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<Vec3d> 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();

View File

@ -1,6 +1,7 @@
#include "SLAPrint.hpp"
#include "SLA/SLASupportTree.hpp"
#include "SLA/SLABasePool.hpp"
#include "SLA/SLAAutoSupports.hpp"
#include "MTUtils.hpp"
#include <unordered_set>
@ -36,8 +37,7 @@ namespace {
const std::array<unsigned, slaposCount> OBJ_STEP_LEVELS =
{
10, // slaposObjectSlice,
10, // slaposSupportIslands,
20, // slaposSupportPoints,
30, // slaposSupportPoints,
25, // slaposSupportTree,
25, // slaposBasePool,
5, // slaposSliceSupports,
@ -47,8 +47,7 @@ const std::array<unsigned, slaposCount> OBJ_STEP_LEVELS =
const std::array<std::string, slaposCount> 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<float> SLAPrint::calculate_heights(const BoundingBoxf3& bb3d,
float elevation,
float initial_layer_height,
float layer_height) const
{
std::vector<float> 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<class...Args>
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>(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<float> 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<float> 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());
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<float> 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<Vec3d>& 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<SLAPrintObjectStep, slaposCount> 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<slaposFn, slaposCount> 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<ExPolygons> EMPTY_SLICES;
const TriangleMesh EMPTY_MESH;
}
const Eigen::MatrixXd& SLAPrintObject::get_support_points() const
{
return m_supportdata->support_points;
}
const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const
{
// assert(is_step_done(slaposSliceSupports));

View File

@ -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<ExPolygons>& get_model_slices() const;
const std::vector<ExPolygons>& 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<class Fmt> void export_raster(const std::string& fname) {
if(m_printer) m_printer->save<Fmt>(fname);
@ -226,6 +228,8 @@ private:
lref(std::cref(lyr)), copies(std::cref(cp)) {}
};
std::vector<float> 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<LayerRef>;

View File

@ -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_

View File

@ -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_

View File

@ -8,6 +8,7 @@
#ifdef WIN32
#include <windows.h>
#include <psapi.h>
#else
#include <unistd.h>
#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

View File

@ -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

View File

@ -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<float>();
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<double>());
#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<double>());
else
m_transformed_convex_hull_bounding_box = bounding_box.transformed(world_matrix().cast<double>());
#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<float>().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<float>().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<float>().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<GLVolume*, double> GLVolumeWithZ;
typedef std::vector<GLVolumeWithZ> 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, &current_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<Vec3crd> 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<Vec3crd> 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);

View File

@ -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<GLVolume*> 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;

View File

@ -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 <cassert>
#include <stdexcept>
#include <cctype>
@ -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<SLAZipFmt>(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<PrinterTechnology>("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<SLAZipFmt>(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

View File

@ -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;

View File

@ -163,17 +163,42 @@ void Field::get_value_by_opt_type(wxString& str)
break; }
case coString:
case coStrings:
case coFloatOrPercent:
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;
break; }
default:
break;
}
}
bool TextCtrl::is_defined_input_value() const
template<class T>
bool is_defined_input_value(wxWindow* win, const ConfigOptionType& type)
{
if (static_cast<wxTextCtrl*>(window)->GetValue().empty() && m_opt.type != coString && m_opt.type != coStrings)
if (static_cast<T*>(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<wxTextCtrl>(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<double>(m_value) : -99999;
if (is_defined_input_value<wxComboBox>(window, m_opt.type)) {
if (fabs(old_val - boost::any_cast<double>(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<wxComboBox*>(window)->GetSelection();
if (m_opt_id.compare("top_fill_pattern") == 0 || m_opt_id.compare("bottom_fill_pattern") == 0 )
@ -672,6 +721,15 @@ boost::any& Choice::get_value()
else if (m_opt_id.compare("display_orientation") == 0)
m_value = static_cast<SLADisplayOrientation>(ret_enum);
}
else if (m_opt.gui_type == "f_enum_open") {
const int ret_enum = static_cast<wxComboBox*>(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<wxSizer*>(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;

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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 <size_t N> using Vec2dsEvent = ArrayEvent<Vec2d, N>;
using Vec3dEvent = Event<Vec3d>;
template <size_t N> using Vec3dsEvent = ArrayEvent<Vec3d, N>;
#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<int>); // 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<bool>);
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<unsigned int, VolumeCache> 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<EType, GLGizmoBase*> 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<float> _parse_colors(const std::vector<std::string>& colors);
public:
const Print* fff_print() const;
const SLAPrint* sla_print() const;
};

View File

@ -19,7 +19,6 @@ class ExPolygon;
typedef std::vector<ExPolygon> ExPolygons;
class ModelObject;
class PrintObject;
class GCodePreviewData;
namespace GUI {

View File

@ -8,6 +8,7 @@
#include "libslic3r/Geometry.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/SLA/SLASupportTree.hpp"
#include "libslic3r/SLAPrint.hpp"
#include <cstdio>
#include <numeric>
@ -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<float>();
else if (single_volume)
scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_scaling_factor().cast<float>();
#else
Vec3f scale = single_instance ? 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_scaling_factor().cast<float>() : 100.0f * m_scale.cast<float>();
#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<double>();
// 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<double>();
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; i<points.rows();++i)
model_object->sla_support_points.push_back(Vec3f(po->trafo().inverse().cast<float>() * 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<double>();
if (first_run) {
first_run = false;
goto RENDER_AGAIN;
}
}

View File

@ -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;

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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<GLToolbarItem*> 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<GLRadioToolbarItem*> 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

View File

@ -4,6 +4,7 @@
#include "WipeTowerDialog.hpp"
#include <assert.h>
#include <string>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
@ -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<std::wstring>());
#else
return wxString::FromUTF8(path.string<std::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()

View File

@ -1,6 +1,10 @@
#ifndef slic3r_GUI_hpp_
#define slic3r_GUI_hpp_
#include <boost/filesystem/path.hpp>
#include <wx/string.h>
#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);

View File

@ -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()) {
// 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.
out += ";*";
out += from_u8(custom_extension);
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<MainFrame*>(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<void()> 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,9 +489,12 @@ 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");
#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();
}

View File

@ -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<ImGuiWrapper> m_imgui;
#endif // ENABLE_IMGUI
std::unique_ptr<PrintHostJobQueue> m_printhost_queue;
std::unique_ptr<PrintHostJobQueue> 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<void()> 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)

View File

@ -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<ModelVolume::Type>(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<ModelVolume::Type>(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()));

View File

@ -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<std::string> axes{ "_x", "_y", "_z" };
if (opt_key == "scale_unit") {
const wxString& selection = boost::any_cast<wxString>(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<std::string> 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
// 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());
update_position_value(volume->get_offset());
update_rotation_value(volume->get_rotation());
update_scale_value(volume->get_scaling_factor());
m_og->enable();
}
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<wxString>(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

View File

@ -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);
// 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);
};
}}

View File

@ -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<void()> schedule_background_process_func)
#else
Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function<void()> 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,26 +333,17 @@ 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);
@ -397,13 +353,6 @@ void Preview::set_number_extruders(unsigned int number_extruders)
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()
{
@ -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<double>& 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<ConfigOptionFloats>("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<ConfigOptionFloats>("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 <z-eps, z+eps>.
// Returns -1 if there is no such member.
static int find_close_layer_idx(const std::vector<double>& 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<double>& 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<std::pair<int, double>> 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<double> &ticks_from_config = (config.option<ConfigOptionFloats>("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<std::pair<int, double>> &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<double> &ticks_from_config = (config.option<ConfigOptionFloats>("colorprint_heights"))->values;
std::vector<double> &ticks_from_config = (wxGetApp().preset_bundle->project_config.option<ConfigOptionFloats>("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<std::pair<int, double>> &values,
m_schedule_background_process();
}
void Preview::set_double_slider_thumbs(const std::vector<double> &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<std::string> 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<const ConfigOptionStrings*>(m_config->option("extruder_colour"));
const ConfigOptionStrings* filamemts_opt = dynamic_cast<const ConfigOptionStrings*>(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");
@ -810,6 +769,13 @@ void Preview::load_print_as_fff()
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<double> layer_zs;
std::copy(zs.begin(), zs.end(), std::back_inserter(layer_zs));
m_force_sliders_full_range = true;
update_sliders(layer_zs);
}

View File

@ -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<void()> schedule_background_process = [](){});
#else
Preview(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function<void()> 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<double>& layers_z, bool force_sliders_full_range);
void update_double_slider(const std::vector<double>& layers_z, bool force_sliders_full_range = false);
void fill_slider_values(std::vector<std::pair<int, double>> &values,
const std::vector<double> &layers_z);
void set_double_slider_thumbs( const std::vector<double> &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);

View File

@ -0,0 +1,167 @@
#include "KBShortcutsDialog.hpp"
#include "I18N.hpp"
#include "libslic3r/Utils.hpp"
#include "GUI.hpp"
#include <wx/scrolwin.h>
#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

View File

@ -0,0 +1,32 @@
#ifndef slic3r_GUI_KBShortcutsDialog_hpp_
#define slic3r_GUI_KBShortcutsDialog_hpp_
#include <wx/wx.h>
#include <map>
namespace Slic3r {
namespace GUI {
class KBShortcutsDialog : public wxDialog
{
typedef std::pair<std::string, std::string> Shortcut;
typedef std::vector< Shortcut > Shortcuts;
typedef std::map<wxString, Shortcuts> ShortcutsMap;
wxString text_info {wxEmptyString};
ShortcutsMap m_full_shortcuts;
public:
KBShortcutsDialog();
void fill_shortcuts();
private:
void onCloseDialog(wxEvent &);
};
} // namespace GUI
} // namespace Slic3r
#endif

View File

@ -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")));
}
// 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"),
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_ANY, L("Preview\tCtrl+6"), L("Show the 3D slices preview"),
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)

View File

@ -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 };

View File

@ -6,6 +6,7 @@
#include <wx/button.h>
#include <wx/statbmp.h>
#include <wx/scrolwin.h>
#include <wx/clipbrd.h>
#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();
}

View File

@ -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;
};

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More