Merge remote-tracking branch 'remotes/prusa/master' into masterPE
@ -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/)
|
||||
|
BIN
resources/icons/Slic3r_32px.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 13 KiB |
BIN
resources/icons/toolbar141.png
Normal file
After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 16 KiB |
@ -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
|
||||
|
@ -5,7 +5,7 @@
|
||||
name = Prusa Research
|
||||
# Configuration version of this file. Config file will only be installed, if the config_version differs.
|
||||
# This means, the server may force the Slic3r configuration to be downgraded.
|
||||
config_version = 0.4.0-alpha2
|
||||
config_version = 0.4.0-alpha3
|
||||
# Where to get the updates from?
|
||||
config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/PrusaResearch/
|
||||
|
||||
@ -18,30 +18,37 @@ config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/ma
|
||||
[printer_model:MK3]
|
||||
name = Original Prusa i3 MK3
|
||||
variants = 0.4; 0.25; 0.6
|
||||
technology = FFF
|
||||
|
||||
[printer_model:MK2.5]
|
||||
name = Original Prusa i3 MK2.5
|
||||
variants = 0.4; 0.25; 0.6
|
||||
technology = FFF
|
||||
|
||||
[printer_model:MK2S]
|
||||
name = Original Prusa i3 MK2/S
|
||||
variants = 0.4; 0.25; 0.6
|
||||
technology = FFF
|
||||
|
||||
[printer_model:MK3MMU2]
|
||||
name = Original Prusa i3 MK3 MMU 2.0
|
||||
variants = 0.4
|
||||
technology = FFF
|
||||
|
||||
[printer_model:MK2SMM]
|
||||
name = Original Prusa i3 MK2/S MMU 1.0
|
||||
variants = 0.4; 0.6
|
||||
technology = FFF
|
||||
|
||||
[printer_model:MK2.5MMU2]
|
||||
name = Original Prusa i3 MK2.5 MMU 2.0
|
||||
variants = 0.4
|
||||
technology = FFF
|
||||
|
||||
[printer_model:SL1]
|
||||
name = Original Prusa SL1
|
||||
variants = default; dummy
|
||||
variants = default
|
||||
technology = SLA
|
||||
|
||||
# All presets starting with asterisk, for example *common*, are intermediate and they will
|
||||
# not make it into the user interface.
|
||||
@ -1140,19 +1147,132 @@ min_fan_speed = 100
|
||||
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
|
||||
temperature = 220
|
||||
|
||||
[sla_material:*common*]
|
||||
[sla_print:*common*]
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_SL1.*/
|
||||
layer_height = 0.05
|
||||
initial_layer_height = 0.3
|
||||
output_filename_format = [input_filename_base].dwz
|
||||
pad_edge_radius = 0.5
|
||||
pad_enable = 1
|
||||
pad_max_merge_distance = 50
|
||||
pad_wall_height = 3
|
||||
pad_wall_thickness = 1
|
||||
support_base_diameter = 3
|
||||
support_base_height = 0.5
|
||||
support_critical_angle = 45
|
||||
support_density_at_45 = 250
|
||||
support_density_at_horizontal = 500
|
||||
support_head_front_diameter = 0.4
|
||||
support_head_penetration = 0.4
|
||||
support_head_width = 3
|
||||
support_max_bridge_length = 10
|
||||
support_minimal_z = 0
|
||||
support_object_elevation = 5
|
||||
support_pillar_diameter = 1
|
||||
support_pillar_widening_factor = 0
|
||||
supports_enable = 1
|
||||
|
||||
[sla_print:0.025 UltraDetail]
|
||||
inherits = *common*
|
||||
layer_height = 0.025
|
||||
support_head_width = 2
|
||||
|
||||
[sla_print:0.035 Detail]
|
||||
inherits = *common*
|
||||
layer_height = 0.035
|
||||
|
||||
[sla_print:0.05 Normal]
|
||||
inherits = *common*
|
||||
layer_height = 0.05
|
||||
|
||||
[sla_print:0.1 Fast]
|
||||
inherits = *common*
|
||||
layer_height = 0.1
|
||||
|
||||
########### Materials 0.025
|
||||
|
||||
[sla_material:*common 0.05*]
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_SL1.*/
|
||||
compatible_prints_condition = layer_height == 0.05
|
||||
exposure_time = 12
|
||||
initial_exposure_time = 45
|
||||
initial_layer_height = 0.5
|
||||
material_correction_curing = 1,1,1
|
||||
material_correction_printing = 1,1,1
|
||||
material_notes =
|
||||
|
||||
[sla_material:*common 0.025*]
|
||||
inherits = *common 0.05*
|
||||
compatible_prints_condition = layer_height == 0.025
|
||||
exposure_time = 10
|
||||
initial_exposure_time = 15
|
||||
material_correction_printing = 1, 1, 1
|
||||
material_correction_curing = 1, 1, 1
|
||||
initial_exposure_time = 35
|
||||
|
||||
[sla_material:Material 1]
|
||||
inherits = *common*
|
||||
[sla_material:*common 0.035*]
|
||||
inherits = *common 0.05*
|
||||
compatible_prints_condition = layer_height == 0.035
|
||||
exposure_time = 13
|
||||
initial_exposure_time = 40
|
||||
|
||||
[sla_material:Material 2]
|
||||
inherits = *common*
|
||||
[sla_material:*common 0.1*]
|
||||
inherits = *common 0.05*
|
||||
compatible_prints_condition = layer_height == 0.1
|
||||
exposure_time = 20
|
||||
initial_exposure_time = 90
|
||||
|
||||
########### Materials 0.025
|
||||
|
||||
[sla_material:Jamg He Transparent Clear 0.025]
|
||||
inherits = *common 0.025*
|
||||
|
||||
[sla_material:Jamg He Transparent Green 0.025]
|
||||
inherits = *common 0.025*
|
||||
|
||||
[sla_material:Jamg He Transparent Orange 0.025]
|
||||
inherits = *common 0.025*
|
||||
|
||||
[sla_material:Jamg He Transparent Red 0.025]
|
||||
inherits = *common 0.025*
|
||||
|
||||
########### Materials 0.05
|
||||
|
||||
[sla_material:Jamg He Transparent Clear 0.05]
|
||||
inherits = *common 0.05*
|
||||
|
||||
[sla_material:Jamg He Transparent Green 0.05]
|
||||
inherits = *common 0.05*
|
||||
|
||||
[sla_material:Jamg He Transparent Orange 0.05]
|
||||
inherits = *common 0.05*
|
||||
|
||||
[sla_material:Jamg He Transparent Red 0.05]
|
||||
inherits = *common 0.05*
|
||||
|
||||
########### Materials 0.035
|
||||
|
||||
[sla_material:Jamg He Transparent Clear 0.035]
|
||||
inherits = *common 0.035*
|
||||
|
||||
[sla_material:Jamg He Transparent Green 0.035]
|
||||
inherits = *common 0.035*
|
||||
|
||||
[sla_material:Jamg He Transparent Orange 0.035]
|
||||
inherits = *common 0.035*
|
||||
|
||||
[sla_material:Jamg He Transparent Red 0.035]
|
||||
inherits = *common 0.035*
|
||||
|
||||
########### Materials 0.1
|
||||
|
||||
[sla_material:Jamg He Transparent Clear 0.1]
|
||||
inherits = *common 0.1*
|
||||
|
||||
[sla_material:Jamg He Transparent Green 0.1]
|
||||
inherits = *common 0.1*
|
||||
|
||||
[sla_material:Jamg He Transparent Orange 0.1]
|
||||
inherits = *common 0.1*
|
||||
|
||||
[sla_material:Jamg He Transparent Red 0.1]
|
||||
inherits = *common 0.1*
|
||||
|
||||
[printer:*common*]
|
||||
printer_technology = FFF
|
||||
@ -1470,21 +1590,20 @@ end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG
|
||||
printer_technology = SLA
|
||||
printer_model = SL1
|
||||
printer_variant = default
|
||||
default_sla_material_profile = Material 1
|
||||
bed_shape = 0x0,150x0,150x100,0x100
|
||||
max_print_height = 100
|
||||
display_width = 150
|
||||
display_height = 100
|
||||
display_pixels_x = 2000
|
||||
display_pixels_y = 1000
|
||||
default_sla_material_profile = Jamg He Transparent Green 0.05
|
||||
default_sla_print_profile = 0.05 Normal
|
||||
bed_shape = 0.98x1.02,119.98x1.02,119.98x68.02,0.98x68.02
|
||||
display_height = 68.04
|
||||
display_orientation = portrait
|
||||
display_pixels_x = 2560
|
||||
display_pixels_y = 1440
|
||||
display_width = 120.96
|
||||
max_print_height = 150
|
||||
printer_correction = 1,1,1
|
||||
|
||||
[printer:Original Prusa SL1 dummy]
|
||||
inherits = Original Prusa SL1
|
||||
printer_variant = dummy
|
||||
default_sla_material_profile = Material 2
|
||||
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\n
|
||||
|
||||
# The obsolete presets will be removed when upgrading from the legacy configuration structure (up to Slic3r 1.39.2) to 1.40.0 and newer.
|
||||
[obsolete_presets]
|
||||
print="0.05mm DETAIL 0.25 nozzle";"0.05mm DETAIL MK3";"0.05mm DETAIL";"0.20mm NORMAL MK3";"0.35mm FAST MK3";"print:0.15mm OPTIMAL MK3 MMU2";"print:0.20mm FAST MK3 MMU2"
|
||||
filament="ColorFabb Brass Bronze 1.75mm";"ColorFabb HT 1.75mm";"ColorFabb nGen 1.75mm";"ColorFabb Woodfil 1.75mm";"ColorFabb XT 1.75mm";"ColorFabb XT-CF20 1.75mm";"E3D PC-ABS 1.75mm";"Fillamentum ABS 1.75mm";"Fillamentum ASA 1.75mm";"Generic ABS 1.75mm";"Generic PET 1.75mm";"Generic PLA 1.75mm";"Prusa ABS 1.75mm";"Prusa HIPS 1.75mm";"Prusa PET 1.75mm";"Prusa PLA 1.75mm";"Taulman Bridge 1.75mm";"Taulman T-Glase 1.75mm"
|
||||
printer="Original Prusa SL1 dummy"
|
||||
|
53
src/eigen/unsupported/Eigen/SparseExtra
Normal 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
|
@ -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
|
1079
src/eigen/unsupported/Eigen/src/SparseExtra/BlockSparseMatrix.h
Normal 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
|
275
src/eigen/unsupported/Eigen/src/SparseExtra/MarketIO.h
Normal 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
|
@ -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
|
327
src/eigen/unsupported/Eigen/src/SparseExtra/RandomSetter.h
Normal 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
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
||||
} }
|
||||
|
@ -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
|
||||
|
||||
} }
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)));
|
||||
|
||||
@ -629,12 +625,13 @@ BedShapeHint bedShape(const Polyline &bed) {
|
||||
avg_dist /= vertex_distances.size();
|
||||
|
||||
Circle ret(center, avg_dist);
|
||||
for (auto el: vertex_distances)
|
||||
for(auto el : vertex_distances)
|
||||
{
|
||||
if (abs(el - avg_dist) > 10 * SCALED_EPSILON)
|
||||
if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) {
|
||||
ret = Circle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 ®ion_other = *m_regions[i];
|
||||
if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
|
||||
// Regions were merged. Reset this print_object.
|
||||
goto print_object_end;
|
||||
}
|
||||
this_region_config_set = true;
|
||||
}
|
||||
}
|
||||
@ -1096,8 +1098,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
||||
bool fresh = print_object.region_volumes.empty();
|
||||
unsigned int volume_id = 0;
|
||||
for (const ModelVolume *volume : model_object.volumes) {
|
||||
if (! volume->is_model_part() && ! volume->is_modifier())
|
||||
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.
|
||||
|
@ -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.
|
||||
|
@ -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");
|
||||
|
@ -36,7 +36,7 @@ enum GCodeFlavor {
|
||||
};
|
||||
|
||||
enum PrintHostType {
|
||||
htOctoPrint, htDuet,
|
||||
htOctoPrint, htDuet, htSL1,
|
||||
};
|
||||
|
||||
enum InfillPattern {
|
||||
|
@ -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 ®ion = *m_print->regions()[region_id];
|
||||
@ -1101,7 +1103,7 @@ void PrintObject::discover_vertical_shells()
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells...";
|
||||
BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells..." << log_memory_info();
|
||||
|
||||
struct DiscoverVerticalShellsCacheEntry
|
||||
{
|
||||
@ -1485,7 +1487,7 @@ void PrintObject::discover_vertical_shells()
|
||||
sparse infill */
|
||||
void PrintObject::bridge_over_infill()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Bridge over infill...";
|
||||
BOOST_LOG_TRIVIAL(info) << "Bridge over infill..." << log_memory_info();
|
||||
|
||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
||||
const PrintRegion ®ion = *m_print->regions()[region_id];
|
||||
@ -1833,7 +1835,7 @@ bool PrintObject::update_layer_height_profile()
|
||||
// this should be idempotent
|
||||
void PrintObject::_slice()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing objects...";
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info();
|
||||
|
||||
this->typed_slices = false;
|
||||
|
||||
@ -2064,15 +2066,11 @@ std::vector<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) {
|
||||
|
@ -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
|
@ -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
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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));
|
||||
|
@ -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>;
|
||||
|
@ -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_
|
||||
|
||||
|
@ -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_
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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, ¤t_program_id);
|
||||
GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1;
|
||||
GLint print_box_detection_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1;
|
||||
|
||||
m_volume.render_VBOs(color_id, print_box_detection_id, -1);
|
||||
|
||||
::glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
::glDisableClientState(GL_VERTEX_ARRAY);
|
||||
::glDisableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
::glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void GLModel::render_legacy() const
|
||||
{
|
||||
::glEnable(GL_LIGHTING);
|
||||
::glEnable(GL_BLEND);
|
||||
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
::glCullFace(GL_BACK);
|
||||
::glEnableClientState(GL_VERTEX_ARRAY);
|
||||
::glEnableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
m_volume.render_legacy();
|
||||
|
||||
::glDisableClientState(GL_VERTEX_ARRAY);
|
||||
::glDisableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
::glDisable(GL_BLEND);
|
||||
::glDisable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
bool GLArrow::on_init(bool useVBOs)
|
||||
{
|
||||
Pointf3s vertices;
|
||||
std::vector<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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -19,7 +19,6 @@ class ExPolygon;
|
||||
typedef std::vector<ExPolygon> ExPolygons;
|
||||
class ModelObject;
|
||||
class PrintObject;
|
||||
class GCodePreviewData;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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()));
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}}
|
||||
|
@ -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);
|
||||
@ -396,13 +352,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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
167
src/slic3r/GUI/KBShortcutsDialog.cpp
Normal 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
|
32
src/slic3r/GUI/KBShortcutsDialog.hpp
Normal 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
|
@ -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)
|
||||
|
@ -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 };
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|