mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-11 16:57:53 -06:00
Merge branch 'master' into lm_sla_supports_auto
This commit is contained in:
commit
75ef3431b3
77 changed files with 5581 additions and 1010 deletions
BIN
resources/icons/toolbar_background.png
Normal file
BIN
resources/icons/toolbar_background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 22 KiB |
|
@ -46,7 +46,18 @@ endif()
|
||||||
add_subdirectory(libslic3r)
|
add_subdirectory(libslic3r)
|
||||||
|
|
||||||
if (SLIC3R_GUI)
|
if (SLIC3R_GUI)
|
||||||
|
if(WIN32)
|
||||||
message(STATUS "WXWIN environment set to: $ENV{WXWIN}")
|
message(STATUS "WXWIN environment set to: $ENV{WXWIN}")
|
||||||
|
elseif(UNIX)
|
||||||
|
message(STATUS "wx-config path: ${wxWidgets_CONFIG_EXECUTABLE}")
|
||||||
|
set(wxWidgets_USE_UNICODE ON)
|
||||||
|
if(SLIC3R_STATIC)
|
||||||
|
set(wxWidgets_USE_STATIC ON)
|
||||||
|
else()
|
||||||
|
set(wxWidgets_USE_STATIC OFF)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
find_package(wxWidgets REQUIRED COMPONENTS base core adv html gl)
|
find_package(wxWidgets REQUIRED COMPONENTS base core adv html gl)
|
||||||
include(${wxWidgets_USE_FILE})
|
include(${wxWidgets_USE_FILE})
|
||||||
endif()
|
endif()
|
||||||
|
|
53
src/eigen/unsupported/Eigen/SparseExtra
Normal file
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
1079
src/eigen/unsupported/Eigen/src/SparseExtra/BlockSparseMatrix.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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
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
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
|
|
@ -68,7 +68,7 @@ if(TBB_FOUND)
|
||||||
target_compile_definitions(libnest2d INTERFACE -D__TBB_NO_IMPLICIT_LINKAGE)
|
target_compile_definitions(libnest2d INTERFACE -D__TBB_NO_IMPLICIT_LINKAGE)
|
||||||
endif()
|
endif()
|
||||||
# The Intel TBB library will use the std::exception_ptr feature of C++11.
|
# The Intel TBB library will use the std::exception_ptr feature of C++11.
|
||||||
target_compile_definitions(libnest2d INTERFACE -DTBB_USE_CAPTURED_EXCEPTION=1)
|
target_compile_definitions(libnest2d INTERFACE -DTBB_USE_CAPTURED_EXCEPTION=0)
|
||||||
|
|
||||||
target_link_libraries(libnest2d INTERFACE tbb)
|
target_link_libraries(libnest2d INTERFACE tbb)
|
||||||
else()
|
else()
|
||||||
|
|
|
@ -194,6 +194,10 @@ target_link_libraries(libslic3r
|
||||||
tbb
|
tbb
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
target_link_libraries(libslic3r Psapi.lib)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(SLIC3R_PROFILE)
|
if(SLIC3R_PROFILE)
|
||||||
target_link_libraries(slic3r Shiny)
|
target_link_libraries(slic3r Shiny)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef slic3r_Channel_hpp_
|
#ifndef slic3r_Channel_hpp_
|
||||||
#define slic3r_Channel_hpp_
|
#define slic3r_Channel_hpp_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
@ -13,32 +14,26 @@ namespace Slic3r {
|
||||||
|
|
||||||
template<class T> class Channel
|
template<class T> class Channel
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
using UniqueLock = std::unique_lock<std::mutex>;
|
|
||||||
using Queue = std::deque<T>;
|
|
||||||
public:
|
public:
|
||||||
class Guard
|
using UniqueLock = std::unique_lock<std::mutex>;
|
||||||
|
|
||||||
|
template<class Ptr> class Unlocker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Guard(UniqueLock lock, const Queue &queue) : m_lock(std::move(lock)), m_queue(queue) {}
|
Unlocker(UniqueLock lock) : m_lock(std::move(lock)) {}
|
||||||
Guard(const Guard &other) = delete;
|
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
|
||||||
Guard(Guard &&other) = delete;
|
Unlocker(Unlocker &&other) noexcept : m_lock(std::move(other.m_lock)) {}
|
||||||
~Guard() {}
|
Unlocker& operator=(const Unlocker &other) = delete;
|
||||||
|
Unlocker& operator=(Unlocker &&other) { m_lock = std::move(other.m_lock); }
|
||||||
|
|
||||||
// Access trampolines
|
void operator()(Ptr*) { m_lock.unlock(); }
|
||||||
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;
|
|
||||||
private:
|
private:
|
||||||
UniqueLock m_lock;
|
mutable UniqueLock m_lock; // XXX: mutable: see above
|
||||||
const Queue &m_queue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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() {}
|
||||||
~Channel() {}
|
~Channel() {}
|
||||||
|
@ -56,7 +51,7 @@ public:
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
UniqueLock lock(m_mutex);
|
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(); }
|
if (! silent) { m_condition.notify_one(); }
|
||||||
}
|
}
|
||||||
|
@ -82,19 +77,22 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlocked observers
|
// Unlocked observers/hints
|
||||||
// Thread unsafe! Keep in mind you need to re-verify the result after acquiring lock!
|
// Thread unsafe! Keep in mind you need to re-verify the result after locking!
|
||||||
size_t size() const noexcept { return m_queue.size(); }
|
size_t size_hint() const noexcept { return m_queue.size(); }
|
||||||
bool empty() const noexcept { return m_queue.empty(); }
|
|
||||||
|
|
||||||
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:
|
private:
|
||||||
Queue m_queue;
|
Queue m_queue;
|
||||||
std::mutex m_mutex;
|
mutable std::mutex m_mutex;
|
||||||
std::condition_variable m_condition;
|
std::condition_variable m_condition;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,6 @@ static void extract_model_from_archive(
|
||||||
const char *model_xml = strstr(scene_xml_data.data(), model_name_tag);
|
const char *model_xml = strstr(scene_xml_data.data(), model_name_tag);
|
||||||
const char *zero_tag = "<zero>";
|
const char *zero_tag = "<zero>";
|
||||||
const char *zero_xml = strstr(scene_xml_data.data(), zero_tag);
|
const char *zero_xml = strstr(scene_xml_data.data(), zero_tag);
|
||||||
float trafo[3][4] = { 0 };
|
|
||||||
Vec3d instance_rotation = Vec3d::Zero();
|
Vec3d instance_rotation = Vec3d::Zero();
|
||||||
Vec3d instance_scaling_factor = Vec3d::Ones();
|
Vec3d instance_scaling_factor = Vec3d::Ones();
|
||||||
Vec3d instance_offset = Vec3d::Zero();
|
Vec3d instance_offset = Vec3d::Zero();
|
||||||
|
@ -124,19 +123,7 @@ static void extract_model_from_archive(
|
||||||
"[%f, %f, %f]", zero, zero+1, zero+2) == 3) {
|
"[%f, %f, %f]", zero, zero+1, zero+2) == 3) {
|
||||||
instance_scaling_factor = Vec3d((double)scale[0], (double)scale[1], (double)scale[2]);
|
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]);
|
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]));
|
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;
|
trafo_set = true;
|
||||||
}
|
}
|
||||||
const char *group_tag = "<group>";
|
const char *group_tag = "<group>";
|
||||||
|
@ -189,8 +176,6 @@ static void extract_model_from_archive(
|
||||||
// All the faces have been read.
|
// All the faces have been read.
|
||||||
stl_get_size(&stl);
|
stl_get_size(&stl);
|
||||||
mesh.repair();
|
mesh.repair();
|
||||||
// Transform the model.
|
|
||||||
stl_transform(&stl, &trafo[0][0]);
|
|
||||||
if (std::abs(stl.stats.min(2)) < EPSILON)
|
if (std::abs(stl.stats.min(2)) < EPSILON)
|
||||||
stl.stats.min(2) = 0.;
|
stl.stats.min(2) = 0.;
|
||||||
// Add a mesh to a model.
|
// 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);
|
memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50);
|
||||||
stl_get_size(&stl);
|
stl_get_size(&stl);
|
||||||
mesh.repair();
|
mesh.repair();
|
||||||
// Transform the model.
|
|
||||||
stl_transform(&stl, &trafo[0][0]);
|
|
||||||
// Add a mesh to a model.
|
// Add a mesh to a model.
|
||||||
if (mesh.facets_count() > 0)
|
if (mesh.facets_count() > 0)
|
||||||
mesh_valid = true;
|
mesh_valid = true;
|
||||||
|
@ -329,7 +312,7 @@ bool load_prus(const char *path, Model *model)
|
||||||
if (! mz_zip_reader_file_stat(&archive, i, &stat))
|
if (! mz_zip_reader_file_stat(&archive, i, &stat))
|
||||||
continue;
|
continue;
|
||||||
std::vector<char> buffer;
|
std::vector<char> buffer;
|
||||||
buffer.assign((size_t)stat.m_uncomp_size + 1, 0);
|
buffer.assign((size_t)stat.m_uncomp_size, 0);
|
||||||
res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (char*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
|
res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (char*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
|
||||||
if (res == MZ_FALSE)
|
if (res == MZ_FALSE)
|
||||||
std::runtime_error(std::string("Error while extracting a file from ") + path);
|
std::runtime_error(std::string("Error while extracting a file from ") + path);
|
||||||
|
|
|
@ -423,7 +423,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
|
||||||
|
|
||||||
print->set_started(psGCodeExport);
|
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.
|
// Remove the old g-code if it exists.
|
||||||
boost::nowide::remove(path);
|
boost::nowide::remove(path);
|
||||||
|
@ -435,9 +435,11 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
|
||||||
if (file == nullptr)
|
if (file == nullptr)
|
||||||
throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
|
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 {
|
try {
|
||||||
m_placeholder_parser_failed_templates.clear();
|
m_placeholder_parser_failed_templates.clear();
|
||||||
this->_do_export(*print, file, preview_data);
|
this->_do_export(*print, file);
|
||||||
fflush(file);
|
fflush(file);
|
||||||
if (ferror(file)) {
|
if (ferror(file)) {
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
@ -453,15 +455,6 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
|
||||||
}
|
}
|
||||||
fclose(file);
|
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()) {
|
if (! m_placeholder_parser_failed_templates.empty()) {
|
||||||
// G-code export proceeded, but some of the PlaceholderParser substitutions failed.
|
// 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";
|
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);
|
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)
|
if (rename_file(path_tmp, path) != 0)
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' +
|
std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' +
|
||||||
"Is " + path_tmp + " locked?" + '\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);
|
print->set_done(psGCodeExport);
|
||||||
|
|
||||||
// Write the profiler measurements to file
|
// 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());
|
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();
|
PROFILE_FUNC();
|
||||||
|
|
||||||
|
@ -558,7 +569,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||||
|
|
||||||
// resets analyzer
|
// resets analyzer
|
||||||
m_analyzer.reset();
|
m_analyzer.reset();
|
||||||
m_enable_analyzer = preview_data != nullptr;
|
|
||||||
|
|
||||||
// resets analyzer's tracking data
|
// resets analyzer's tracking data
|
||||||
m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm;
|
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);
|
_write(file, full_config);
|
||||||
}
|
}
|
||||||
print.throw_if_canceled();
|
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)
|
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)
|
||||||
|
@ -1644,6 +1648,11 @@ void GCode::process_layer(
|
||||||
// printf("G-code after filter:\n%s\n", out.c_str());
|
// printf("G-code after filter:\n%s\n", out.c_str());
|
||||||
|
|
||||||
_write(file, gcode);
|
_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)
|
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);
|
static void append_full_config(const Print& print, std::string& str);
|
||||||
|
|
||||||
protected:
|
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.
|
// Object and support extrusions of the same PrintObject at the same print_z.
|
||||||
struct LayerToPrint
|
struct LayerToPrint
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "../libslic3r.h"
|
#include "../libslic3r.h"
|
||||||
#include "../PrintConfig.hpp"
|
#include "../PrintConfig.hpp"
|
||||||
|
#include "../Utils.hpp"
|
||||||
#include "Print.hpp"
|
#include "Print.hpp"
|
||||||
|
|
||||||
#include "Analyzer.hpp"
|
#include "Analyzer.hpp"
|
||||||
|
@ -852,6 +853,16 @@ void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return an estimate of the memory consumed by the time estimator.
|
||||||
|
size_t GCodeAnalyzer::memory_used() const
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2)
|
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]),
|
return GCodePreviewData::Color(clamp(0.0f, 1.0f, c1.rgba[0] + c2.rgba[0]),
|
||||||
|
|
|
@ -120,6 +120,9 @@ public:
|
||||||
// Calculates all data needed for gcode visualization
|
// Calculates all data needed for gcode visualization
|
||||||
void calc_gcode_preview_data(GCodePreviewData& preview_data);
|
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);
|
static bool is_valid_extrusion_role(ExtrusionRole role);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "PreviewData.hpp"
|
#include "PreviewData.hpp"
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
#include <I18N.hpp>
|
#include <I18N.hpp>
|
||||||
|
#include "Utils.hpp"
|
||||||
|
|
||||||
#include <boost/format.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;
|
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_Width = 0.075f;
|
||||||
const float GCodePreviewData::Travel::Default_Height = 0.075f;
|
const float GCodePreviewData::Travel::Default_Height = 0.075f;
|
||||||
const GCodePreviewData::Color GCodePreviewData::Travel::Default_Type_Colors[Num_Types] =
|
const GCodePreviewData::Color GCodePreviewData::Travel::Default_Type_Colors[Num_Types] =
|
||||||
|
@ -224,6 +237,15 @@ void GCodePreviewData::Travel::set_default()
|
||||||
is_visible = false;
|
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);
|
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)
|
GCodePreviewData::Retraction::Position::Position(const Vec3crd& position, float width, float height)
|
||||||
|
@ -239,6 +261,11 @@ void GCodePreviewData::Retraction::set_default()
|
||||||
is_visible = false;
|
is_visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t GCodePreviewData::Retraction::memory_used() const
|
||||||
|
{
|
||||||
|
return sizeof(*this) + SLIC3R_STDVEC_MEMSIZE(this->positions, Position);
|
||||||
|
}
|
||||||
|
|
||||||
void GCodePreviewData::Shell::set_default()
|
void GCodePreviewData::Shell::set_default()
|
||||||
{
|
{
|
||||||
is_visible = false;
|
is_visible = false;
|
||||||
|
@ -483,4 +510,15 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
|
||||||
return items;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
|
@ -99,6 +99,9 @@ public:
|
||||||
void set_default();
|
void set_default();
|
||||||
bool is_role_flag_set(ExtrusionRole role) const;
|
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);
|
static bool is_role_flag_set(unsigned int flags, ExtrusionRole role);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -144,6 +147,9 @@ public:
|
||||||
size_t color_print_idx;
|
size_t color_print_idx;
|
||||||
|
|
||||||
void set_default();
|
void set_default();
|
||||||
|
|
||||||
|
// Return an estimate of the memory consumed by the time estimator.
|
||||||
|
size_t memory_used() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Retraction
|
struct Retraction
|
||||||
|
@ -166,6 +172,9 @@ public:
|
||||||
bool is_visible;
|
bool is_visible;
|
||||||
|
|
||||||
void set_default();
|
void set_default();
|
||||||
|
|
||||||
|
// Return an estimate of the memory consumed by the time estimator.
|
||||||
|
size_t memory_used() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Shell
|
struct Shell
|
||||||
|
@ -199,6 +208,9 @@ public:
|
||||||
|
|
||||||
std::string get_legend_title() const;
|
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;
|
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);
|
GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2);
|
||||||
|
|
|
@ -315,6 +315,20 @@ public:
|
||||||
return *this;
|
return *this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Let the firmware back up the active speed override value.
|
||||||
|
Writer& speed_override_backup()
|
||||||
|
{
|
||||||
|
m_gcode += "M220 B\n";
|
||||||
|
return *this;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Let the firmware restore the active speed override value.
|
||||||
|
Writer& speed_override_restore()
|
||||||
|
{
|
||||||
|
m_gcode += "M220 R\n";
|
||||||
|
return *this;
|
||||||
|
};
|
||||||
|
|
||||||
// Set digital trimpot motor
|
// Set digital trimpot motor
|
||||||
Writer& set_extruder_trimpot(int current)
|
Writer& set_extruder_trimpot(int current)
|
||||||
{
|
{
|
||||||
|
@ -473,7 +487,6 @@ WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *nam
|
||||||
return INVALID;
|
return INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Returns gcode to prime the nozzles at the front edge of the print bed.
|
// Returns gcode to prime the nozzles at the front edge of the print bed.
|
||||||
WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
||||||
// print_z of the first layer.
|
// print_z of the first layer.
|
||||||
|
@ -501,12 +514,15 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
||||||
.set_initial_tool(m_current_tool)
|
.set_initial_tool(m_current_tool)
|
||||||
.append(";--------------------\n"
|
.append(";--------------------\n"
|
||||||
"; CP PRIMING START\n")
|
"; CP PRIMING START\n")
|
||||||
.append(";--------------------\n")
|
.append(";--------------------\n");
|
||||||
.speed_override(100);
|
if (m_retain_speed_override)
|
||||||
|
writer.speed_override_backup();
|
||||||
|
writer.speed_override(100);
|
||||||
|
|
||||||
writer.set_initial_position(xy(0.f, 0.f)) // Always move to the starting position
|
writer.set_initial_position(xy(0.f, 0.f)) // Always move to the starting position
|
||||||
.travel(cleaning_box.ld, 7200)
|
.travel(cleaning_box.ld, 7200);
|
||||||
.set_extruder_trimpot(750); // Increase the extruder driver current to allow fast ramming.
|
if (m_set_extruder_trimpot)
|
||||||
|
writer.set_extruder_trimpot(750); // Increase the extruder driver current to allow fast ramming.
|
||||||
|
|
||||||
for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) {
|
for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) {
|
||||||
unsigned int tool = tools[idx_tool];
|
unsigned int tool = tools[idx_tool];
|
||||||
|
@ -533,8 +549,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
||||||
// in the output gcode - we should not remember emitting them (we will output them twice in the worst case)
|
// in the output gcode - we should not remember emitting them (we will output them twice in the worst case)
|
||||||
|
|
||||||
// Reset the extruder current to a normal value.
|
// Reset the extruder current to a normal value.
|
||||||
writer.set_extruder_trimpot(550)
|
if (m_set_extruder_trimpot)
|
||||||
.feedrate(6000)
|
writer.set_extruder_trimpot(550);
|
||||||
|
if (m_retain_speed_override)
|
||||||
|
writer.speed_override_restore();
|
||||||
|
writer.feedrate(6000)
|
||||||
.flush_planner_queue()
|
.flush_planner_queue()
|
||||||
.reset_extruder()
|
.reset_extruder()
|
||||||
.append("; CP PRIMING END\n"
|
.append("; CP PRIMING END\n"
|
||||||
|
@ -600,14 +619,17 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
||||||
"; CP TOOLCHANGE START\n")
|
"; CP TOOLCHANGE START\n")
|
||||||
.comment_with_value(" toolchange #", m_num_tool_changes + 1) // the number is zero-based
|
.comment_with_value(" toolchange #", m_num_tool_changes + 1) // the number is zero-based
|
||||||
.comment_material(m_filpar[m_current_tool].material)
|
.comment_material(m_filpar[m_current_tool].material)
|
||||||
.append(";--------------------\n")
|
.append(";--------------------\n");
|
||||||
.speed_override(100);
|
if (m_retain_speed_override)
|
||||||
|
writer.speed_override_backup();
|
||||||
|
writer.speed_override(100);
|
||||||
|
|
||||||
xy initial_position = cleaning_box.ld + WipeTower::xy(0.f,m_depth_traversed);
|
xy initial_position = cleaning_box.ld + WipeTower::xy(0.f,m_depth_traversed);
|
||||||
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
|
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
|
||||||
|
|
||||||
// Increase the extruder driver current to allow fast ramming.
|
// Increase the extruder driver current to allow fast ramming.
|
||||||
writer.set_extruder_trimpot(750);
|
if (m_set_extruder_trimpot)
|
||||||
|
writer.set_extruder_trimpot(550);
|
||||||
|
|
||||||
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
|
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
|
||||||
if (tool != (unsigned int)-1){ // This is not the last change.
|
if (tool != (unsigned int)-1){ // This is not the last change.
|
||||||
|
@ -635,8 +657,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.set_extruder_trimpot(550) // Reset the extruder current to a normal value.
|
if (m_set_extruder_trimpot)
|
||||||
.feedrate(6000)
|
writer.set_extruder_trimpot(550); // Reset the extruder current to a normal value.
|
||||||
|
if (m_retain_speed_override)
|
||||||
|
writer.speed_override_restore();
|
||||||
|
writer.feedrate(6000)
|
||||||
.flush_planner_queue()
|
.flush_planner_queue()
|
||||||
.reset_extruder()
|
.reset_extruder()
|
||||||
.append("; CP TOOLCHANGE END\n"
|
.append("; CP TOOLCHANGE END\n"
|
||||||
|
@ -881,14 +906,15 @@ void WipeTowerPrusaMM::toolchange_Change(
|
||||||
case FLEX: speed_override = 35; break;
|
case FLEX: speed_override = 35; break;
|
||||||
default: speed_override = 100;
|
default: speed_override = 100;
|
||||||
}
|
}
|
||||||
writer.set_tool(new_tool)
|
writer.set_tool(new_tool);
|
||||||
.speed_override(speed_override)
|
if (m_retain_speed_override)
|
||||||
.flush_planner_queue();
|
assert(speed_override == 100);
|
||||||
|
else
|
||||||
|
writer.speed_override(speed_override);
|
||||||
|
writer.flush_planner_queue();
|
||||||
m_current_tool = new_tool;
|
m_current_tool = new_tool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void WipeTowerPrusaMM::toolchange_Load(
|
void WipeTowerPrusaMM::toolchange_Load(
|
||||||
PrusaMultiMaterial::Writer &writer,
|
PrusaMultiMaterial::Writer &writer,
|
||||||
const box_coordinates &cleaning_box)
|
const box_coordinates &cleaning_box)
|
||||||
|
@ -916,12 +942,10 @@ void WipeTowerPrusaMM::toolchange_Load(
|
||||||
.resume_preview();
|
.resume_preview();
|
||||||
|
|
||||||
// Reset the extruder current to the normal value.
|
// Reset the extruder current to the normal value.
|
||||||
|
if (m_set_extruder_trimpot)
|
||||||
writer.set_extruder_trimpot(550);
|
writer.set_extruder_trimpot(550);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Wipe the newly loaded filament until the end of the assigned wipe area.
|
// Wipe the newly loaded filament until the end of the assigned wipe area.
|
||||||
void WipeTowerPrusaMM::toolchange_Wipe(
|
void WipeTowerPrusaMM::toolchange_Wipe(
|
||||||
PrusaMultiMaterial::Writer &writer,
|
PrusaMultiMaterial::Writer &writer,
|
||||||
|
|
|
@ -44,7 +44,8 @@ public:
|
||||||
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
|
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
|
||||||
// wipe_area -- space available for one toolchange in mm
|
// wipe_area -- space available for one toolchange in mm
|
||||||
WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
|
WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
|
||||||
float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, float bridging,
|
float cooling_tube_length, float parking_pos_retraction, float extra_loading_move,
|
||||||
|
float bridging, bool set_extruder_trimpot,
|
||||||
const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
|
const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
|
||||||
m_wipe_tower_pos(x, y),
|
m_wipe_tower_pos(x, y),
|
||||||
m_wipe_tower_width(width),
|
m_wipe_tower_width(width),
|
||||||
|
@ -57,6 +58,7 @@ public:
|
||||||
m_parking_pos_retraction(parking_pos_retraction),
|
m_parking_pos_retraction(parking_pos_retraction),
|
||||||
m_extra_loading_move(extra_loading_move),
|
m_extra_loading_move(extra_loading_move),
|
||||||
m_bridging(bridging),
|
m_bridging(bridging),
|
||||||
|
m_set_extruder_trimpot(set_extruder_trimpot),
|
||||||
m_current_tool(initial_tool),
|
m_current_tool(initial_tool),
|
||||||
wipe_volumes(wiping_matrix)
|
wipe_volumes(wiping_matrix)
|
||||||
{}
|
{}
|
||||||
|
@ -73,6 +75,11 @@ public:
|
||||||
m_filpar.push_back(FilamentParameters());
|
m_filpar.push_back(FilamentParameters());
|
||||||
|
|
||||||
m_filpar[idx].material = material;
|
m_filpar[idx].material = material;
|
||||||
|
if (material == FLEX || material == SCAFF || material == PVA) {
|
||||||
|
// MMU2 lowers the print speed using the speed override (M220) for printing of soluble PVA/BVOH and flex materials.
|
||||||
|
// Therefore it does not make sense to use the new M220 B and M220 R (backup / restore).
|
||||||
|
m_retain_speed_override = false;
|
||||||
|
}
|
||||||
m_filpar[idx].temperature = temp;
|
m_filpar[idx].temperature = temp;
|
||||||
m_filpar[idx].first_layer_temperature = first_layer_temp;
|
m_filpar[idx].first_layer_temperature = first_layer_temp;
|
||||||
m_filpar[idx].loading_speed = loading_speed;
|
m_filpar[idx].loading_speed = loading_speed;
|
||||||
|
@ -212,6 +219,8 @@ private:
|
||||||
float m_parking_pos_retraction = 0.f;
|
float m_parking_pos_retraction = 0.f;
|
||||||
float m_extra_loading_move = 0.f;
|
float m_extra_loading_move = 0.f;
|
||||||
float m_bridging = 0.f;
|
float m_bridging = 0.f;
|
||||||
|
bool m_set_extruder_trimpot = false;
|
||||||
|
bool m_retain_speed_override = true;
|
||||||
bool m_adhesion = true;
|
bool m_adhesion = true;
|
||||||
|
|
||||||
float m_perimeter_width = 0.4 * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
|
float m_perimeter_width = 0.4 * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
|
||||||
|
|
|
@ -290,6 +290,7 @@ namespace Slic3r {
|
||||||
// buffer line to export only when greater than 64K to reduce writing calls
|
// buffer line to export only when greater than 64K to reduce writing calls
|
||||||
std::string export_line;
|
std::string export_line;
|
||||||
char time_line[64];
|
char time_line[64];
|
||||||
|
G1LineIdToBlockIdMap::const_iterator it_line_id = _g1_line_ids.begin();
|
||||||
while (std::getline(in, gcode_line))
|
while (std::getline(in, gcode_line))
|
||||||
{
|
{
|
||||||
if (!in.good())
|
if (!in.good())
|
||||||
|
@ -310,32 +311,32 @@ namespace Slic3r {
|
||||||
|
|
||||||
// add remaining time lines where needed
|
// add remaining time lines where needed
|
||||||
_parser.parse_line(gcode_line,
|
_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"))
|
if (line.cmd_is("G1"))
|
||||||
{
|
{
|
||||||
++g1_lines_count;
|
++g1_lines_count;
|
||||||
|
|
||||||
if (!line.has_e())
|
assert(it_line_id == _g1_line_ids.end() || it_line_id->first >= g1_lines_count);
|
||||||
return;
|
|
||||||
|
|
||||||
G1LineIdToBlockIdMap::const_iterator it = _g1_line_ids.find(g1_lines_count);
|
const Block *block = nullptr;
|
||||||
if ((it != _g1_line_ids.end()) && (it->second < (unsigned int)_blocks.size()))
|
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())
|
||||||
const Block& block = _blocks[it->second];
|
block = &_blocks[it_line_id->second];
|
||||||
if (block.elapsed_time != -1.0f)
|
++it_line_id;
|
||||||
{
|
}
|
||||||
float block_remaining_time = _time - block.elapsed_time;
|
|
||||||
|
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)
|
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;
|
gcode_line += time_line;
|
||||||
|
|
||||||
last_recorded_time = block_remaining_time;
|
last_recorded_time = block_remaining_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export_line += gcode_line;
|
export_line += gcode_line;
|
||||||
|
@ -667,6 +668,15 @@ namespace Slic3r {
|
||||||
return _get_time_minutes(get_time());
|
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()
|
void GCodeTimeEstimator::_reset()
|
||||||
{
|
{
|
||||||
_curr.reset();
|
_curr.reset();
|
||||||
|
@ -1072,7 +1082,7 @@ namespace Slic3r {
|
||||||
|
|
||||||
// adds block to blocks list
|
// adds block to blocks list
|
||||||
_blocks.emplace_back(block);
|
_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)
|
void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line)
|
||||||
|
@ -1223,7 +1233,8 @@ namespace Slic3r {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate
|
// see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate
|
||||||
float factor = (dialect == gcfMarlin) ? 1.0f : MMMIN_TO_MMSEC;
|
// http://smoothieware.org/supported-g-codes
|
||||||
|
float factor = (dialect == gcfMarlin || dialect == gcfSmoothie) ? 1.0f : MMMIN_TO_MMSEC;
|
||||||
|
|
||||||
if (line.has_x())
|
if (line.has_x())
|
||||||
set_axis_max_feedrate(X, line.x() * factor);
|
set_axis_max_feedrate(X, line.x() * factor);
|
||||||
|
|
|
@ -209,7 +209,8 @@ namespace Slic3r {
|
||||||
typedef std::map<Block::EMoveType, MoveStats> MovesStatsMap;
|
typedef std::map<Block::EMoveType, MoveStats> MovesStatsMap;
|
||||||
#endif // ENABLE_MOVE_STATS
|
#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:
|
private:
|
||||||
EMode _mode;
|
EMode _mode;
|
||||||
|
@ -338,6 +339,9 @@ namespace Slic3r {
|
||||||
// Returns the estimated time, in minutes (integer)
|
// Returns the estimated time, in minutes (integer)
|
||||||
std::string get_time_minutes() const;
|
std::string get_time_minutes() const;
|
||||||
|
|
||||||
|
// Return an estimate of the memory consumed by the time estimator.
|
||||||
|
size_t memory_used() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _reset();
|
void _reset();
|
||||||
void _reset_time();
|
void _reset_time();
|
||||||
|
|
|
@ -17,6 +17,16 @@ Layer::~Layer()
|
||||||
m_regions.clear();
|
m_regions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test whether whether there are any slices assigned to this layer.
|
||||||
|
bool Layer::empty() const
|
||||||
|
{
|
||||||
|
for (const LayerRegion *layerm : m_regions)
|
||||||
|
if (layerm != nullptr && ! layerm->slices.empty())
|
||||||
|
// Non empty layer.
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
LayerRegion* Layer::add_region(PrintRegion* print_region)
|
LayerRegion* Layer::add_region(PrintRegion* print_region)
|
||||||
{
|
{
|
||||||
m_regions.emplace_back(new LayerRegion(this, print_region));
|
m_regions.emplace_back(new LayerRegion(this, print_region));
|
||||||
|
|
|
@ -114,7 +114,8 @@ public:
|
||||||
LayerRegion* get_region(int idx) { return m_regions[idx]; }
|
LayerRegion* get_region(int idx) { return m_regions[idx]; }
|
||||||
LayerRegion* add_region(PrintRegion* print_region);
|
LayerRegion* add_region(PrintRegion* print_region);
|
||||||
const LayerRegionPtrs& regions() const { return m_regions; }
|
const LayerRegionPtrs& regions() const { return m_regions; }
|
||||||
|
// Test whether whether there are any slices assigned to this layer.
|
||||||
|
bool empty() const;
|
||||||
void make_slices();
|
void make_slices();
|
||||||
void merge_slices();
|
void merge_slices();
|
||||||
template <class T> bool any_internal_region_slice_contains(const T &item) const {
|
template <class T> bool any_internal_region_slice_contains(const T &item) const {
|
||||||
|
|
|
@ -34,23 +34,22 @@ bool Line::intersection_infinite(const Line &other, Point* point) const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* distance to the closest point of line */
|
// Distance to the closest point of line.
|
||||||
double Line::distance_to(const Point &point) const
|
double Line::distance_to_squared(const Point &point, const Point &a, const Point &b)
|
||||||
{
|
{
|
||||||
const Line &line = *this;
|
const Vec2d v = (b - a).cast<double>();
|
||||||
const Vec2d v = (line.b - line.a).cast<double>();
|
const Vec2d va = (point - a).cast<double>();
|
||||||
const Vec2d va = (point - line.a).cast<double>();
|
|
||||||
const double l2 = v.squaredNorm(); // avoid a sqrt
|
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||||
if (l2 == 0.0)
|
if (l2 == 0.0)
|
||||||
// line.a == line.b case
|
// a == b case
|
||||||
return va.norm();
|
return va.squaredNorm();
|
||||||
// Consider the line extending the segment, parameterized as line.a + t (line.b - line.a).
|
// Consider the line extending the segment, parameterized as a + t (b - a).
|
||||||
// We find projection of this point onto the line.
|
// We find projection of this point onto the line.
|
||||||
// It falls where t = [(this-line.a) . (line.b-line.a)] / |line.b-line.a|^2
|
// It falls where t = [(this-a) . (b-a)] / |b-a|^2
|
||||||
const double t = va.dot(v) / l2;
|
const double t = va.dot(v) / l2;
|
||||||
if (t < 0.0) return va.norm(); // beyond the 'a' end of the segment
|
if (t < 0.0) return va.squaredNorm(); // beyond the 'a' end of the segment
|
||||||
else if (t > 1.0) return (point - line.b).cast<double>().norm(); // beyond the 'b' end of the segment
|
else if (t > 1.0) return (point - b).cast<double>().squaredNorm(); // beyond the 'b' end of the segment
|
||||||
return (t * v - va).norm();
|
return (t * v - va).squaredNorm();
|
||||||
}
|
}
|
||||||
|
|
||||||
double Line::perp_distance_to(const Point &point) const
|
double Line::perp_distance_to(const Point &point) const
|
||||||
|
|
|
@ -31,7 +31,8 @@ public:
|
||||||
Point midpoint() const { return (this->a + this->b) / 2; }
|
Point midpoint() const { return (this->a + this->b) / 2; }
|
||||||
bool intersection_infinite(const Line &other, Point* point) const;
|
bool intersection_infinite(const Line &other, Point* point) const;
|
||||||
bool operator==(const Line &rhs) const { return this->a == rhs.a && this->b == rhs.b; }
|
bool operator==(const Line &rhs) const { return this->a == rhs.a && this->b == rhs.b; }
|
||||||
double distance_to(const Point &point) const;
|
double distance_to_squared(const Point &point) const { return distance_to_squared(point, this->a, this->b); }
|
||||||
|
double distance_to(const Point &point) const { return distance_to(point, this->a, this->b); }
|
||||||
double perp_distance_to(const Point &point) const;
|
double perp_distance_to(const Point &point) const;
|
||||||
bool parallel_to(double angle) const;
|
bool parallel_to(double angle) const;
|
||||||
bool parallel_to(const Line &line) const { return this->parallel_to(line.direction()); }
|
bool parallel_to(const Line &line) const { return this->parallel_to(line.direction()); }
|
||||||
|
@ -43,6 +44,9 @@ public:
|
||||||
bool intersection(const Line& line, Point* intersection) const;
|
bool intersection(const Line& line, Point* intersection) const;
|
||||||
double ccw(const Point& point) const { return point.ccw(*this); }
|
double ccw(const Point& point) const { return point.ccw(*this); }
|
||||||
|
|
||||||
|
static double distance_to_squared(const Point &point, const Point &a, const Point &b);
|
||||||
|
static double distance_to(const Point &point, const Point &a, const Point &b) { return sqrt(distance_to_squared(point, a, b)); }
|
||||||
|
|
||||||
Point a;
|
Point a;
|
||||||
Point b;
|
Point b;
|
||||||
};
|
};
|
||||||
|
|
|
@ -809,6 +809,25 @@ TriangleMesh ModelObject::raw_mesh() const
|
||||||
return mesh;
|
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)
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
// A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
|
// 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.
|
// This bounding box is only used for the actual slicing.
|
||||||
BoundingBoxf3 ModelObject::raw_bounding_box() const
|
BoundingBoxf3 ModelObject::raw_bounding_box() const
|
||||||
|
@ -964,6 +983,16 @@ void ModelObject::mirror(Axis axis)
|
||||||
this->invalidate_bounding_box();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
size_t ModelObject::materials_count() const
|
size_t ModelObject::materials_count() const
|
||||||
{
|
{
|
||||||
std::set<t_model_material_id> material_ids;
|
std::set<t_model_material_id> material_ids;
|
||||||
|
@ -1495,6 +1524,12 @@ void ModelVolume::mirror(Axis axis)
|
||||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModelVolume::scale_geometry(const Vec3d& versor)
|
||||||
|
{
|
||||||
|
mesh.scale(versor);
|
||||||
|
m_convex_hull.scale(versor);
|
||||||
|
}
|
||||||
|
|
||||||
#if !ENABLE_MODELVOLUME_TRANSFORM
|
#if !ENABLE_MODELVOLUME_TRANSFORM
|
||||||
void ModelInstance::set_rotation(const Vec3d& rotation)
|
void ModelInstance::set_rotation(const Vec3d& rotation)
|
||||||
{
|
{
|
||||||
|
|
|
@ -218,6 +218,8 @@ public:
|
||||||
// Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
|
// 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.
|
// Currently used by ModelObject::mesh() and to calculate the 2D envelope for 2D platter.
|
||||||
TriangleMesh raw_mesh() const;
|
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.
|
// 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.
|
// This bounding box is only used for the actual slicing.
|
||||||
BoundingBoxf3 raw_bounding_box() const;
|
BoundingBoxf3 raw_bounding_box() const;
|
||||||
|
@ -235,6 +237,9 @@ public:
|
||||||
void rotate(double angle, Axis axis);
|
void rotate(double angle, Axis axis);
|
||||||
void rotate(double angle, const Vec3d& axis);
|
void rotate(double angle, const Vec3d& axis);
|
||||||
void mirror(Axis axis);
|
void mirror(Axis axis);
|
||||||
|
|
||||||
|
void scale_mesh(const Vec3d& versor);
|
||||||
|
|
||||||
size_t materials_count() const;
|
size_t materials_count() const;
|
||||||
size_t facets_count() const;
|
size_t facets_count() const;
|
||||||
bool needed_repair() const;
|
bool needed_repair() const;
|
||||||
|
@ -329,6 +334,8 @@ public:
|
||||||
void rotate(double angle, const Vec3d& axis);
|
void rotate(double angle, const Vec3d& axis);
|
||||||
void mirror(Axis axis);
|
void mirror(Axis axis);
|
||||||
|
|
||||||
|
void scale_geometry(const Vec3d& versor);
|
||||||
|
|
||||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||||
// translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box
|
// 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();
|
void center_geometry();
|
||||||
|
|
|
@ -162,45 +162,51 @@ bool MultiPoint::first_intersection(const Line& line, Point* intersection) const
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
//FIXME This is very inefficient in term of memory use.
|
std::vector<Point> MultiPoint::_douglas_peucker(const std::vector<Point>& pts, const double tolerance)
|
||||||
// The recursive algorithm shall run in place, not allocating temporary data in each recursion.
|
|
||||||
Points
|
|
||||||
MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
|
|
||||||
{
|
{
|
||||||
assert(points.size() >= 2);
|
std::vector<Point> result_pts;
|
||||||
Points results;
|
if (! pts.empty()) {
|
||||||
double dmax = 0;
|
const Point *anchor = &pts.front();
|
||||||
size_t index = 0;
|
size_t anchor_idx = 0;
|
||||||
Line full(points.front(), points.back());
|
const Point *floater = &pts.back();
|
||||||
for (Points::const_iterator it = points.begin() + 1; it != points.end(); ++it) {
|
size_t floater_idx = pts.size() - 1;
|
||||||
// we use shortest distance, not perpendicular distance
|
result_pts.reserve(pts.size());
|
||||||
double d = full.distance_to(*it);
|
result_pts.emplace_back(*anchor);
|
||||||
if (d > dmax) {
|
if (anchor_idx != floater_idx) {
|
||||||
index = it - points.begin();
|
assert(pts.size() > 1);
|
||||||
dmax = d;
|
std::vector<size_t> dpStack;
|
||||||
|
dpStack.reserve(pts.size());
|
||||||
|
dpStack.emplace_back(floater_idx);
|
||||||
|
for (;;) {
|
||||||
|
double max_distSq = 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;
|
||||||
|
furthest_idx = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dmax >= tolerance) {
|
// remove point if less than tolerance
|
||||||
Points dp0;
|
if (max_distSq <= tolerance) {
|
||||||
dp0.reserve(index + 1);
|
result_pts.emplace_back(*floater);
|
||||||
dp0.insert(dp0.end(), points.begin(), points.begin() + index + 1);
|
anchor_idx = floater_idx;
|
||||||
// Recursive call.
|
anchor = floater;
|
||||||
Points dp1 = MultiPoint::_douglas_peucker(dp0, tolerance);
|
assert(dpStack.back() == floater_idx);
|
||||||
results.reserve(results.size() + dp1.size() - 1);
|
dpStack.pop_back();
|
||||||
results.insert(results.end(), dp1.begin(), dp1.end() - 1);
|
if (dpStack.empty())
|
||||||
|
break;
|
||||||
dp0.clear();
|
floater_idx = dpStack.back();
|
||||||
dp0.reserve(points.size() - index);
|
|
||||||
dp0.insert(dp0.end(), points.begin() + index, points.end());
|
|
||||||
// Recursive call.
|
|
||||||
dp1 = MultiPoint::_douglas_peucker(dp0, tolerance);
|
|
||||||
results.reserve(results.size() + dp1.size());
|
|
||||||
results.insert(results.end(), dp1.begin(), dp1.end());
|
|
||||||
} else {
|
} else {
|
||||||
results.push_back(points.front());
|
floater_idx = furthest_idx;
|
||||||
results.push_back(points.back());
|
dpStack.emplace_back(floater_idx);
|
||||||
}
|
}
|
||||||
return results;
|
floater = &pts[floater_idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result_pts;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visivalingam simplification algorithm https://github.com/slic3r/Slic3r/pull/3825
|
// Visivalingam simplification algorithm https://github.com/slic3r/Slic3r/pull/3825
|
||||||
|
|
|
@ -8,13 +8,14 @@
|
||||||
#include "SupportMaterial.hpp"
|
#include "SupportMaterial.hpp"
|
||||||
#include "GCode.hpp"
|
#include "GCode.hpp"
|
||||||
#include "GCode/WipeTowerPrusaMM.hpp"
|
#include "GCode/WipeTowerPrusaMM.hpp"
|
||||||
#include <algorithm>
|
#include "Utils.hpp"
|
||||||
#include <unordered_set>
|
|
||||||
#include <boost/log/trivial.hpp>
|
|
||||||
|
|
||||||
#include "PrintExport.hpp"
|
#include "PrintExport.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <unordered_set>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
//! macro used to mark string used at localization,
|
//! macro used to mark string used at localization,
|
||||||
//! return same string
|
//! return same string
|
||||||
|
@ -213,6 +214,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
||||||
|| opt_key == "filament_cooling_final_speed"
|
|| opt_key == "filament_cooling_final_speed"
|
||||||
|| opt_key == "filament_ramming_parameters"
|
|| opt_key == "filament_ramming_parameters"
|
||||||
|| opt_key == "gcode_flavor"
|
|| opt_key == "gcode_flavor"
|
||||||
|
|| opt_key == "high_current_on_filament_swap"
|
||||||
|| opt_key == "infill_first"
|
|| opt_key == "infill_first"
|
||||||
|| opt_key == "single_extruder_multi_material"
|
|| opt_key == "single_extruder_multi_material"
|
||||||
|| opt_key == "spiral_vase"
|
|| opt_key == "spiral_vase"
|
||||||
|
@ -1051,10 +1053,12 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
||||||
goto print_object_end;
|
goto print_object_end;
|
||||||
} else {
|
} else {
|
||||||
this_region_config = region_config_from_model_volume(m_default_region_config, volume, num_extruders);
|
this_region_config = region_config_from_model_volume(m_default_region_config, volume, num_extruders);
|
||||||
for (size_t i = 0; i < region_id; ++ i)
|
for (size_t i = 0; i < region_id; ++i) {
|
||||||
if (m_regions[i]->config().equals(this_region_config))
|
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.
|
// Regions were merged. Reset this print_object.
|
||||||
goto print_object_end;
|
goto print_object_end;
|
||||||
|
}
|
||||||
this_region_config_set = true;
|
this_region_config_set = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1092,8 +1096,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
||||||
bool fresh = print_object.region_volumes.empty();
|
bool fresh = print_object.region_volumes.empty();
|
||||||
unsigned int volume_id = 0;
|
unsigned int volume_id = 0;
|
||||||
for (const ModelVolume *volume : model_object.volumes) {
|
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;
|
continue;
|
||||||
|
}
|
||||||
int region_id = -1;
|
int region_id = -1;
|
||||||
if (&print_object == &print_object0) {
|
if (&print_object == &print_object0) {
|
||||||
// Get the config applied to this volume.
|
// Get the config applied to this volume.
|
||||||
|
@ -1101,9 +1107,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
||||||
// Find an existing print region with the same config.
|
// Find an existing print region with the same config.
|
||||||
int idx_empty_slot = -1;
|
int idx_empty_slot = -1;
|
||||||
for (int i = 0; i < (int)m_regions.size(); ++ i) {
|
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;
|
idx_empty_slot = i;
|
||||||
else if (config.equals(m_regions[i]->config())) {
|
} else if (config.equals(m_regions[i]->config())) {
|
||||||
region_id = i;
|
region_id = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1469,7 +1476,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
|
||||||
// Slicing process, running at a background thread.
|
// Slicing process, running at a background thread.
|
||||||
void Print::process()
|
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)
|
for (PrintObject *obj : m_objects)
|
||||||
obj->make_perimeters();
|
obj->make_perimeters();
|
||||||
this->set_status(70, "Infilling layers");
|
this->set_status(70, "Infilling layers");
|
||||||
|
@ -1501,7 +1508,7 @@ void Print::process()
|
||||||
}
|
}
|
||||||
this->set_done(psWipeTower);
|
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.
|
// G-code export process, running at a background thread.
|
||||||
|
@ -1768,7 +1775,8 @@ void Print::_make_wipe_tower()
|
||||||
float(m_config.wipe_tower_width.value),
|
float(m_config.wipe_tower_width.value),
|
||||||
float(m_config.wipe_tower_rotation_angle.value), float(m_config.cooling_tube_retraction.value),
|
float(m_config.wipe_tower_rotation_angle.value), float(m_config.cooling_tube_retraction.value),
|
||||||
float(m_config.cooling_tube_length.value), float(m_config.parking_pos_retraction.value),
|
float(m_config.cooling_tube_length.value), float(m_config.parking_pos_retraction.value),
|
||||||
float(m_config.extra_loading_move.value), float(m_config.wipe_tower_bridging), wipe_volumes,
|
float(m_config.extra_loading_move.value), float(m_config.wipe_tower_bridging),
|
||||||
|
m_config.high_current_on_filament_swap.value, wipe_volumes,
|
||||||
m_wipe_tower_data.tool_ordering.first_extruder());
|
m_wipe_tower_data.tool_ordering.first_extruder());
|
||||||
|
|
||||||
//wipe_tower.set_retract();
|
//wipe_tower.set_retract();
|
||||||
|
|
|
@ -108,7 +108,7 @@ public:
|
||||||
void add_region_volume(unsigned int region_id, int volume_id) {
|
void add_region_volume(unsigned int region_id, int volume_id) {
|
||||||
if (region_id >= region_volumes.size())
|
if (region_id >= region_volumes.size())
|
||||||
region_volumes.resize(region_id + 1);
|
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 is the *total* layer count (including support layers)
|
||||||
// this value is not supposed to be compared with Layer::id
|
// this value is not supposed to be compared with Layer::id
|
||||||
|
|
|
@ -925,6 +925,15 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->mode = comExpert;
|
def->mode = comExpert;
|
||||||
def->default_value = new ConfigOptionEnum<GCodeFlavor>(gcfRepRap);
|
def->default_value = new ConfigOptionEnum<GCodeFlavor>(gcfRepRap);
|
||||||
|
|
||||||
|
def = this->add("high_current_on_filament_swap", coBool);
|
||||||
|
def->label = L("High extruder current on filament swap");
|
||||||
|
def->tooltip = L("It may be beneficial to increase the extruder motor current during the filament exchange"
|
||||||
|
" sequence to allow for rapid ramming feed rates and to overcome resistance when loading"
|
||||||
|
" a filament with an ugly shaped tip.");
|
||||||
|
def->cli = "high-current-on-filament-swap!";
|
||||||
|
def->mode = comExpert;
|
||||||
|
def->default_value = new ConfigOptionBool(0);
|
||||||
|
|
||||||
def = this->add("infill_acceleration", coFloat);
|
def = this->add("infill_acceleration", coFloat);
|
||||||
def->label = L("Infill");
|
def->label = L("Infill");
|
||||||
def->tooltip = L("This is the acceleration your printer will use for infill. Set zero to disable "
|
def->tooltip = L("This is the acceleration your printer will use for infill. Set zero to disable "
|
||||||
|
@ -2394,8 +2403,10 @@ void PrintConfigDef::init_sla_params()
|
||||||
def->tooltip = L("Display orientation");
|
def->tooltip = L("Display orientation");
|
||||||
def->cli = "display-orientation=s";
|
def->cli = "display-orientation=s";
|
||||||
def->enum_keys_map = &ConfigOptionEnum<SLADisplayOrientation>::get_enum_values();
|
def->enum_keys_map = &ConfigOptionEnum<SLADisplayOrientation>::get_enum_values();
|
||||||
def->enum_values.push_back("Landscape");
|
def->enum_values.push_back("landscape");
|
||||||
def->enum_values.push_back("Portrait");
|
def->enum_values.push_back("portrait");
|
||||||
|
def->enum_labels.push_back(L("Landscape"));
|
||||||
|
def->enum_labels.push_back(L("Portrait"));
|
||||||
def->default_value = new ConfigOptionEnum<SLADisplayOrientation>(sladoPortrait);
|
def->default_value = new ConfigOptionEnum<SLADisplayOrientation>(sladoPortrait);
|
||||||
|
|
||||||
def = this->add("printer_correction", coFloats);
|
def = this->add("printer_correction", coFloats);
|
||||||
|
|
|
@ -36,7 +36,7 @@ enum GCodeFlavor {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum PrintHostType {
|
enum PrintHostType {
|
||||||
htOctoPrint, htDuet,
|
htOctoPrint, htDuet, htSL1,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum InfillPattern {
|
enum InfillPattern {
|
||||||
|
@ -155,8 +155,8 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<FilamentType>::ge
|
||||||
|
|
||||||
template<> inline const t_config_enum_values& ConfigOptionEnum<SLADisplayOrientation>::get_enum_values() {
|
template<> inline const t_config_enum_values& ConfigOptionEnum<SLADisplayOrientation>::get_enum_values() {
|
||||||
static const t_config_enum_values keys_map = {
|
static const t_config_enum_values keys_map = {
|
||||||
{ "Landscape", sladoLandscape},
|
{ "landscape", sladoLandscape},
|
||||||
{ "Portrait", sladoPortrait}
|
{ "portrait", sladoPortrait}
|
||||||
};
|
};
|
||||||
|
|
||||||
return keys_map;
|
return keys_map;
|
||||||
|
@ -627,6 +627,7 @@ public:
|
||||||
ConfigOptionBool variable_layer_height;
|
ConfigOptionBool variable_layer_height;
|
||||||
ConfigOptionFloat cooling_tube_retraction;
|
ConfigOptionFloat cooling_tube_retraction;
|
||||||
ConfigOptionFloat cooling_tube_length;
|
ConfigOptionFloat cooling_tube_length;
|
||||||
|
ConfigOptionBool high_current_on_filament_swap;
|
||||||
ConfigOptionFloat parking_pos_retraction;
|
ConfigOptionFloat parking_pos_retraction;
|
||||||
ConfigOptionBool remaining_times;
|
ConfigOptionBool remaining_times;
|
||||||
ConfigOptionBool silent_mode;
|
ConfigOptionBool silent_mode;
|
||||||
|
@ -695,6 +696,7 @@ protected:
|
||||||
OPT_PTR(variable_layer_height);
|
OPT_PTR(variable_layer_height);
|
||||||
OPT_PTR(cooling_tube_retraction);
|
OPT_PTR(cooling_tube_retraction);
|
||||||
OPT_PTR(cooling_tube_length);
|
OPT_PTR(cooling_tube_length);
|
||||||
|
OPT_PTR(high_current_on_filament_swap);
|
||||||
OPT_PTR(parking_pos_retraction);
|
OPT_PTR(parking_pos_retraction);
|
||||||
OPT_PTR(remaining_times);
|
OPT_PTR(remaining_times);
|
||||||
OPT_PTR(silent_mode);
|
OPT_PTR(silent_mode);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "SupportMaterial.hpp"
|
#include "SupportMaterial.hpp"
|
||||||
#include "Surface.hpp"
|
#include "Surface.hpp"
|
||||||
#include "Slicing.hpp"
|
#include "Slicing.hpp"
|
||||||
|
#include "Utils.hpp"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
@ -132,7 +133,7 @@ void PrintObject::make_perimeters()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_print->set_status(20, "Generating perimeters");
|
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
|
// merge slices if they were split into types
|
||||||
if (this->typed_slices) {
|
if (this->typed_slices) {
|
||||||
|
@ -253,7 +254,7 @@ void PrintObject::prepare_infill()
|
||||||
// Decide what surfaces are to be filled.
|
// 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.
|
// 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.
|
// 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 *layer : m_layers)
|
||||||
for (auto *region : layer->m_regions) {
|
for (auto *region : layer->m_regions) {
|
||||||
region->prepare_fill_surfaces();
|
region->prepare_fill_surfaces();
|
||||||
|
@ -384,6 +385,14 @@ void PrintObject::generate_support_material()
|
||||||
m_print->set_status(85, "Generating support material");
|
m_print->set_status(85, "Generating support material");
|
||||||
this->_generate_support_material();
|
this->_generate_support_material();
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
|
} else {
|
||||||
|
#if 0
|
||||||
|
// Printing without supports. Empty layer means some objects or object parts are levitating,
|
||||||
|
// therefore they cannot be printed without supports.
|
||||||
|
for (const Layer *layer : m_layers)
|
||||||
|
if (layer->empty())
|
||||||
|
throw std::runtime_error("Levitating objects cannot be printed without supports.");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
this->set_done(posSupportMaterial);
|
this->set_done(posSupportMaterial);
|
||||||
}
|
}
|
||||||
|
@ -522,11 +531,13 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
||||||
|| opt_key == "perimeter_speed"
|
|| opt_key == "perimeter_speed"
|
||||||
|| opt_key == "small_perimeter_speed"
|
|| opt_key == "small_perimeter_speed"
|
||||||
|| opt_key == "solid_infill_speed"
|
|| opt_key == "solid_infill_speed"
|
||||||
|| opt_key == "top_solid_infill_speed"
|
|| opt_key == "top_solid_infill_speed") {
|
||||||
|| opt_key == "wipe_into_infill" // when these these two are changed, we only need to invalidate the wipe tower,
|
invalidated |= m_print->invalidate_step(psGCodeExport);
|
||||||
|| opt_key == "wipe_into_objects" // which we already did at the very beginning - nothing more to be done
|
} else if (
|
||||||
) {
|
opt_key == "wipe_into_infill"
|
||||||
// these options only affect G-code export, so nothing to invalidate
|
|| opt_key == "wipe_into_objects") {
|
||||||
|
invalidated |= m_print->invalidate_step(psWipeTower);
|
||||||
|
invalidated |= m_print->invalidate_step(psGCodeExport);
|
||||||
} else {
|
} else {
|
||||||
// for legacy, if we can't handle this option let's invalidate all steps
|
// for legacy, if we can't handle this option let's invalidate all steps
|
||||||
this->invalidate_all_steps();
|
this->invalidate_all_steps();
|
||||||
|
@ -547,15 +558,15 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
|
||||||
|
|
||||||
// propagate to dependent steps
|
// propagate to dependent steps
|
||||||
if (step == posPerimeters) {
|
if (step == posPerimeters) {
|
||||||
invalidated |= this->invalidate_step(posPrepareInfill);
|
invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill });
|
||||||
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
||||||
} else if (step == posPrepareInfill) {
|
} else if (step == posPrepareInfill) {
|
||||||
invalidated |= this->invalidate_step(posInfill);
|
invalidated |= this->invalidate_step(posInfill);
|
||||||
} else if (step == posInfill) {
|
} else if (step == posInfill) {
|
||||||
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
||||||
} else if (step == posSlice) {
|
} else if (step == posSlice) {
|
||||||
invalidated |= this->invalidate_steps({ posPerimeters, posSupportMaterial });
|
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posSupportMaterial });
|
||||||
invalidated |= m_print->invalidate_step(psWipeTower);
|
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
||||||
} else if (step == posSupportMaterial)
|
} else if (step == posSupportMaterial)
|
||||||
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
||||||
|
|
||||||
|
@ -591,7 +602,7 @@ bool PrintObject::has_support_material() const
|
||||||
// If a part of a region is of stBottom and stTop, the stBottom wins.
|
// If a part of a region is of stBottom and stTop, the stBottom wins.
|
||||||
void PrintObject::detect_surfaces_type()
|
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.
|
// 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
|
// Each of the objects will have a full number of top / bottom layers, even if these top / bottom layers
|
||||||
|
@ -783,7 +794,7 @@ void PrintObject::detect_surfaces_type()
|
||||||
|
|
||||||
void PrintObject::process_external_surfaces()
|
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) {
|
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) {
|
||||||
const PrintRegion ®ion = *m_print->regions()[region_id];
|
const PrintRegion ®ion = *m_print->regions()[region_id];
|
||||||
|
@ -808,7 +819,7 @@ void PrintObject::discover_vertical_shells()
|
||||||
{
|
{
|
||||||
PROFILE_FUNC();
|
PROFILE_FUNC();
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells...";
|
BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells..." << log_memory_info();
|
||||||
|
|
||||||
struct DiscoverVerticalShellsCacheEntry
|
struct DiscoverVerticalShellsCacheEntry
|
||||||
{
|
{
|
||||||
|
@ -1192,7 +1203,7 @@ void PrintObject::discover_vertical_shells()
|
||||||
sparse infill */
|
sparse infill */
|
||||||
void PrintObject::bridge_over_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) {
|
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
||||||
const PrintRegion ®ion = *m_print->regions()[region_id];
|
const PrintRegion ®ion = *m_print->regions()[region_id];
|
||||||
|
@ -1377,7 +1388,7 @@ bool PrintObject::update_layer_height_profile()
|
||||||
// this should be idempotent
|
// this should be idempotent
|
||||||
void PrintObject::_slice()
|
void PrintObject::_slice()
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(info) << "Slicing objects...";
|
BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info();
|
||||||
|
|
||||||
this->typed_slices = false;
|
this->typed_slices = false;
|
||||||
|
|
||||||
|
@ -1463,9 +1474,7 @@ void PrintObject::_slice()
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - removing top empty layers";
|
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - removing top empty layers";
|
||||||
while (! m_layers.empty()) {
|
while (! m_layers.empty()) {
|
||||||
const Layer *layer = m_layers.back();
|
const Layer *layer = m_layers.back();
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
|
if (! layer->empty())
|
||||||
if (layer->m_regions[region_id] != nullptr && ! layer->m_regions[region_id]->slices.empty())
|
|
||||||
// Non empty layer.
|
|
||||||
goto end;
|
goto end;
|
||||||
delete layer;
|
delete layer;
|
||||||
m_layers.pop_back();
|
m_layers.pop_back();
|
||||||
|
@ -1701,7 +1710,7 @@ void PrintObject::_make_perimeters()
|
||||||
if (! this->set_started(posPerimeters))
|
if (! this->set_started(posPerimeters))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "Generating perimeters...";
|
BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info();
|
||||||
|
|
||||||
// merge slices if they were split into types
|
// merge slices if they were split into types
|
||||||
if (this->typed_slices) {
|
if (this->typed_slices) {
|
||||||
|
|
|
@ -612,7 +612,9 @@ double ray_mesh_intersect(const Vec3d& s,
|
||||||
const Vec3d& dir,
|
const Vec3d& dir,
|
||||||
const EigenMesh3D& m);
|
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) {
|
inline Vec2d to_vec2(const Vec3d& v3) {
|
||||||
return {v3(X), v3(Y)};
|
return {v3(X), v3(Y)};
|
||||||
|
@ -1049,7 +1051,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
tifcl();
|
tifcl();
|
||||||
|
|
||||||
// calculate the normals to the triangles belonging to filtered points
|
// 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_norm.resize(count, 3);
|
||||||
head_pos.resize(count, 3);
|
head_pos.resize(count, 3);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <cmath>
|
||||||
#include "SLA/SLASupportTree.hpp"
|
#include "SLA/SLASupportTree.hpp"
|
||||||
#include "SLA/SLABoilerPlate.hpp"
|
#include "SLA/SLABoilerPlate.hpp"
|
||||||
#include "SLA/SLASpatIndex.hpp"
|
#include "SLA/SLASpatIndex.hpp"
|
||||||
|
@ -9,15 +10,8 @@
|
||||||
#include "boost/geometry/index/rtree.hpp"
|
#include "boost/geometry/index/rtree.hpp"
|
||||||
|
|
||||||
#include <igl/ray_mesh_intersect.h>
|
#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>
|
#include <igl/point_mesh_squared_distance.h>
|
||||||
#endif
|
#include <igl/remove_duplicate_vertices.h>
|
||||||
|
|
||||||
#include "SLASpatIndex.hpp"
|
#include "SLASpatIndex.hpp"
|
||||||
#include "ClipperUtils.hpp"
|
#include "ClipperUtils.hpp"
|
||||||
|
@ -84,33 +78,124 @@ size_t SpatIndex::size() const
|
||||||
return m_impl->m_store.size();
|
return m_impl->m_store.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
PointSet normals(const PointSet& points, const EigenMesh3D& mesh) {
|
bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2,
|
||||||
if(points.rows() == 0 || mesh.V.rows() == 0 || mesh.F.rows() == 0) return {};
|
double eps = 0.05)
|
||||||
#ifdef IGL_COMPATIBLE
|
{
|
||||||
|
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::VectorXd dists;
|
||||||
Eigen::VectorXi I;
|
Eigen::VectorXi I;
|
||||||
PointSet C;
|
PointSet C;
|
||||||
|
|
||||||
|
// We need to remove duplicate vertices and have a true index triangle
|
||||||
|
// structure
|
||||||
|
EigenMesh3D mesh;
|
||||||
|
Eigen::VectorXi SVI, SVJ;
|
||||||
|
igl::remove_duplicate_vertices(emesh.V, emesh.F, 1e-6,
|
||||||
|
mesh.V, SVI, SVJ, mesh.F);
|
||||||
|
|
||||||
igl::point_mesh_squared_distance( points, mesh.V, mesh.F, dists, I, C);
|
igl::point_mesh_squared_distance( points, mesh.V, mesh.F, dists, I, C);
|
||||||
|
|
||||||
PointSet ret(I.rows(), 3);
|
PointSet ret(I.rows(), 3);
|
||||||
for(int i = 0; i < I.rows(); i++) {
|
for(int i = 0; i < I.rows(); i++) {
|
||||||
|
throw_on_cancel();
|
||||||
auto idx = I(i);
|
auto idx = I(i);
|
||||||
auto trindex = mesh.F.row(idx);
|
auto trindex = mesh.F.row(idx);
|
||||||
|
|
||||||
auto& p1 = mesh.V.row(trindex(0));
|
const Vec3d& p1 = mesh.V.row(trindex(0));
|
||||||
auto& p2 = mesh.V.row(trindex(1));
|
const Vec3d& p2 = mesh.V.row(trindex(1));
|
||||||
auto& p3 = mesh.V.row(trindex(2));
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!neigh.empty()) { // there were neighbors to count with
|
||||||
|
Vec3d sumnorm(0, 0, 0);
|
||||||
|
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;
|
||||||
|
sumnorm += U.cross(V).normalized();
|
||||||
|
}
|
||||||
|
sumnorm /= neigh.size();
|
||||||
|
ret.row(i) = sumnorm;
|
||||||
|
}
|
||||||
|
else { // point lies safely within its triangle
|
||||||
Eigen::Vector3d U = p2 - p1;
|
Eigen::Vector3d U = p2 - p1;
|
||||||
Eigen::Vector3d V = p3 - p1;
|
Eigen::Vector3d V = p3 - p1;
|
||||||
ret.row(i) = U.cross(V).normalized();
|
ret.row(i) = U.cross(V).normalized();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
#else // TODO: do something on 32 bit windows
|
|
||||||
return {};
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double ray_mesh_intersect(const Vec3d& s,
|
double ray_mesh_intersect(const Vec3d& s,
|
||||||
|
@ -223,7 +308,7 @@ Segments model_boundary(const EigenMesh3D& emesh, double offs)
|
||||||
pp.emplace_back(p);
|
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) {
|
for(auto& expoly : merged) {
|
||||||
auto lines = expoly.lines();
|
auto lines = expoly.lines();
|
||||||
|
|
|
@ -436,10 +436,17 @@ std::vector<float> SLAPrint::calculate_heights(const BoundingBoxf3& bb3d, float
|
||||||
return heights;
|
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()
|
void SLAPrint::process()
|
||||||
{
|
{
|
||||||
using namespace sla;
|
using namespace sla;
|
||||||
|
using ExPolygon = Slic3r::ExPolygon;
|
||||||
|
|
||||||
// Assumption: at this point the print objects should be populated only with
|
// Assumption: at this point the print objects should be populated only with
|
||||||
// the model objects we have to process and the instances are also filtered
|
// the model objects we have to process and the instances are also filtered
|
||||||
|
@ -556,9 +563,11 @@ void SLAPrint::process()
|
||||||
// scaling for the sub operations
|
// scaling for the sub operations
|
||||||
double d = *stthis / (objcount * 100.0);
|
double d = *stthis / (objcount * 100.0);
|
||||||
|
|
||||||
ctl.statuscb = [this, init, d](unsigned st, const std::string& msg){
|
ctl.statuscb = [this, init, d](unsigned st, const std::string& msg)
|
||||||
set_status(int(init + st*d), msg);
|
{
|
||||||
|
report_status(*this, int(init + st*d), msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
ctl.stopcondition = [this](){ return canceled(); };
|
ctl.stopcondition = [this](){ return canceled(); };
|
||||||
ctl.cancelfn = [this]() { throw_if_canceled(); };
|
ctl.cancelfn = [this]() { throw_if_canceled(); };
|
||||||
|
|
||||||
|
@ -568,9 +577,9 @@ void SLAPrint::process()
|
||||||
|
|
||||||
// Create the unified mesh
|
// Create the unified mesh
|
||||||
auto rc = SlicingStatus::RELOAD_SCENE;
|
auto rc = SlicingStatus::RELOAD_SCENE;
|
||||||
set_status(-1, L("Visualizing supports"));
|
report_status(*this, -1, L("Visualizing supports"));
|
||||||
po.m_supportdata->support_tree_ptr->merged_mesh();
|
po.m_supportdata->support_tree_ptr->merged_mesh();
|
||||||
set_status(-1, L("Visualizing supports"), rc);
|
report_status(*this, -1, L("Visualizing supports"), rc);
|
||||||
} catch(sla::SLASupportsStoppedException&) {
|
} catch(sla::SLASupportsStoppedException&) {
|
||||||
// no need to rethrow
|
// no need to rethrow
|
||||||
// throw_if_canceled();
|
// throw_if_canceled();
|
||||||
|
@ -613,7 +622,7 @@ void SLAPrint::process()
|
||||||
|
|
||||||
po.throw_if_canceled();
|
po.throw_if_canceled();
|
||||||
auto rc = SlicingStatus::RELOAD_SCENE;
|
auto rc = SlicingStatus::RELOAD_SCENE;
|
||||||
set_status(-1, L("Visualizing supports"), rc);
|
report_status(*this, -1, L("Visualizing supports"), rc);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Slicing the support geometries similarly to the model slicing procedure.
|
// Slicing the support geometries similarly to the model slicing procedure.
|
||||||
|
@ -772,8 +781,12 @@ void SLAPrint::process()
|
||||||
auto lvlcnt = unsigned(m_printer_input.size());
|
auto lvlcnt = unsigned(m_printer_input.size());
|
||||||
printer.layers(lvlcnt);
|
printer.layers(lvlcnt);
|
||||||
|
|
||||||
|
// slot is the portion of 100% that is realted to rasterization
|
||||||
unsigned slot = PRINT_STEP_LEVELS[slapsRasterize];
|
unsigned slot = PRINT_STEP_LEVELS[slapsRasterize];
|
||||||
|
// ist: initial state; pst: previous state
|
||||||
unsigned ist = max_objstatus, pst = ist;
|
unsigned ist = max_objstatus, pst = ist;
|
||||||
|
// coefficient to map the rasterization state (0-99) to the allocated
|
||||||
|
// portion (slot) of the process state
|
||||||
double sd = (100 - ist) / 100.0;
|
double sd = (100 - ist) / 100.0;
|
||||||
SpinMutex slck;
|
SpinMutex slck;
|
||||||
|
|
||||||
|
@ -810,11 +823,11 @@ void SLAPrint::process()
|
||||||
// Finish the layer for later saving it.
|
// Finish the layer for later saving it.
|
||||||
printer.finish_layer(level_id);
|
printer.finish_layer(level_id);
|
||||||
|
|
||||||
// Status indication
|
// Status indication guarded with the spinlock
|
||||||
auto st = ist + unsigned(sd*level_id*slot/m_printer_input.size());
|
auto st = ist + unsigned(sd*level_id*slot/m_printer_input.size());
|
||||||
{ std::lock_guard<SpinMutex> lck(slck);
|
{ std::lock_guard<SpinMutex> lck(slck);
|
||||||
if( st > pst) {
|
if( st > pst) {
|
||||||
set_status(int(st), PRINT_STEP_LABELS[slapsRasterize]);
|
report_status(*this, int(st), PRINT_STEP_LABELS[slapsRasterize]);
|
||||||
pst = st;
|
pst = st;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -862,9 +875,14 @@ void SLAPrint::process()
|
||||||
unsigned st = min_objstatus;
|
unsigned st = min_objstatus;
|
||||||
unsigned incr = 0;
|
unsigned incr = 0;
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "Start slicing process.";
|
||||||
|
|
||||||
// TODO: this loop could run in parallel but should not exhaust all the CPU
|
// TODO: this loop could run in parallel but should not exhaust all the CPU
|
||||||
// power available
|
// power available
|
||||||
for(SLAPrintObject * po : m_objects) {
|
for(SLAPrintObject * po : m_objects) {
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "Slicing object " << po->model_object()->name;
|
||||||
|
|
||||||
for(size_t s = 0; s < objectsteps.size(); ++s) {
|
for(size_t s = 0; s < objectsteps.size(); ++s) {
|
||||||
auto currentstep = objectsteps[s];
|
auto currentstep = objectsteps[s];
|
||||||
|
|
||||||
|
@ -876,8 +894,7 @@ void SLAPrint::process()
|
||||||
st += unsigned(incr * ostepd);
|
st += unsigned(incr * ostepd);
|
||||||
|
|
||||||
if(po->m_stepmask[currentstep] && po->set_started(currentstep)) {
|
if(po->m_stepmask[currentstep] && po->set_started(currentstep)) {
|
||||||
|
report_status(*this, int(st), OBJ_STEP_LABELS[currentstep]);
|
||||||
set_status(int(st), OBJ_STEP_LABELS[currentstep]);
|
|
||||||
pobj_program[currentstep](*po);
|
pobj_program[currentstep](*po);
|
||||||
po->set_done(currentstep);
|
po->set_done(currentstep);
|
||||||
}
|
}
|
||||||
|
@ -902,7 +919,7 @@ void SLAPrint::process()
|
||||||
|
|
||||||
if(m_stepmask[currentstep] && set_started(currentstep))
|
if(m_stepmask[currentstep] && set_started(currentstep))
|
||||||
{
|
{
|
||||||
set_status(int(st), PRINT_STEP_LABELS[currentstep]);
|
report_status(*this, int(st), PRINT_STEP_LABELS[currentstep]);
|
||||||
print_program[currentstep]();
|
print_program[currentstep]();
|
||||||
set_done(currentstep);
|
set_done(currentstep);
|
||||||
}
|
}
|
||||||
|
@ -911,7 +928,7 @@ void SLAPrint::process()
|
||||||
}
|
}
|
||||||
|
|
||||||
// If everything vent well
|
// If everything vent well
|
||||||
set_status(100, L("Slicing done"));
|
report_status(*this, 100, L("Slicing done"));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys)
|
bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys)
|
||||||
|
|
|
@ -36,6 +36,12 @@
|
||||||
#define ENABLE_REMOVE_TABS_FROM_PLATER (1 && ENABLE_1_42_0)
|
#define ENABLE_REMOVE_TABS_FROM_PLATER (1 && ENABLE_1_42_0)
|
||||||
// Constrains the camera target into the scene bounding box
|
// Constrains the camera target into the scene bounding box
|
||||||
#define ENABLE_CONSTRAINED_CAMERA_TARGET (1 && ENABLE_1_42_0)
|
#define ENABLE_CONSTRAINED_CAMERA_TARGET (1 && ENABLE_1_42_0)
|
||||||
|
// 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)
|
||||||
|
// 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)
|
||||||
|
|
||||||
#endif // _technologies_h_
|
#endif // _technologies_h_
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,13 @@
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
extern void set_logging_level(unsigned int level);
|
extern void set_logging_level(unsigned int level);
|
||||||
|
extern unsigned get_logging_level();
|
||||||
extern void trace(unsigned int level, const char *message);
|
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();
|
extern void disable_multi_threading();
|
||||||
|
|
||||||
// Set a path with GUI resource files.
|
// Set a path with GUI resource files.
|
||||||
|
@ -182,7 +188,12 @@ public:
|
||||||
void reset() { closure = Closure(); }
|
void reset() { closure = Closure(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // 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_
|
#endif // slic3r_Utils_hpp_
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include <psapi.h>
|
||||||
#else
|
#else
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#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.
|
// Force set_logging_level(<=error) after loading of the DLL.
|
||||||
// Switch boost::filesystem to utf8.
|
// Switch boost::filesystem to utf8.
|
||||||
static struct RunOnInit {
|
static struct RunOnInit {
|
||||||
|
@ -365,4 +378,71 @@ std::string xml_escape(std::string text)
|
||||||
return 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
|
}; // namespace Slic3r
|
||||||
|
|
|
@ -65,11 +65,6 @@ PrinterTechnology BackgroundSlicingProcess::current_printer_technology() const
|
||||||
return m_print->technology();
|
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
|
// This function may one day be merged into the Print, but historically the print was separated
|
||||||
// from the G-code generator.
|
// from the G-code generator.
|
||||||
void BackgroundSlicingProcess::process_fff()
|
void BackgroundSlicingProcess::process_fff()
|
||||||
|
@ -88,6 +83,27 @@ void BackgroundSlicingProcess::process_fff()
|
||||||
m_print->set_status(95, "Running post-processing scripts");
|
m_print->set_status(95, "Running post-processing scripts");
|
||||||
run_post_process_scripts(export_path, m_fff_print->config());
|
run_post_process_scripts(export_path, m_fff_print->config());
|
||||||
m_print->set_status(100, "G-code file exported to " + export_path);
|
m_print->set_status(100, "G-code file exported to " + export_path);
|
||||||
|
} else if (! m_upload_job.empty()) {
|
||||||
|
// A print host upload job has been scheduled
|
||||||
|
|
||||||
|
// XXX: is fs::path::string() right?
|
||||||
|
|
||||||
|
// Generate a unique temp path to which the gcode is copied
|
||||||
|
boost::filesystem::path source_path = boost::filesystem::temp_directory_path()
|
||||||
|
/ boost::filesystem::unique_path(".printhost.%%%%-%%%%-%%%%-%%%%.gcode");
|
||||||
|
|
||||||
|
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_print->set_status(95, "Running post-processing scripts");
|
||||||
|
run_post_process_scripts(source_path.string(), m_fff_print->config());
|
||||||
|
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);
|
||||||
|
m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
|
||||||
|
|
||||||
|
GUI::wxGetApp().printhost_job_queue().enqueue(std::move(m_upload_job));
|
||||||
} else {
|
} else {
|
||||||
m_print->set_status(100, "Slicing complete");
|
m_print->set_status(100, "Slicing complete");
|
||||||
}
|
}
|
||||||
|
@ -373,13 +389,10 @@ void BackgroundSlicingProcess::schedule_upload(Slic3r::PrintHostJob upload_job)
|
||||||
if (! m_export_path.empty())
|
if (! m_export_path.empty())
|
||||||
return;
|
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.
|
// Guard against entering the export step before changing the export path.
|
||||||
tbb::mutex::scoped_lock lock(m_print->state_mutex());
|
tbb::mutex::scoped_lock lock(m_print->state_mutex());
|
||||||
this->invalidate_step(bspsGCodeFinalize);
|
this->invalidate_step(bspsGCodeFinalize);
|
||||||
m_export_path = path.string();
|
m_export_path = std::string();
|
||||||
m_upload_job = std::move(upload_job);
|
m_upload_job = std::move(upload_job);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -163,17 +163,40 @@ void Field::get_value_by_opt_type(wxString& str)
|
||||||
break; }
|
break; }
|
||||||
case coString:
|
case coString:
|
||||||
case coStrings:
|
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 (val > 1)
|
||||||
|
{
|
||||||
|
const int nVal = int(val);
|
||||||
|
wxString msg_text = wxString::Format(_(L("Do you mean %d%% instead of %dmm?\n"
|
||||||
|
"Select YES if you want to change this value to %d%%, \n"
|
||||||
|
"or NO if you are sure that %dmm is a correct value.")), nVal, nVal, nVal, nVal);
|
||||||
|
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();
|
m_value = str.ToStdString();
|
||||||
break;
|
break; }
|
||||||
default:
|
default:
|
||||||
break;
|
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 false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -252,7 +275,7 @@ void TextCtrl::BUILD() {
|
||||||
temp->GetToolTip()->Enable(true);
|
temp->GetToolTip()->Enable(true);
|
||||||
#endif // __WXGTK__
|
#endif // __WXGTK__
|
||||||
// if (!is_defined_input_value())
|
// if (!is_defined_input_value())
|
||||||
if (is_defined_input_value())
|
if (is_defined_input_value<wxTextCtrl>(window, m_opt.type))
|
||||||
on_change_field();
|
on_change_field();
|
||||||
else
|
else
|
||||||
on_kill_focus(e);
|
on_kill_focus(e);
|
||||||
|
@ -377,6 +400,9 @@ void SpinCtrl::BUILD() {
|
||||||
0, min_val, max_val, default_value);
|
0, min_val, max_val, default_value);
|
||||||
|
|
||||||
// temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { tmp_value = undef_spin_val; on_change_field(); }), temp->GetId());
|
// temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { tmp_value = undef_spin_val; on_change_field(); }), temp->GetId());
|
||||||
|
|
||||||
|
// #ys_FIXME_KILL_FOCUS
|
||||||
|
// wxEVT_KILL_FOCUS doesn't handled on OSX now
|
||||||
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e)
|
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e)
|
||||||
{
|
{
|
||||||
if (tmp_value < 0)
|
if (tmp_value < 0)
|
||||||
|
@ -386,6 +412,7 @@ void SpinCtrl::BUILD() {
|
||||||
on_change_field();
|
on_change_field();
|
||||||
}
|
}
|
||||||
}), temp->GetId());
|
}), temp->GetId());
|
||||||
|
|
||||||
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e)
|
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e)
|
||||||
{
|
{
|
||||||
// # On OSX / Cocoa, wxSpinCtrl::GetValue() doesn't return the new value
|
// # On OSX / Cocoa, wxSpinCtrl::GetValue() doesn't return the new value
|
||||||
|
@ -398,9 +425,15 @@ void SpinCtrl::BUILD() {
|
||||||
tmp_value = std::stoi(value);
|
tmp_value = std::stoi(value);
|
||||||
else tmp_value = -9999;
|
else tmp_value = -9999;
|
||||||
// on_change_field();
|
// on_change_field();
|
||||||
// # We don't reset tmp_value here because _on_change might put callbacks
|
#ifdef __WXOSX__
|
||||||
// # in the CallAfter queue, and we want the tmp value to be available from
|
// #ys_FIXME_KILL_FOCUS so call on_change_field() inside wxEVT_TEXT
|
||||||
// # them as well.
|
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->GetId());
|
||||||
|
|
||||||
temp->SetToolTip(get_tooltip_text(text_value));
|
temp->SetToolTip(get_tooltip_text(text_value));
|
||||||
|
@ -432,9 +465,24 @@ void Choice::BUILD() {
|
||||||
}
|
}
|
||||||
set_selection();
|
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());
|
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()));
|
temp->SetToolTip(get_tooltip_text(temp->GetValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -611,9 +659,7 @@ boost::any& Choice::get_value()
|
||||||
if (m_opt_id == rp_option)
|
if (m_opt_id == rp_option)
|
||||||
return m_value = boost::any(ret_str);
|
return m_value = boost::any(ret_str);
|
||||||
|
|
||||||
if (m_opt.type != coEnum)
|
if (m_opt.type == coEnum)
|
||||||
/*m_value = */get_value_by_opt_type(ret_str);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
int ret_enum = static_cast<wxComboBox*>(window)->GetSelection();
|
int ret_enum = static_cast<wxComboBox*>(window)->GetSelection();
|
||||||
if (m_opt_id.compare("external_fill_pattern") == 0)
|
if (m_opt_id.compare("external_fill_pattern") == 0)
|
||||||
|
@ -638,7 +684,18 @@ boost::any& Choice::get_value()
|
||||||
m_value = static_cast<SeamPosition>(ret_enum);
|
m_value = static_cast<SeamPosition>(ret_enum);
|
||||||
else if (m_opt_id.compare("host_type") == 0)
|
else if (m_opt_id.compare("host_type") == 0)
|
||||||
m_value = static_cast<PrintHostType>(ret_enum);
|
m_value = static_cast<PrintHostType>(ret_enum);
|
||||||
|
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;
|
return m_value;
|
||||||
}
|
}
|
||||||
|
@ -702,8 +759,11 @@ void PointCtrl::BUILD()
|
||||||
temp->Add(new wxStaticText(m_parent, wxID_ANY, " y : "), 0, wxALIGN_CENTER_VERTICAL, 0);
|
temp->Add(new wxStaticText(m_parent, wxID_ANY, " y : "), 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||||
temp->Add(y_textctrl);
|
temp->Add(y_textctrl);
|
||||||
|
|
||||||
x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_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());
|
// 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
|
// // recast as a wxWindow to fit the calling convention
|
||||||
sizer = dynamic_cast<wxSizer*>(temp);
|
sizer = dynamic_cast<wxSizer*>(temp);
|
||||||
|
@ -712,6 +772,16 @@ void PointCtrl::BUILD()
|
||||||
y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y));
|
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)
|
void PointCtrl::set_value(const Vec2d& value, bool change_event)
|
||||||
{
|
{
|
||||||
m_disable_change_event = !change_event;
|
m_disable_change_event = !change_event;
|
||||||
|
|
|
@ -266,7 +266,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::any& get_value() override;
|
boost::any& get_value() override;
|
||||||
bool is_defined_input_value() const ;
|
|
||||||
|
|
||||||
virtual void enable();
|
virtual void enable();
|
||||||
virtual void disable();
|
virtual void disable();
|
||||||
|
@ -395,6 +394,7 @@ public:
|
||||||
|
|
||||||
void BUILD() override;
|
void BUILD() override;
|
||||||
|
|
||||||
|
void OnKillFocus(wxEvent& e, wxTextCtrl* win);
|
||||||
void set_value(const Vec2d& value, bool change_event = false);
|
void set_value(const Vec2d& value, bool change_event = false);
|
||||||
void set_value(const boost::any& value, bool change_event = false);
|
void set_value(const boost::any& value, bool change_event = false);
|
||||||
boost::any& get_value() override;
|
boost::any& get_value() override;
|
||||||
|
|
|
@ -617,42 +617,71 @@ bool GLCanvas3D::Bed::_are_equal(const Pointfs& bed_1, const Pointfs& bed_2)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const double GLCanvas3D::Axes::Radius = 0.5;
|
||||||
|
const double GLCanvas3D::Axes::ArrowBaseRadius = 2.5 * GLCanvas3D::Axes::Radius;
|
||||||
|
const double GLCanvas3D::Axes::ArrowLength = 5.0;
|
||||||
|
|
||||||
GLCanvas3D::Axes::Axes()
|
GLCanvas3D::Axes::Axes()
|
||||||
: origin(Vec3d::Zero())
|
: origin(Vec3d::Zero())
|
||||||
, length(0.0f)
|
, length(Vec3d::Zero())
|
||||||
{
|
{
|
||||||
|
m_quadric = ::gluNewQuadric();
|
||||||
|
if (m_quadric != nullptr)
|
||||||
|
::gluQuadricDrawStyle(m_quadric, GLU_FILL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::Axes::render(bool depth_test) const
|
GLCanvas3D::Axes::~Axes()
|
||||||
{
|
{
|
||||||
if (depth_test)
|
if (m_quadric != nullptr)
|
||||||
::glEnable(GL_DEPTH_TEST);
|
::gluDeleteQuadric(m_quadric);
|
||||||
else
|
}
|
||||||
::glDisable(GL_DEPTH_TEST);
|
|
||||||
|
|
||||||
::glLineWidth(2.0f);
|
void GLCanvas3D::Axes::render() const
|
||||||
::glBegin(GL_LINES);
|
{
|
||||||
// draw line for x axis
|
if (m_quadric == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
::glEnable(GL_DEPTH_TEST);
|
||||||
|
::glEnable(GL_LIGHTING);
|
||||||
|
|
||||||
|
// x axis
|
||||||
::glColor3f(1.0f, 0.0f, 0.0f);
|
::glColor3f(1.0f, 0.0f, 0.0f);
|
||||||
::glVertex3dv(origin.data());
|
::glPushMatrix();
|
||||||
::glVertex3f((GLfloat)origin(0) + length, (GLfloat)origin(1), (GLfloat)origin(2));
|
::glTranslated(origin(0), origin(1), origin(2));
|
||||||
// draw line for y axis
|
::glRotated(90.0, 0.0, 1.0, 0.0);
|
||||||
::glColor3f(0.0f, 1.0f, 0.0f);
|
render_axis(length(0));
|
||||||
::glVertex3dv(origin.data());
|
::glPopMatrix();
|
||||||
::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1) + length, (GLfloat)origin(2));
|
|
||||||
::glEnd();
|
|
||||||
// draw line for Z axis
|
|
||||||
// (re-enable depth test so that axis is correctly shown when objects are behind it)
|
|
||||||
if (!depth_test)
|
|
||||||
::glEnable(GL_DEPTH_TEST);
|
|
||||||
|
|
||||||
::glBegin(GL_LINES);
|
// y axis
|
||||||
|
::glColor3f(0.0f, 1.0f, 0.0f);
|
||||||
|
::glPushMatrix();
|
||||||
|
::glTranslated(origin(0), origin(1), origin(2));
|
||||||
|
::glRotated(-90.0, 1.0, 0.0, 0.0);
|
||||||
|
render_axis(length(1));
|
||||||
|
::glPopMatrix();
|
||||||
|
|
||||||
|
// z axis
|
||||||
::glColor3f(0.0f, 0.0f, 1.0f);
|
::glColor3f(0.0f, 0.0f, 1.0f);
|
||||||
::glVertex3dv(origin.data());
|
::glPushMatrix();
|
||||||
::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2) + length);
|
::glTranslated(origin(0), origin(1), origin(2));
|
||||||
::glEnd();
|
render_axis(length(2));
|
||||||
|
::glPopMatrix();
|
||||||
|
|
||||||
|
::glDisable(GL_LIGHTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLCanvas3D::Axes::render_axis(double length) const
|
||||||
|
{
|
||||||
|
::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
|
||||||
|
::gluCylinder(m_quadric, Radius, Radius, length, 32, 1);
|
||||||
|
::gluQuadricOrientation(m_quadric, GLU_INSIDE);
|
||||||
|
::gluDisk(m_quadric, 0.0, Radius, 32, 1);
|
||||||
|
::glTranslated(0.0, 0.0, length);
|
||||||
|
::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
|
||||||
|
::gluCylinder(m_quadric, ArrowBaseRadius, 0.0, ArrowLength, 32, 1);
|
||||||
|
::gluQuadricOrientation(m_quadric, GLU_INSIDE);
|
||||||
|
::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1);
|
||||||
|
}
|
||||||
|
|
||||||
GLCanvas3D::Shader::Shader()
|
GLCanvas3D::Shader::Shader()
|
||||||
: m_shader(nullptr)
|
: m_shader(nullptr)
|
||||||
|
@ -1133,8 +1162,21 @@ GLCanvas3D::Selection::Selection()
|
||||||
, m_valid(false)
|
, m_valid(false)
|
||||||
, m_bounding_box_dirty(true)
|
, m_bounding_box_dirty(true)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
m_quadric = ::gluNewQuadric();
|
||||||
|
if (m_quadric != nullptr)
|
||||||
|
::gluQuadricDrawStyle(m_quadric, GLU_FILL);
|
||||||
|
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
GLCanvas3D::Selection::~Selection()
|
||||||
|
{
|
||||||
|
if (m_quadric != nullptr)
|
||||||
|
::gluDeleteQuadric(m_quadric);
|
||||||
|
}
|
||||||
|
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
|
||||||
void GLCanvas3D::Selection::set_volumes(GLVolumePtrs* volumes)
|
void GLCanvas3D::Selection::set_volumes(GLVolumePtrs* volumes)
|
||||||
{
|
{
|
||||||
m_volumes = volumes;
|
m_volumes = volumes;
|
||||||
|
@ -1436,6 +1478,14 @@ bool GLCanvas3D::Selection::is_from_single_object() const
|
||||||
return (0 <= idx) && (idx < 1000);
|
return (0 <= idx) && (idx < 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GLCanvas3D::Selection::requires_uniform_scale() const
|
||||||
|
{
|
||||||
|
if (is_single_full_instance() || is_single_modifier() || is_single_volume())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int GLCanvas3D::Selection::get_object_idx() const
|
int GLCanvas3D::Selection::get_object_idx() const
|
||||||
{
|
{
|
||||||
return (m_cache.content.size() == 1) ? m_cache.content.begin()->first : -1;
|
return (m_cache.content.size() == 1) ? m_cache.content.begin()->first : -1;
|
||||||
|
@ -1489,10 +1539,15 @@ void GLCanvas3D::Selection::translate(const Vec3d& displacement)
|
||||||
{
|
{
|
||||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||||
if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower)
|
if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower)
|
||||||
|
{
|
||||||
|
if (_requires_local_axes())
|
||||||
|
(*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement;
|
Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement;
|
||||||
(*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement);
|
(*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (m_mode == Instance)
|
else if (m_mode == Instance)
|
||||||
(*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement);
|
(*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement);
|
||||||
#else
|
#else
|
||||||
|
@ -1519,11 +1574,16 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local)
|
||||||
{
|
{
|
||||||
if (is_single_full_instance())
|
if (is_single_full_instance())
|
||||||
#if ENABLE_WORLD_ROTATIONS
|
#if ENABLE_WORLD_ROTATIONS
|
||||||
|
{
|
||||||
|
if (local)
|
||||||
|
(*m_volumes)[i]->set_instance_rotation(rotation);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
||||||
Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix());
|
Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix());
|
||||||
(*m_volumes)[i]->set_instance_rotation(new_rotation);
|
(*m_volumes)[i]->set_instance_rotation(new_rotation);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||||
(*m_volumes)[i]->set_instance_rotation(rotation);
|
(*m_volumes)[i]->set_instance_rotation(rotation);
|
||||||
|
@ -1534,12 +1594,17 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local)
|
||||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||||
else if (is_single_volume() || is_single_modifier())
|
else if (is_single_volume() || is_single_modifier())
|
||||||
#if ENABLE_WORLD_ROTATIONS
|
#if ENABLE_WORLD_ROTATIONS
|
||||||
|
{
|
||||||
|
if (_requires_local_axes())
|
||||||
|
(*m_volumes)[i]->set_volume_rotation(rotation);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
||||||
const Transform3d& inst_m = m_cache.volumes_data[i].get_instance_rotation_matrix();
|
const Transform3d& inst_m = m_cache.volumes_data[i].get_instance_rotation_matrix();
|
||||||
Vec3d new_rotation = Geometry::extract_euler_angles(inst_m.inverse() * m * inst_m * m_cache.volumes_data[i].get_volume_rotation_matrix());
|
Vec3d new_rotation = Geometry::extract_euler_angles(inst_m.inverse() * m * inst_m * m_cache.volumes_data[i].get_volume_rotation_matrix());
|
||||||
(*m_volumes)[i]->set_volume_rotation(new_rotation);
|
(*m_volumes)[i]->set_volume_rotation(new_rotation);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
(*m_volumes)[i]->set_volume_rotation(rotation);
|
(*m_volumes)[i]->set_volume_rotation(rotation);
|
||||||
#endif // ENABLE_WORLD_ROTATIONS
|
#endif // ENABLE_WORLD_ROTATIONS
|
||||||
|
@ -1960,7 +2025,7 @@ void GLCanvas3D::Selection::erase()
|
||||||
|
|
||||||
void GLCanvas3D::Selection::render() const
|
void GLCanvas3D::Selection::render() const
|
||||||
{
|
{
|
||||||
if (is_empty())
|
if (!m_valid || is_empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// render cumulative bounding box of selected volumes
|
// render cumulative bounding box of selected volumes
|
||||||
|
@ -1968,6 +2033,28 @@ void GLCanvas3D::Selection::render() const
|
||||||
_render_synchronized_volumes();
|
_render_synchronized_volumes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
void GLCanvas3D::Selection::render_center() const
|
||||||
|
{
|
||||||
|
if (!m_valid || is_empty() || (m_quadric == nullptr))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const Vec3d& center = get_bounding_box().center();
|
||||||
|
|
||||||
|
::glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
::glEnable(GL_LIGHTING);
|
||||||
|
|
||||||
|
::glColor3f(1.0f, 1.0f, 1.0f);
|
||||||
|
::glPushMatrix();
|
||||||
|
::glTranslated(center(0), center(1), center(2));
|
||||||
|
::gluSphere(m_quadric, 0.75, 32, 32);
|
||||||
|
::glPopMatrix();
|
||||||
|
|
||||||
|
::glDisable(GL_LIGHTING);
|
||||||
|
}
|
||||||
|
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
|
||||||
void GLCanvas3D::Selection::_update_valid()
|
void GLCanvas3D::Selection::_update_valid()
|
||||||
{
|
{
|
||||||
m_valid = (m_volumes != nullptr) && (m_model != nullptr);
|
m_valid = (m_volumes != nullptr) && (m_model != nullptr);
|
||||||
|
@ -2501,9 +2588,14 @@ void GLCanvas3D::Selection::_ensure_on_bed()
|
||||||
}
|
}
|
||||||
#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING
|
#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING
|
||||||
|
|
||||||
const float GLCanvas3D::Gizmos::OverlayTexturesScale = 1.0f;
|
bool GLCanvas3D::Selection::_requires_local_axes() const
|
||||||
const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f * OverlayTexturesScale;
|
{
|
||||||
const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale;
|
return (m_mode == Volume) && is_from_single_instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
const float GLCanvas3D::Gizmos::OverlayIconsScale = 1.0f;
|
||||||
|
const float GLCanvas3D::Gizmos::OverlayBorder = 5.0f;
|
||||||
|
const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayIconsScale;
|
||||||
|
|
||||||
GLCanvas3D::Gizmos::Gizmos()
|
GLCanvas3D::Gizmos::Gizmos()
|
||||||
: m_enabled(false)
|
: m_enabled(false)
|
||||||
|
@ -2584,6 +2676,23 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent)
|
||||||
|
|
||||||
m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo));
|
m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo));
|
||||||
|
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
m_background_texture.metadata.filename = "toolbar_background.png";
|
||||||
|
m_background_texture.metadata.left = 16;
|
||||||
|
m_background_texture.metadata.top = 16;
|
||||||
|
m_background_texture.metadata.right = 16;
|
||||||
|
m_background_texture.metadata.bottom = 16;
|
||||||
|
|
||||||
|
if (!m_background_texture.metadata.filename.empty())
|
||||||
|
{
|
||||||
|
if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false))
|
||||||
|
{
|
||||||
|
_reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2606,24 +2715,22 @@ std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, con
|
||||||
|
|
||||||
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
||||||
float height = _get_total_overlay_height();
|
float height = _get_total_overlay_height();
|
||||||
float top_y = 0.5f * (cnv_h - height);
|
float top_y = 0.5f * (cnv_h - height) + OverlayBorder;
|
||||||
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||||
{
|
{
|
||||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
|
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale;
|
||||||
float half_tex_size = 0.5f * tex_size;
|
|
||||||
|
|
||||||
// we currently use circular icons for gizmo, so we check the radius
|
|
||||||
if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On))
|
if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On))
|
||||||
{
|
{
|
||||||
bool inside = (mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size;
|
bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size);
|
||||||
it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off);
|
it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off);
|
||||||
if (inside)
|
if (inside)
|
||||||
name = it->second->get_name();
|
name = it->second->get_name();
|
||||||
}
|
}
|
||||||
top_y += (tex_size + OverlayGapY);
|
top_y += (icon_size + OverlayGapY);
|
||||||
}
|
}
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
|
@ -2636,17 +2743,16 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec
|
||||||
|
|
||||||
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
||||||
float height = _get_total_overlay_height();
|
float height = _get_total_overlay_height();
|
||||||
float top_y = 0.5f * (cnv_h - height);
|
float top_y = 0.5f * (cnv_h - height) + OverlayBorder;
|
||||||
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||||
{
|
{
|
||||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
|
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale;
|
||||||
float half_tex_size = 0.5f * tex_size;
|
|
||||||
|
|
||||||
// we currently use circular icons for gizmo, so we check the radius
|
bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size);
|
||||||
if (it->second->is_activable(selection) && ((mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size))
|
if (it->second->is_activable(selection) && inside)
|
||||||
{
|
{
|
||||||
if ((it->second->get_state() == GLGizmoBase::On))
|
if ((it->second->get_state() == GLGizmoBase::On))
|
||||||
{
|
{
|
||||||
|
@ -2662,7 +2768,7 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec
|
||||||
else
|
else
|
||||||
it->second->set_state(GLGizmoBase::Off);
|
it->second->set_state(GLGizmoBase::Off);
|
||||||
|
|
||||||
top_y += (tex_size + OverlayGapY);
|
top_y += (icon_size + OverlayGapY);
|
||||||
}
|
}
|
||||||
|
|
||||||
GizmosMap::iterator it = m_gizmos.find(m_current);
|
GizmosMap::iterator it = m_gizmos.find(m_current);
|
||||||
|
@ -2734,20 +2840,18 @@ bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const
|
||||||
|
|
||||||
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
||||||
float height = _get_total_overlay_height();
|
float height = _get_total_overlay_height();
|
||||||
float top_y = 0.5f * (cnv_h - height);
|
float top_y = 0.5f * (cnv_h - height) + OverlayBorder;
|
||||||
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||||
{
|
{
|
||||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
|
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale;
|
||||||
float half_tex_size = 0.5f * tex_size;
|
|
||||||
|
|
||||||
// we currently use circular icons for gizmo, so we check the radius
|
if ((OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size))
|
||||||
if ((mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
top_y += (tex_size + OverlayGapY);
|
top_y += (icon_size + OverlayGapY);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -3020,21 +3124,102 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva
|
||||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||||
|
|
||||||
float height = _get_total_overlay_height();
|
float height = _get_total_overlay_height();
|
||||||
float top_x = (OverlayOffsetX - 0.5f * cnv_w) * inv_zoom;
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
float top_y = 0.5f * height * inv_zoom;
|
float scaled_border = OverlayBorder * inv_zoom;
|
||||||
|
|
||||||
|
float top_x = (-0.5f * cnv_w) * inv_zoom;
|
||||||
|
float top_y = (0.5f * height) * inv_zoom;
|
||||||
|
|
||||||
|
float left = top_x;
|
||||||
|
float top = top_y;
|
||||||
|
float right = left + _get_total_overlay_width() * inv_zoom;
|
||||||
|
float bottom = top - height * inv_zoom;
|
||||||
|
|
||||||
|
// renders background
|
||||||
|
unsigned int bg_tex_id = m_background_texture.texture.get_id();
|
||||||
|
float bg_tex_width = (float)m_background_texture.texture.get_width();
|
||||||
|
float bg_tex_height = (float)m_background_texture.texture.get_height();
|
||||||
|
if ((bg_tex_id != 0) && (bg_tex_width > 0) && (bg_tex_height > 0))
|
||||||
|
{
|
||||||
|
float inv_bg_tex_width = (bg_tex_width != 0.0f) ? 1.0f / bg_tex_width : 0.0f;
|
||||||
|
float inv_bg_tex_height = (bg_tex_height != 0.0f) ? 1.0f / bg_tex_height : 0.0f;
|
||||||
|
|
||||||
|
float bg_uv_left = 0.0f;
|
||||||
|
float bg_uv_right = 1.0f;
|
||||||
|
float bg_uv_top = 1.0f;
|
||||||
|
float bg_uv_bottom = 0.0f;
|
||||||
|
|
||||||
|
float bg_left = left;
|
||||||
|
float bg_right = right;
|
||||||
|
float bg_top = top;
|
||||||
|
float bg_bottom = bottom;
|
||||||
|
float bg_width = right - left;
|
||||||
|
float bg_height = top - bottom;
|
||||||
|
float bg_min_size = std::min(bg_width, bg_height);
|
||||||
|
|
||||||
|
float bg_uv_i_left = (float)m_background_texture.metadata.left * inv_bg_tex_width;
|
||||||
|
float bg_uv_i_right = 1.0f - (float)m_background_texture.metadata.right * inv_bg_tex_width;
|
||||||
|
float bg_uv_i_top = 1.0f - (float)m_background_texture.metadata.top * inv_bg_tex_height;
|
||||||
|
float bg_uv_i_bottom = (float)m_background_texture.metadata.bottom * inv_bg_tex_height;
|
||||||
|
|
||||||
|
float bg_i_left = bg_left + scaled_border;
|
||||||
|
float bg_i_right = bg_right - scaled_border;
|
||||||
|
float bg_i_top = bg_top - scaled_border;
|
||||||
|
float bg_i_bottom = bg_bottom + scaled_border;
|
||||||
|
|
||||||
|
bg_uv_left = bg_uv_i_left;
|
||||||
|
bg_i_left = bg_left;
|
||||||
|
|
||||||
|
if ((OverlayBorder > 0) && (bg_uv_top != bg_uv_i_top))
|
||||||
|
{
|
||||||
|
if (bg_uv_left != bg_uv_i_left)
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_top, bg_top, { { bg_uv_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_top }, { bg_uv_left, bg_uv_top } });
|
||||||
|
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_top, bg_top, { { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_top }, { bg_uv_i_left, bg_uv_top } });
|
||||||
|
|
||||||
|
if (bg_uv_right != bg_uv_i_right)
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_top, bg_top, { { bg_uv_i_right, bg_uv_i_top }, { bg_uv_right, bg_uv_i_top }, { bg_uv_right, bg_uv_top }, { bg_uv_i_right, bg_uv_top } });
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((OverlayBorder > 0) && (bg_uv_left != bg_uv_i_left))
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_bottom, bg_i_top, { { bg_uv_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_left, bg_uv_i_top } });
|
||||||
|
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_bottom, bg_i_top, { { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top } });
|
||||||
|
|
||||||
|
if ((OverlayBorder > 0) && (bg_uv_right != bg_uv_i_right))
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_bottom, bg_i_top, { { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top } });
|
||||||
|
|
||||||
|
if ((OverlayBorder > 0) && (bg_uv_bottom != bg_uv_i_bottom))
|
||||||
|
{
|
||||||
|
if (bg_uv_left != bg_uv_i_left)
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_bottom, bg_i_bottom, { { bg_uv_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_left, bg_uv_i_bottom } });
|
||||||
|
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_bottom, bg_i_bottom, { { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_right, bg_uv_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom } });
|
||||||
|
|
||||||
|
if (bg_uv_right != bg_uv_i_right)
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_bottom, bg_i_bottom, { { bg_uv_i_right, bg_uv_bottom }, { bg_uv_right, bg_uv_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
top_x += OverlayBorder * inv_zoom;
|
||||||
|
top_y -= OverlayBorder * inv_zoom;
|
||||||
|
#else
|
||||||
|
float top_x = (OverlayBorder - 0.5f * cnv_w) * inv_zoom;
|
||||||
|
float top_y = (0.5f * height - OverlayBorder) * inv_zoom;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
float scaled_gap_y = OverlayGapY * inv_zoom;
|
float scaled_gap_y = OverlayGapY * inv_zoom;
|
||||||
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||||
{
|
{
|
||||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale * inv_zoom;
|
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale * inv_zoom;
|
||||||
GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + tex_size, top_y - tex_size, top_y);
|
GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + icon_size, top_y - icon_size, top_y);
|
||||||
#if ENABLE_IMGUI
|
#if ENABLE_IMGUI
|
||||||
if (it->second->get_state() == GLGizmoBase::On)
|
if (it->second->get_state() == GLGizmoBase::On)
|
||||||
it->second->render_input_window(2.0f * OverlayOffsetX + tex_size * zoom, 0.5f * cnv_h - top_y * zoom, selection);
|
it->second->render_input_window(2.0f * OverlayBorder + icon_size * zoom, 0.5f * cnv_h - top_y * zoom, selection);
|
||||||
#endif // ENABLE_IMGUI
|
#endif // ENABLE_IMGUI
|
||||||
top_y -= (tex_size + scaled_gap_y);
|
top_y -= (icon_size + scaled_gap_y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3047,19 +3232,35 @@ void GLCanvas3D::Gizmos::_render_current_gizmo(const GLCanvas3D::Selection& sele
|
||||||
|
|
||||||
float GLCanvas3D::Gizmos::_get_total_overlay_height() const
|
float GLCanvas3D::Gizmos::_get_total_overlay_height() const
|
||||||
{
|
{
|
||||||
float height = 0.0f;
|
float height = 2.0f * OverlayBorder;
|
||||||
|
|
||||||
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||||
{
|
{
|
||||||
if (it->first == SlaSupports && wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA)
|
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
height += (float)it->second->get_textures_size() * OverlayTexturesScale + OverlayGapY;
|
height += (float)it->second->get_textures_size() * OverlayIconsScale + OverlayGapY;
|
||||||
}
|
}
|
||||||
|
|
||||||
return height - OverlayGapY;
|
return height - OverlayGapY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
float GLCanvas3D::Gizmos::_get_total_overlay_width() const
|
||||||
|
{
|
||||||
|
float max_icon_width = 0.0f;
|
||||||
|
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||||
|
{
|
||||||
|
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
max_icon_width = std::max(max_icon_width, (float)it->second->get_textures_size() * OverlayIconsScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
return max_icon_width + 2.0f * OverlayBorder;
|
||||||
|
}
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
|
||||||
GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const
|
GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const
|
||||||
{
|
{
|
||||||
GizmosMap::const_iterator it = m_gizmos.find(m_current);
|
GizmosMap::const_iterator it = m_gizmos.find(m_current);
|
||||||
|
@ -3434,11 +3635,16 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
|
||||||
: m_canvas(canvas)
|
: m_canvas(canvas)
|
||||||
, m_context(nullptr)
|
, m_context(nullptr)
|
||||||
, m_in_render(false)
|
, m_in_render(false)
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
, m_toolbar(GLToolbar::Normal)
|
||||||
|
#else
|
||||||
, m_toolbar(*this)
|
, m_toolbar(*this)
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
, m_view_toolbar(nullptr)
|
, m_view_toolbar(nullptr)
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
, m_use_clipping_planes(false)
|
, m_use_clipping_planes(false)
|
||||||
|
, m_sidebar_field("")
|
||||||
, m_config(nullptr)
|
, m_config(nullptr)
|
||||||
, m_process(nullptr)
|
, m_process(nullptr)
|
||||||
, m_model(nullptr)
|
, m_model(nullptr)
|
||||||
|
@ -3668,7 +3874,7 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape)
|
||||||
|
|
||||||
// Set the origin and size for painting of the coordinate system axes.
|
// Set the origin and size for painting of the coordinate system axes.
|
||||||
m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z);
|
m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z);
|
||||||
set_axes_length(0.3f * (float)m_bed.get_bounding_box().max_size());
|
set_bed_axes_length(0.1 * m_bed.get_bounding_box().max_size());
|
||||||
|
|
||||||
if (new_shape)
|
if (new_shape)
|
||||||
zoom_to_bed();
|
zoom_to_bed();
|
||||||
|
@ -3676,9 +3882,9 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape)
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::set_axes_length(float length)
|
void GLCanvas3D::set_bed_axes_length(double length)
|
||||||
{
|
{
|
||||||
m_axes.length = length;
|
m_axes.length = length * Vec3d::Ones();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::set_color_by(const std::string& value)
|
void GLCanvas3D::set_color_by(const std::string& value)
|
||||||
|
@ -3938,21 +4144,20 @@ void GLCanvas3D::render()
|
||||||
_render_background();
|
_render_background();
|
||||||
|
|
||||||
if (is_custom_bed) // untextured bed needs to be rendered before objects
|
if (is_custom_bed) // untextured bed needs to be rendered before objects
|
||||||
{
|
|
||||||
_render_bed(theta);
|
_render_bed(theta);
|
||||||
// disable depth testing so that axes are not covered by ground
|
|
||||||
_render_axes(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_render_objects();
|
_render_objects();
|
||||||
_render_sla_slices();
|
_render_sla_slices();
|
||||||
_render_selection();
|
_render_selection();
|
||||||
|
|
||||||
|
_render_axes();
|
||||||
|
|
||||||
if (!is_custom_bed) // textured bed needs to be rendered after objects
|
if (!is_custom_bed) // textured bed needs to be rendered after objects
|
||||||
{
|
|
||||||
_render_axes(true);
|
|
||||||
_render_bed(theta);
|
_render_bed(theta);
|
||||||
}
|
|
||||||
|
#if ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
_render_selection_center();
|
||||||
|
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
|
||||||
// we need to set the mouse's scene position here because the depth buffer
|
// we need to set the mouse's scene position here because the depth buffer
|
||||||
// could be invalidated by the following gizmo render methods
|
// could be invalidated by the following gizmo render methods
|
||||||
|
@ -4675,7 +4880,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||||
int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1;
|
int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1;
|
||||||
m_layers_editing.last_object_id = layer_editing_object_idx;
|
m_layers_editing.last_object_id = layer_editing_object_idx;
|
||||||
bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position);
|
bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position);
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position, *this);
|
||||||
|
#else
|
||||||
int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position);
|
int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position);
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
int view_toolbar_contains_mouse = (m_view_toolbar != nullptr) ? m_view_toolbar->contains_mouse(m_mouse.position, *this) : -1;
|
int view_toolbar_contains_mouse = (m_view_toolbar != nullptr) ? m_view_toolbar->contains_mouse(m_mouse.position, *this) : -1;
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
|
@ -4699,7 +4908,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||||
else if (evt.LeftDClick() && (toolbar_contains_mouse != -1))
|
else if (evt.LeftDClick() && (toolbar_contains_mouse != -1))
|
||||||
{
|
{
|
||||||
m_toolbar_action_running = true;
|
m_toolbar_action_running = true;
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
m_toolbar.do_action((unsigned int)toolbar_contains_mouse, *this);
|
||||||
|
#else
|
||||||
m_toolbar.do_action((unsigned int)toolbar_contains_mouse);
|
m_toolbar.do_action((unsigned int)toolbar_contains_mouse);
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
}
|
}
|
||||||
else if (evt.LeftDClick() && (m_gizmos.get_current_type() != Gizmos::Undefined))
|
else if (evt.LeftDClick() && (m_gizmos.get_current_type() != Gizmos::Undefined))
|
||||||
{
|
{
|
||||||
|
@ -4778,7 +4991,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||||
else if (toolbar_contains_mouse != -1)
|
else if (toolbar_contains_mouse != -1)
|
||||||
{
|
{
|
||||||
m_toolbar_action_running = true;
|
m_toolbar_action_running = true;
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
m_toolbar.do_action((unsigned int)toolbar_contains_mouse, *this);
|
||||||
|
#else
|
||||||
m_toolbar.do_action((unsigned int)toolbar_contains_mouse);
|
m_toolbar.do_action((unsigned int)toolbar_contains_mouse);
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
m_mouse.left_down = false;
|
m_mouse.left_down = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -4983,7 +5200,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||||
else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled())
|
else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled())
|
||||||
{
|
{
|
||||||
// deselect and propagate event through callback
|
// deselect and propagate event through callback
|
||||||
if (m_picking_enabled && !m_toolbar_action_running && !m_mouse.ignore_up_event)
|
if (!evt.ShiftDown() && m_picking_enabled && !m_toolbar_action_running && !m_mouse.ignore_up_event)
|
||||||
{
|
{
|
||||||
m_selection.clear();
|
m_selection.clear();
|
||||||
m_selection.set_mode(Selection::Instance);
|
m_selection.set_mode(Selection::Instance);
|
||||||
|
@ -5061,7 +5278,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||||
|
|
||||||
// updates toolbar overlay
|
// updates toolbar overlay
|
||||||
if (tooltip.empty())
|
if (tooltip.empty())
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
tooltip = m_toolbar.update_hover_state(m_mouse.position, *this);
|
||||||
|
#else
|
||||||
tooltip = m_toolbar.update_hover_state(m_mouse.position);
|
tooltip = m_toolbar.update_hover_state(m_mouse.position);
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
|
||||||
// updates view toolbar overlay
|
// updates view toolbar overlay
|
||||||
if (tooltip.empty() && (m_view_toolbar != nullptr))
|
if (tooltip.empty() && (m_view_toolbar != nullptr))
|
||||||
|
@ -5413,6 +5634,17 @@ void GLCanvas3D::update_gizmos_on_off_state()
|
||||||
m_gizmos.update_on_off_state(get_selection());
|
m_gizmos.update_on_off_state(get_selection());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool focus_on)
|
||||||
|
{
|
||||||
|
m_sidebar_field = focus_on ? opt_key : "";
|
||||||
|
|
||||||
|
if (!m_sidebar_field.empty())
|
||||||
|
{
|
||||||
|
m_gizmos.reset_all_states();
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool GLCanvas3D::_is_shown_on_screen() const
|
bool GLCanvas3D::_is_shown_on_screen() const
|
||||||
{
|
{
|
||||||
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
|
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
|
||||||
|
@ -5429,7 +5661,24 @@ bool GLCanvas3D::_init_toolbar()
|
||||||
if (!m_toolbar.is_enabled())
|
if (!m_toolbar.is_enabled())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
ItemsIconsTexture::Metadata icons_data;
|
||||||
|
icons_data.filename = "toolbar.png";
|
||||||
|
icons_data.icon_size = 36;
|
||||||
|
icons_data.icon_border_size = 1;
|
||||||
|
icons_data.icon_gap_size = 1;
|
||||||
|
|
||||||
|
BackgroundTexture::Metadata background_data;
|
||||||
|
background_data.filename = "toolbar_background.png";
|
||||||
|
background_data.left = 16;
|
||||||
|
background_data.top = 16;
|
||||||
|
background_data.right = 16;
|
||||||
|
background_data.bottom = 16;
|
||||||
|
|
||||||
|
if (!m_toolbar.init(icons_data, background_data))
|
||||||
|
#else
|
||||||
if (!m_toolbar.init("toolbar.png", 36, 1, 1))
|
if (!m_toolbar.init("toolbar.png", 36, 1, 1))
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
{
|
{
|
||||||
// unable to init the toolbar texture, disable it
|
// unable to init the toolbar texture, disable it
|
||||||
m_toolbar.set_enabled(false);
|
m_toolbar.set_enabled(false);
|
||||||
|
@ -5438,6 +5687,10 @@ bool GLCanvas3D::_init_toolbar()
|
||||||
|
|
||||||
// m_toolbar.set_layout_type(GLToolbar::Layout::Vertical);
|
// m_toolbar.set_layout_type(GLToolbar::Layout::Vertical);
|
||||||
m_toolbar.set_layout_type(GLToolbar::Layout::Horizontal);
|
m_toolbar.set_layout_type(GLToolbar::Layout::Horizontal);
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
m_toolbar.set_layout_orientation(GLToolbar::Layout::Top);
|
||||||
|
m_toolbar.set_border(5.0f);
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
m_toolbar.set_separator_size(5);
|
m_toolbar.set_separator_size(5);
|
||||||
m_toolbar.set_gap_size(2);
|
m_toolbar.set_gap_size(2);
|
||||||
|
|
||||||
|
@ -5524,9 +5777,6 @@ bool GLCanvas3D::_init_toolbar()
|
||||||
if (!m_toolbar.add_item(item))
|
if (!m_toolbar.add_item(item))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!m_toolbar.add_separator())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
enable_toolbar_item("add", true);
|
enable_toolbar_item("add", true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -5872,9 +6122,9 @@ void GLCanvas3D::_render_bed(float theta) const
|
||||||
m_bed.render(theta);
|
m_bed.render(theta);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::_render_axes(bool depth_test) const
|
void GLCanvas3D::_render_axes() const
|
||||||
{
|
{
|
||||||
m_axes.render(depth_test);
|
m_axes.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::_render_objects() const
|
void GLCanvas3D::_render_objects() const
|
||||||
|
@ -5950,6 +6200,14 @@ void GLCanvas3D::_render_selection() const
|
||||||
m_selection.render();
|
m_selection.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
void GLCanvas3D::_render_selection_center() const
|
||||||
|
{
|
||||||
|
if (!m_gizmos.is_running())
|
||||||
|
m_selection.render_center();
|
||||||
|
}
|
||||||
|
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
|
||||||
void GLCanvas3D::_render_warning_texture() const
|
void GLCanvas3D::_render_warning_texture() const
|
||||||
{
|
{
|
||||||
if (!m_warning_texture_enabled)
|
if (!m_warning_texture_enabled)
|
||||||
|
@ -6063,7 +6321,11 @@ void GLCanvas3D::_render_toolbar() const
|
||||||
#if !ENABLE_REMOVE_TABS_FROM_PLATER
|
#if !ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
_resize_toolbar();
|
_resize_toolbar();
|
||||||
#endif // !ENABLE_REMOVE_TABS_FROM_PLATER
|
#endif // !ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
m_toolbar.render(*this);
|
||||||
|
#else
|
||||||
m_toolbar.render();
|
m_toolbar.render();
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
|
@ -6093,9 +6355,7 @@ void GLCanvas3D::_render_camera_target() const
|
||||||
::glColor3f(0.0f, 1.0f, 0.0f);
|
::glColor3f(0.0f, 1.0f, 0.0f);
|
||||||
::glVertex3d(target(0), target(1) - half_length, target(2));
|
::glVertex3d(target(0), target(1) - half_length, target(2));
|
||||||
::glVertex3d(target(0), target(1) + half_length, target(2));
|
::glVertex3d(target(0), target(1) + half_length, target(2));
|
||||||
::glEnd();
|
// draw line for z axis
|
||||||
|
|
||||||
::glBegin(GL_LINES);
|
|
||||||
::glColor3f(0.0f, 0.0f, 1.0f);
|
::glColor3f(0.0f, 0.0f, 1.0f);
|
||||||
::glVertex3d(target(0), target(1), target(2) - half_length);
|
::glVertex3d(target(0), target(1), target(2) - half_length);
|
||||||
::glVertex3d(target(0), target(1), target(2) + half_length);
|
::glVertex3d(target(0), target(1), target(2) + half_length);
|
||||||
|
@ -7721,25 +7981,54 @@ void GLCanvas3D::_resize_toolbar() const
|
||||||
float zoom = get_camera_zoom();
|
float zoom = get_camera_zoom();
|
||||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||||
|
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation();
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
|
||||||
switch (m_toolbar.get_layout_type())
|
switch (m_toolbar.get_layout_type())
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case GLToolbar::Layout::Horizontal:
|
case GLToolbar::Layout::Horizontal:
|
||||||
{
|
{
|
||||||
// centers the toolbar on the top edge of the 3d scene
|
// centers the toolbar on the top edge of the 3d scene
|
||||||
unsigned int toolbar_width = m_toolbar.get_width();
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
float top = (0.5f * (float)cnv_size.get_height() - 2.0f) * inv_zoom;
|
float top, left;
|
||||||
float left = -0.5f * (float)toolbar_width * inv_zoom;
|
if (orientation == GLToolbar::Layout::Top)
|
||||||
|
{
|
||||||
|
top = 0.5f * (float)cnv_size.get_height() * inv_zoom;
|
||||||
|
left = -0.5f * m_toolbar.get_width() * inv_zoom;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar->get_height()) * inv_zoom;
|
||||||
|
left = -0.5f * m_toolbar.get_width() * inv_zoom;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
float top = 0.5f * (float)cnv_size.get_height() * inv_zoom;
|
||||||
|
float left = -0.5f * m_toolbar.get_width() * inv_zoom;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
m_toolbar.set_position(top, left);
|
m_toolbar.set_position(top, left);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GLToolbar::Layout::Vertical:
|
case GLToolbar::Layout::Vertical:
|
||||||
{
|
{
|
||||||
// centers the toolbar on the right edge of the 3d scene
|
// centers the toolbar on the right edge of the 3d scene
|
||||||
unsigned int toolbar_width = m_toolbar.get_width();
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
unsigned int toolbar_height = m_toolbar.get_height();
|
float top, left;
|
||||||
float top = 0.5f * (float)toolbar_height * inv_zoom;
|
if (orientation == GLToolbar::Layout::Left)
|
||||||
float left = (0.5f * (float)cnv_size.get_width() - toolbar_width - 2.0f) * inv_zoom;
|
{
|
||||||
|
top = 0.5f * m_toolbar.get_height() * inv_zoom;
|
||||||
|
left = (-0.5f * (float)cnv_size.get_width()) * inv_zoom;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
top = 0.5f * m_toolbar.get_height() * inv_zoom;
|
||||||
|
left = (0.5f * (float)cnv_size.get_width() - m_toolbar.get_width()) * inv_zoom;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
float top = 0.5f * m_toolbar.get_height() * inv_zoom;
|
||||||
|
float left = (0.5f * (float)cnv_size.get_width() - m_toolbar.get_width()) * inv_zoom;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
m_toolbar.set_position(top, left);
|
m_toolbar.set_position(top, left);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -7748,6 +8037,7 @@ void GLCanvas3D::_resize_toolbar() const
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
if (m_view_toolbar != nullptr)
|
if (m_view_toolbar != nullptr)
|
||||||
{
|
{
|
||||||
|
// places the toolbar on the bottom-left corner of the 3d scene
|
||||||
float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar->get_height()) * inv_zoom;
|
float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar->get_height()) * inv_zoom;
|
||||||
float left = -0.5f * (float)cnv_size.get_width() * inv_zoom;
|
float left = -0.5f * (float)cnv_size.get_width() * inv_zoom;
|
||||||
m_view_toolbar->set_position(top, left);
|
m_view_toolbar->set_position(top, left);
|
||||||
|
|
|
@ -20,6 +20,8 @@ class wxTimerEvent;
|
||||||
class wxPaintEvent;
|
class wxPaintEvent;
|
||||||
class wxGLCanvas;
|
class wxGLCanvas;
|
||||||
|
|
||||||
|
class GLUquadric;
|
||||||
|
typedef class GLUquadric GLUquadricObj;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -155,7 +157,7 @@ class GLCanvas3D
|
||||||
// float distance;
|
// float distance;
|
||||||
#if !ENABLE_CONSTRAINED_CAMERA_TARGET
|
#if !ENABLE_CONSTRAINED_CAMERA_TARGET
|
||||||
Vec3d target;
|
Vec3d target;
|
||||||
#endif !// ENABLE_CONSTRAINED_CAMERA_TARGET
|
#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if ENABLE_CONSTRAINED_CAMERA_TARGET
|
#if ENABLE_CONSTRAINED_CAMERA_TARGET
|
||||||
|
@ -231,12 +233,20 @@ class GLCanvas3D
|
||||||
|
|
||||||
struct Axes
|
struct Axes
|
||||||
{
|
{
|
||||||
|
static const double Radius;
|
||||||
|
static const double ArrowBaseRadius;
|
||||||
|
static const double ArrowLength;
|
||||||
Vec3d origin;
|
Vec3d origin;
|
||||||
float length;
|
Vec3d length;
|
||||||
|
GLUquadricObj* m_quadric;
|
||||||
|
|
||||||
Axes();
|
Axes();
|
||||||
|
~Axes();
|
||||||
|
|
||||||
void render(bool depth_test) const;
|
void render() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void render_axis(double length) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Shader
|
class Shader
|
||||||
|
@ -481,8 +491,15 @@ public:
|
||||||
mutable BoundingBoxf3 m_bounding_box;
|
mutable BoundingBoxf3 m_bounding_box;
|
||||||
mutable bool m_bounding_box_dirty;
|
mutable bool m_bounding_box_dirty;
|
||||||
|
|
||||||
|
#if ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
GLUquadricObj* m_quadric;
|
||||||
|
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Selection();
|
Selection();
|
||||||
|
#if ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
~Selection();
|
||||||
|
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
|
||||||
void set_volumes(GLVolumePtrs* volumes);
|
void set_volumes(GLVolumePtrs* volumes);
|
||||||
|
|
||||||
|
@ -515,6 +532,7 @@ public:
|
||||||
bool is_wipe_tower() const { return m_type == WipeTower; }
|
bool is_wipe_tower() const { return m_type == WipeTower; }
|
||||||
bool is_modifier() const { return (m_type == SingleModifier) || (m_type == MultipleModifier); }
|
bool is_modifier() const { return (m_type == SingleModifier) || (m_type == MultipleModifier); }
|
||||||
bool is_single_modifier() const { return m_type == SingleModifier; }
|
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_single_full_instance() const;
|
||||||
bool is_multiple_full_instance() const { return m_type == MultipleFullInstance; }
|
bool is_multiple_full_instance() const { return m_type == MultipleFullInstance; }
|
||||||
bool is_single_full_object() const { return m_type == SingleFullObject; }
|
bool is_single_full_object() const { return m_type == SingleFullObject; }
|
||||||
|
@ -526,6 +544,7 @@ public:
|
||||||
bool is_from_single_object() const;
|
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 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
|
// Returns the the object id if the selection is from a single object, otherwise is -1
|
||||||
int get_object_idx() const;
|
int get_object_idx() const;
|
||||||
|
@ -557,6 +576,9 @@ public:
|
||||||
void erase();
|
void erase();
|
||||||
|
|
||||||
void render() const;
|
void render() const;
|
||||||
|
#if ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
void render_center() const;
|
||||||
|
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _update_valid();
|
void _update_valid();
|
||||||
|
@ -577,6 +599,7 @@ public:
|
||||||
#if ENABLE_ENSURE_ON_BED_WHILE_SCALING
|
#if ENABLE_ENSURE_ON_BED_WHILE_SCALING
|
||||||
void _ensure_on_bed();
|
void _ensure_on_bed();
|
||||||
#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING
|
#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING
|
||||||
|
bool _requires_local_axes() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClippingPlane
|
class ClippingPlane
|
||||||
|
@ -607,8 +630,8 @@ public:
|
||||||
private:
|
private:
|
||||||
class Gizmos
|
class Gizmos
|
||||||
{
|
{
|
||||||
static const float OverlayTexturesScale;
|
static const float OverlayIconsScale;
|
||||||
static const float OverlayOffsetX;
|
static const float OverlayBorder;
|
||||||
static const float OverlayGapY;
|
static const float OverlayGapY;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -628,6 +651,9 @@ private:
|
||||||
bool m_enabled;
|
bool m_enabled;
|
||||||
typedef std::map<EType, GLGizmoBase*> GizmosMap;
|
typedef std::map<EType, GLGizmoBase*> GizmosMap;
|
||||||
GizmosMap m_gizmos;
|
GizmosMap m_gizmos;
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
BackgroundTexture m_background_texture;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
EType m_current;
|
EType m_current;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -696,6 +722,9 @@ private:
|
||||||
void _render_current_gizmo(const Selection& selection) const;
|
void _render_current_gizmo(const Selection& selection) const;
|
||||||
|
|
||||||
float _get_total_overlay_height() 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;
|
GLGizmoBase* _get_current() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -769,11 +798,16 @@ private:
|
||||||
mutable Gizmos m_gizmos;
|
mutable Gizmos m_gizmos;
|
||||||
mutable GLToolbar m_toolbar;
|
mutable GLToolbar m_toolbar;
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
GLToolbar* m_view_toolbar;
|
||||||
|
#else
|
||||||
GLRadioToolbar* m_view_toolbar;
|
GLRadioToolbar* m_view_toolbar;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
ClippingPlane m_clipping_planes[2];
|
ClippingPlane m_clipping_planes[2];
|
||||||
bool m_use_clipping_planes;
|
bool m_use_clipping_planes;
|
||||||
mutable SlaCap m_sla_caps[2];
|
mutable SlaCap m_sla_caps[2];
|
||||||
|
std::string m_sidebar_field;
|
||||||
|
|
||||||
mutable GLVolumeCollection m_volumes;
|
mutable GLVolumeCollection m_volumes;
|
||||||
Selection m_selection;
|
Selection m_selection;
|
||||||
|
@ -824,7 +858,11 @@ public:
|
||||||
wxGLCanvas* get_wxglcanvas() { return m_canvas; }
|
wxGLCanvas* get_wxglcanvas() { return m_canvas; }
|
||||||
|
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#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; }
|
void set_view_toolbar(GLRadioToolbar* toolbar) { m_view_toolbar = toolbar; }
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
|
|
||||||
bool init(bool useVBOs, bool use_legacy_opengl);
|
bool init(bool useVBOs, bool use_legacy_opengl);
|
||||||
|
@ -856,8 +894,7 @@ public:
|
||||||
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
|
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
|
||||||
// Sets m_bed.m_polygon to limit the object placement.
|
// Sets m_bed.m_polygon to limit the object placement.
|
||||||
void set_bed_shape(const Pointfs& shape);
|
void set_bed_shape(const Pointfs& shape);
|
||||||
|
void set_bed_axes_length(double length);
|
||||||
void set_axes_length(float length);
|
|
||||||
|
|
||||||
void set_clipping_plane(unsigned int id, const ClippingPlane& plane)
|
void set_clipping_plane(unsigned int id, const ClippingPlane& plane)
|
||||||
{
|
{
|
||||||
|
@ -972,7 +1009,7 @@ public:
|
||||||
void viewport_changed();
|
void viewport_changed();
|
||||||
#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
|
#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:
|
private:
|
||||||
bool _is_shown_on_screen() const;
|
bool _is_shown_on_screen() const;
|
||||||
|
@ -997,9 +1034,12 @@ private:
|
||||||
void _picking_pass() const;
|
void _picking_pass() const;
|
||||||
void _render_background() const;
|
void _render_background() const;
|
||||||
void _render_bed(float theta) const;
|
void _render_bed(float theta) const;
|
||||||
void _render_axes(bool depth_test) const;
|
void _render_axes() const;
|
||||||
void _render_objects() const;
|
void _render_objects() const;
|
||||||
void _render_selection() 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_warning_texture() const;
|
||||||
void _render_legend_texture() const;
|
void _render_legend_texture() const;
|
||||||
void _render_layer_editing_overlay() const;
|
void _render_layer_editing_overlay() const;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -77,6 +77,9 @@ public:
|
||||||
void do_action(wxEvtHandler *target);
|
void do_action(wxEvtHandler *target);
|
||||||
|
|
||||||
bool is_enabled() const;
|
bool is_enabled() const;
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
bool is_disabled() const;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
bool is_hovered() const;
|
bool is_hovered() const;
|
||||||
bool is_pressed() const;
|
bool is_pressed() const;
|
||||||
|
|
||||||
|
@ -94,7 +97,25 @@ private:
|
||||||
// from left to right
|
// from left to right
|
||||||
struct ItemsIconsTexture
|
struct ItemsIconsTexture
|
||||||
{
|
{
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
struct Metadata
|
||||||
|
{
|
||||||
|
// path of the file containing the icons' texture
|
||||||
|
std::string filename;
|
||||||
|
// size of the square icons, in pixels
|
||||||
|
unsigned int icon_size;
|
||||||
|
// size of the border, in pixels
|
||||||
|
unsigned int icon_border_size;
|
||||||
|
// distance between two adjacent icons (to avoid filtering artifacts), in pixels
|
||||||
|
unsigned int icon_gap_size;
|
||||||
|
|
||||||
|
Metadata();
|
||||||
|
};
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
GLTexture texture;
|
GLTexture texture;
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
Metadata metadata;
|
||||||
|
#else
|
||||||
// size of the square icons, in pixels
|
// size of the square icons, in pixels
|
||||||
unsigned int items_icon_size;
|
unsigned int items_icon_size;
|
||||||
// distance from the border, in pixels
|
// distance from the border, in pixels
|
||||||
|
@ -103,25 +124,82 @@ struct ItemsIconsTexture
|
||||||
unsigned int items_icon_gap_size;
|
unsigned int items_icon_gap_size;
|
||||||
|
|
||||||
ItemsIconsTexture();
|
ItemsIconsTexture();
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
struct BackgroundTexture
|
||||||
|
{
|
||||||
|
struct Metadata
|
||||||
|
{
|
||||||
|
// path of the file containing the background texture
|
||||||
|
std::string filename;
|
||||||
|
// size of the left edge, in pixels
|
||||||
|
unsigned int left;
|
||||||
|
// size of the right edge, in pixels
|
||||||
|
unsigned int right;
|
||||||
|
// size of the top edge, in pixels
|
||||||
|
unsigned int top;
|
||||||
|
// size of the bottom edge, in pixels
|
||||||
|
unsigned int bottom;
|
||||||
|
|
||||||
|
Metadata();
|
||||||
|
};
|
||||||
|
|
||||||
|
GLTexture texture;
|
||||||
|
Metadata metadata;
|
||||||
|
};
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
|
||||||
class GLToolbar
|
class GLToolbar
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
enum EType : unsigned char
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Radio,
|
||||||
|
Num_Types
|
||||||
|
};
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
|
||||||
struct Layout
|
struct Layout
|
||||||
{
|
{
|
||||||
enum Type : unsigned char
|
enum EType : unsigned char
|
||||||
{
|
{
|
||||||
Horizontal,
|
Horizontal,
|
||||||
Vertical,
|
Vertical,
|
||||||
Num_Types
|
Num_Types
|
||||||
};
|
};
|
||||||
|
|
||||||
Type type;
|
enum EOrientation : unsigned int
|
||||||
|
{
|
||||||
|
Top,
|
||||||
|
Bottom,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Center,
|
||||||
|
Num_Locations
|
||||||
|
};
|
||||||
|
|
||||||
|
EType type;
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
EOrientation orientation;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
float top;
|
float top;
|
||||||
float left;
|
float left;
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
float border;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
float separator_size;
|
float separator_size;
|
||||||
float gap_size;
|
float gap_size;
|
||||||
|
float icons_scale;
|
||||||
|
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
float width;
|
||||||
|
float height;
|
||||||
|
bool dirty;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
|
||||||
Layout();
|
Layout();
|
||||||
};
|
};
|
||||||
|
@ -129,25 +207,50 @@ public:
|
||||||
private:
|
private:
|
||||||
typedef std::vector<GLToolbarItem*> ItemsList;
|
typedef std::vector<GLToolbarItem*> ItemsList;
|
||||||
|
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
EType m_type;
|
||||||
|
#else
|
||||||
GLCanvas3D& m_parent;
|
GLCanvas3D& m_parent;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
bool m_enabled;
|
bool m_enabled;
|
||||||
ItemsIconsTexture m_icons_texture;
|
ItemsIconsTexture m_icons_texture;
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
BackgroundTexture m_background_texture;
|
||||||
|
mutable Layout m_layout;
|
||||||
|
#else
|
||||||
Layout m_layout;
|
Layout m_layout;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
|
||||||
ItemsList m_items;
|
ItemsList m_items;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
explicit GLToolbar(EType type);
|
||||||
|
#else
|
||||||
explicit GLToolbar(GLCanvas3D& parent);
|
explicit GLToolbar(GLCanvas3D& parent);
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
~GLToolbar();
|
~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);
|
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::Type get_layout_type() const;
|
Layout::EType get_layout_type() const;
|
||||||
void set_layout_type(Layout::Type type);
|
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);
|
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_separator_size(float size);
|
||||||
void set_gap_size(float size);
|
void set_gap_size(float size);
|
||||||
|
void set_icons_scale(float scale);
|
||||||
|
|
||||||
bool is_enabled() const;
|
bool is_enabled() const;
|
||||||
void set_enabled(bool enable);
|
void set_enabled(bool enable);
|
||||||
|
@ -160,42 +263,89 @@ public:
|
||||||
|
|
||||||
void enable_item(const std::string& name);
|
void enable_item(const std::string& name);
|
||||||
void disable_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;
|
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_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);
|
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
|
#else
|
||||||
void update_hover_state(const Vec2d& mouse_pos);
|
void update_hover_state(const Vec2d& mouse_pos);
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
#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
|
// returns the id of the item under the given mouse position or -1 if none
|
||||||
int contains_mouse(const Vec2d& mouse_pos) const;
|
int contains_mouse(const Vec2d& mouse_pos) const;
|
||||||
|
|
||||||
void do_action(unsigned int item_id);
|
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;
|
void render() const;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
void calc_layout() const;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
float get_width_horizontal() const;
|
float get_width_horizontal() const;
|
||||||
float get_width_vertical() const;
|
float get_width_vertical() const;
|
||||||
float get_height_horizontal() const;
|
float get_height_horizontal() const;
|
||||||
float get_height_vertical() const;
|
float get_height_vertical() const;
|
||||||
float get_main_size() const;
|
float get_main_size() const;
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#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_horizontal(const Vec2d& mouse_pos);
|
||||||
std::string update_hover_state_vertical(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
|
#else
|
||||||
void update_hover_state_horizontal(const Vec2d& mouse_pos);
|
void update_hover_state_horizontal(const Vec2d& mouse_pos);
|
||||||
void update_hover_state_vertical(const Vec2d& mouse_pos);
|
void update_hover_state_vertical(const Vec2d& mouse_pos);
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
#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_horizontal(const Vec2d& mouse_pos) const;
|
||||||
int contains_mouse_vertical(const Vec2d& mouse_pos) const;
|
int contains_mouse_vertical(const Vec2d& mouse_pos) const;
|
||||||
|
|
||||||
void render_horizontal() const;
|
void render_horizontal() const;
|
||||||
void render_vertical() const;
|
void render_vertical() const;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if !ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
class GLRadioToolbarItem
|
class GLRadioToolbarItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -274,6 +424,7 @@ public:
|
||||||
|
|
||||||
void render(const GLCanvas3D& parent) const;
|
void render(const GLCanvas3D& parent) const;
|
||||||
};
|
};
|
||||||
|
#endif // !ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
|
@ -195,6 +195,8 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
|
||||||
config.set_key_value(opt_key, new ConfigOptionEnum<SeamPosition>(boost::any_cast<SeamPosition>(value)));
|
config.set_key_value(opt_key, new ConfigOptionEnum<SeamPosition>(boost::any_cast<SeamPosition>(value)));
|
||||||
else if (opt_key.compare("host_type") == 0)
|
else if (opt_key.compare("host_type") == 0)
|
||||||
config.set_key_value(opt_key, new ConfigOptionEnum<PrintHostType>(boost::any_cast<PrintHostType>(value)));
|
config.set_key_value(opt_key, new ConfigOptionEnum<PrintHostType>(boost::any_cast<PrintHostType>(value)));
|
||||||
|
else if (opt_key.compare("display_orientation") == 0)
|
||||||
|
config.set_key_value(opt_key, new ConfigOptionEnum<SLADisplayOrientation>(boost::any_cast<SLADisplayOrientation>(value)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case coPoints:{
|
case coPoints:{
|
||||||
|
|
|
@ -73,7 +73,6 @@ GUI_App::GUI_App()
|
||||||
: wxApp()
|
: wxApp()
|
||||||
#if ENABLE_IMGUI
|
#if ENABLE_IMGUI
|
||||||
, m_imgui(new ImGuiWrapper())
|
, m_imgui(new ImGuiWrapper())
|
||||||
, m_printhost_queue(new PrintHostJobQueue())
|
|
||||||
#endif // ENABLE_IMGUI
|
#endif // ENABLE_IMGUI
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -142,6 +141,8 @@ bool GUI_App::OnInit()
|
||||||
update_mode();
|
update_mode();
|
||||||
SetTopWindow(mainframe);
|
SetTopWindow(mainframe);
|
||||||
|
|
||||||
|
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
||||||
|
|
||||||
CallAfter([this]() {
|
CallAfter([this]() {
|
||||||
// temporary workaround for the correct behavior of the Scrolled sidebar panel
|
// temporary workaround for the correct behavior of the Scrolled sidebar panel
|
||||||
auto& panel = sidebar();
|
auto& panel = sidebar();
|
||||||
|
|
|
@ -92,7 +92,7 @@ class GUI_App : public wxApp
|
||||||
std::unique_ptr<ImGuiWrapper> m_imgui;
|
std::unique_ptr<ImGuiWrapper> m_imgui;
|
||||||
#endif // ENABLE_IMGUI
|
#endif // ENABLE_IMGUI
|
||||||
|
|
||||||
std::unique_ptr<PrintHostJobQueue> m_printhost_queue;
|
std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool OnInit() override;
|
bool OnInit() override;
|
||||||
|
@ -164,7 +164,7 @@ public:
|
||||||
ImGuiWrapper* imgui() { return m_imgui.get(); }
|
ImGuiWrapper* imgui() { return m_imgui.get(); }
|
||||||
#endif // ENABLE_IMGUI
|
#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)
|
DECLARE_APP(GUI_App)
|
||||||
|
|
|
@ -982,6 +982,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)
|
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) {
|
if (type == itVolume) {
|
||||||
const auto volume = (*m_objects)[obj_idx]->volumes[idx];
|
const auto volume = (*m_objects)[obj_idx]->volumes[idx];
|
||||||
|
|
||||||
|
@ -1399,6 +1403,20 @@ void ObjectList::update_selections()
|
||||||
auto& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
auto& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||||
wxDataViewItemArray sels;
|
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())
|
if (selection.is_single_full_object())
|
||||||
{
|
{
|
||||||
sels.Add(m_objects_model->GetItemById(selection.get_object_idx()));
|
sels.Add(m_objects_model->GetItemById(selection.get_object_idx()));
|
||||||
|
@ -1459,13 +1477,11 @@ void ObjectList::update_selections()
|
||||||
|
|
||||||
select_items(sels);
|
select_items(sels);
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
|
||||||
if (GetSelection()) {
|
if (GetSelection()) {
|
||||||
const int sel_item_row = GetRowByItem(GetSelection());
|
const int sel_item_row = m_objects_model->GetRowByItem(GetSelection());
|
||||||
ScrollLines(sel_item_row - m_selected_row);
|
ScrollLines(sel_item_row - m_selected_row);
|
||||||
m_selected_row = sel_item_row;
|
m_selected_row = sel_item_row;
|
||||||
}
|
}
|
||||||
#endif //__WXMSW__
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::update_selections_on_canvas()
|
void ObjectList::update_selections_on_canvas()
|
||||||
|
@ -1656,7 +1672,9 @@ void ObjectList::change_part_type()
|
||||||
void ObjectList::last_volume_is_deleted(const int obj_idx)
|
void ObjectList::last_volume_is_deleted(const int obj_idx)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (obj_idx < 0 || (*m_objects).empty() || (*m_objects)[obj_idx]->volumes.empty())
|
if (obj_idx < 0 || m_objects->empty() ||
|
||||||
|
obj_idx <= m_objects->size() ||
|
||||||
|
(*m_objects)[obj_idx]->volumes.empty())
|
||||||
return;
|
return;
|
||||||
auto volume = (*m_objects)[obj_idx]->volumes[0];
|
auto volume = (*m_objects)[obj_idx]->volumes[0];
|
||||||
|
|
||||||
|
|
|
@ -25,18 +25,6 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||||
m_og->m_on_change = [this](const std::string& opt_key, const boost::any& value) {
|
m_og->m_on_change = [this](const std::string& opt_key, const boost::any& value) {
|
||||||
std::vector<std::string> axes{ "_x", "_y", "_z" };
|
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::string param;
|
||||||
std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param));
|
std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param));
|
||||||
|
|
||||||
|
@ -51,29 +39,51 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||||
change_rotation_value(new_value);
|
change_rotation_value(new_value);
|
||||||
else if (param == "scale")
|
else if (param == "scale")
|
||||||
change_scale_value(new_value);
|
change_scale_value(new_value);
|
||||||
|
else if (param == "size")
|
||||||
|
change_size_value(new_value);
|
||||||
|
|
||||||
|
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
m_og->m_fill_empty_value = [this](const std::string& opt_key)
|
m_og->m_fill_empty_value = [this](const std::string& opt_key)
|
||||||
{
|
{
|
||||||
if (opt_key == "scale_unit")
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::string param;
|
std::string param;
|
||||||
std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param));
|
std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param));
|
||||||
|
|
||||||
|
double value = 0.0;
|
||||||
|
|
||||||
if (param == "position") {
|
if (param == "position") {
|
||||||
int axis = opt_key.back() == 'x' ? 0 :
|
int axis = opt_key.back() == 'x' ? 0 :
|
||||||
opt_key.back() == 'y' ? 1 : 2;
|
opt_key.back() == 'y' ? 1 : 2;
|
||||||
|
|
||||||
m_og->set_value(opt_key, double_to_string(cache_position(axis)));
|
value = cache_position(axis);
|
||||||
return;
|
}
|
||||||
|
else if (param == "rotation") {
|
||||||
|
int axis = opt_key.back() == 'x' ? 0 :
|
||||||
|
opt_key.back() == 'y' ? 1 : 2;
|
||||||
|
|
||||||
|
value = cache_rotation(axis);
|
||||||
|
}
|
||||||
|
else if (param == "scale") {
|
||||||
|
int axis = opt_key.back() == 'x' ? 0 :
|
||||||
|
opt_key.back() == 'y' ? 1 : 2;
|
||||||
|
|
||||||
|
value = cache_scale(axis);
|
||||||
|
}
|
||||||
|
else if (param == "size") {
|
||||||
|
int axis = opt_key.back() == 'x' ? 0 :
|
||||||
|
opt_key.back() == 'y' ? 1 : 2;
|
||||||
|
|
||||||
|
value = 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)
|
m_og->m_set_focus = [this](const std::string& opt_key)
|
||||||
{
|
{
|
||||||
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key);
|
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
ConfigOptionDef def;
|
ConfigOptionDef def;
|
||||||
|
@ -106,59 +116,37 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||||
auto add_og_to_object_settings = [](const std::string& option_name, const std::string& sidetext)
|
auto add_og_to_object_settings = [](const std::string& option_name, const std::string& sidetext)
|
||||||
{
|
{
|
||||||
Line line = { _(option_name), "" };
|
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;
|
ConfigOptionDef def;
|
||||||
def.type = coFloat;
|
def.type = coFloat;
|
||||||
def.default_value = new ConfigOptionFloat(0.0);
|
def.default_value = new ConfigOptionFloat(0.0);
|
||||||
def.width = 50;
|
def.width = 50;
|
||||||
|
|
||||||
if (option_name == "Rotation")
|
if (option_name == "Rotation")
|
||||||
|
{
|
||||||
def.min = -360;
|
def.min = -360;
|
||||||
|
def.max = 360;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string lower_name = boost::algorithm::to_lower_copy(option_name);
|
const std::string lower_name = boost::algorithm::to_lower_copy(option_name);
|
||||||
|
|
||||||
std::vector<std::string> axes{ "x", "y", "z" };
|
std::vector<std::string> axes{ "x", "y", "z" };
|
||||||
for (auto axis : axes) {
|
for (auto axis : axes) {
|
||||||
if (axis == "z" && option_name != "Scale")
|
if (axis == "z")
|
||||||
def.sidetext = sidetext;
|
def.sidetext = sidetext;
|
||||||
Option option = Option(def, lower_name + "_" + axis);
|
Option option = Option(def, lower_name + "_" + axis);
|
||||||
option.opt.full_width = true;
|
option.opt.full_width = true;
|
||||||
line.append_option(option);
|
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;
|
return line;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Settings table
|
// 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("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("Rotation"), "°"), &m_rotate_Label);
|
||||||
m_og->append_line(add_og_to_object_settings(L("Scale"), "mm"));
|
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
|
/* Unused parameter at this time
|
||||||
def.label = L("Place on bed");
|
def.label = L("Place on bed");
|
||||||
|
@ -204,9 +192,11 @@ int ObjectManipulation::ol_selection()
|
||||||
|
|
||||||
void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection)
|
void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection)
|
||||||
{
|
{
|
||||||
wxString move_label = _(L("Position"));
|
wxString move_label = _(L("Position:"));
|
||||||
|
wxString rotate_label = _(L("Rotation:"));
|
||||||
|
wxString scale_label = _(L("Scale factors:"));
|
||||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||||
if (selection.is_single_full_instance() || selection.is_single_full_object())
|
if (selection.is_single_full_instance())
|
||||||
#else
|
#else
|
||||||
if (selection.is_single_full_object())
|
if (selection.is_single_full_object())
|
||||||
{
|
{
|
||||||
|
@ -232,6 +222,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele
|
||||||
update_position_value(volume->get_instance_offset());
|
update_position_value(volume->get_instance_offset());
|
||||||
update_rotation_value(volume->get_instance_rotation());
|
update_rotation_value(volume->get_instance_rotation());
|
||||||
update_scale_value(volume->get_instance_scaling_factor());
|
update_scale_value(volume->get_instance_scaling_factor());
|
||||||
|
update_size_value(volume->get_instance_transformation().get_matrix(true, true) * volume->bounding_box.size());
|
||||||
#else
|
#else
|
||||||
update_position_value(volume->get_offset());
|
update_position_value(volume->get_offset());
|
||||||
update_rotation_value(volume->get_rotation());
|
update_rotation_value(volume->get_rotation());
|
||||||
|
@ -239,19 +230,15 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele
|
||||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
||||||
m_og->enable();
|
m_og->enable();
|
||||||
}
|
}
|
||||||
else if (selection.is_wipe_tower())
|
else if (selection.is_single_full_object())
|
||||||
{
|
{
|
||||||
// the selection contains a single volume
|
const BoundingBoxf3& box = selection.get_bounding_box();
|
||||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
update_position_value(box.center());
|
||||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
reset_rotation_value();
|
||||||
update_position_value(volume->get_volume_offset());
|
reset_scale_value();
|
||||||
update_rotation_value(volume->get_volume_rotation());
|
update_size_value(box.size());
|
||||||
update_scale_value(volume->get_volume_scaling_factor());
|
rotate_label = _(L("Rotate:"));
|
||||||
#else
|
scale_label = _(L("Scale:"));
|
||||||
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_og->enable();
|
||||||
}
|
}
|
||||||
else if (selection.is_single_modifier() || selection.is_single_volume())
|
else if (selection.is_single_modifier() || selection.is_single_volume())
|
||||||
|
@ -262,6 +249,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele
|
||||||
update_position_value(volume->get_volume_offset());
|
update_position_value(volume->get_volume_offset());
|
||||||
update_rotation_value(volume->get_volume_rotation());
|
update_rotation_value(volume->get_volume_rotation());
|
||||||
update_scale_value(volume->get_volume_scaling_factor());
|
update_scale_value(volume->get_volume_scaling_factor());
|
||||||
|
update_size_value(volume->bounding_box.size());
|
||||||
#else
|
#else
|
||||||
update_position_value(volume->get_offset());
|
update_position_value(volume->get_offset());
|
||||||
update_rotation_value(volume->get_rotation());
|
update_rotation_value(volume->get_rotation());
|
||||||
|
@ -272,14 +260,18 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele
|
||||||
else if (wxGetApp().obj_list()->multiple_selection())
|
else if (wxGetApp().obj_list()->multiple_selection())
|
||||||
{
|
{
|
||||||
reset_settings_value();
|
reset_settings_value();
|
||||||
move_label = _(L("Displacement"));
|
move_label = _(L("Translate:"));
|
||||||
|
rotate_label = _(L("Rotate:"));
|
||||||
|
scale_label = _(L("Scale:"));
|
||||||
|
update_size_value(selection.get_bounding_box().size());
|
||||||
m_og->enable();
|
m_og->enable();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
reset_settings_value();
|
reset_settings_value();
|
||||||
|
|
||||||
m_move_Label->SetLabel(move_label);
|
m_move_Label->SetLabel(move_label);
|
||||||
m_og->get_field("scale_unit")->disable();// temporary decision
|
m_rotate_Label->SetLabel(rotate_label);
|
||||||
|
m_scale_Label->SetLabel(scale_label);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectManipulation::reset_settings_value()
|
void ObjectManipulation::reset_settings_value()
|
||||||
|
@ -299,7 +291,7 @@ void ObjectManipulation::reset_position_value()
|
||||||
m_og->set_value("position_y", def_0);
|
m_og->set_value("position_y", def_0);
|
||||||
m_og->set_value("position_z", def_0);
|
m_og->set_value("position_z", def_0);
|
||||||
|
|
||||||
cache_position = { 0., 0., 0. };
|
cache_position = Vec3d::Zero();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectManipulation::reset_rotation_value()
|
void ObjectManipulation::reset_rotation_value()
|
||||||
|
@ -307,68 +299,26 @@ void ObjectManipulation::reset_rotation_value()
|
||||||
m_og->set_value("rotation_x", def_0);
|
m_og->set_value("rotation_x", def_0);
|
||||||
m_og->set_value("rotation_y", def_0);
|
m_og->set_value("rotation_y", def_0);
|
||||||
m_og->set_value("rotation_z", def_0);
|
m_og->set_value("rotation_z", def_0);
|
||||||
|
|
||||||
|
cache_rotation = Vec3d::Zero();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectManipulation::reset_scale_value()
|
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_x", def_100);
|
||||||
m_og->set_value("scale_y", def_100);
|
m_og->set_value("scale_y", def_100);
|
||||||
m_og->set_value("scale_z", def_100);
|
m_og->set_value("scale_z", def_100);
|
||||||
|
|
||||||
|
cache_scale = Vec3d(100.0, 100.0, 100.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectManipulation::update_values()
|
void ObjectManipulation::reset_size_value()
|
||||||
{
|
{
|
||||||
int selection = ol_selection();
|
m_og->set_value("size_x", def_0);
|
||||||
if (selection < 0 || wxGetApp().mainframe->m_plater->model().objects.size() <= selection) {
|
m_og->set_value("size_y", def_0);
|
||||||
m_og->set_value("position_x", def_0);
|
m_og->set_value("size_z", 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();
|
cache_size = Vec3d::Zero();
|
||||||
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)
|
void ObjectManipulation::update_position_value(const Vec3d& position)
|
||||||
|
@ -382,42 +332,21 @@ void ObjectManipulation::update_position_value(const Vec3d& position)
|
||||||
|
|
||||||
void ObjectManipulation::update_scale_value(const Vec3d& scaling_factor)
|
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;
|
auto scale = scaling_factor * 100.0;
|
||||||
m_og->set_value("scale_x", double_to_string(scale(0), 2));
|
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_y", double_to_string(scale(1), 2));
|
||||||
m_og->set_value("scale_z", double_to_string(scale(2), 2));
|
m_og->set_value("scale_z", double_to_string(scale(2), 2));
|
||||||
|
|
||||||
|
cache_scale = scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectManipulation::update_rotation_values()
|
void ObjectManipulation::update_size_value(const Vec3d& size)
|
||||||
{
|
{
|
||||||
update_rotation_value(wxGetApp().mainframe->m_plater->model().objects[ol_selection()]->instances.front()->get_rotation());
|
m_og->set_value("size_x", double_to_string(size(0), 2));
|
||||||
}
|
m_og->set_value("size_y", double_to_string(size(1), 2));
|
||||||
|
m_og->set_value("size_z", double_to_string(size(2), 2));
|
||||||
|
|
||||||
void ObjectManipulation::update_rotation_value(double angle, Axis axis)
|
cache_size = size;
|
||||||
{
|
|
||||||
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)
|
void ObjectManipulation::update_rotation_value(const Vec3d& rotation)
|
||||||
|
@ -425,16 +354,15 @@ 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_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_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));
|
m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(rotation(2)), 0), 2));
|
||||||
}
|
|
||||||
|
|
||||||
|
cache_rotation = rotation;
|
||||||
|
}
|
||||||
|
|
||||||
void ObjectManipulation::change_position_value(const Vec3d& position)
|
void ObjectManipulation::change_position_value(const Vec3d& position)
|
||||||
{
|
{
|
||||||
Vec3d displacement(position - cache_position);
|
|
||||||
|
|
||||||
auto canvas = wxGetApp().plater()->canvas3D();
|
auto canvas = wxGetApp().plater()->canvas3D();
|
||||||
canvas->get_selection().start_dragging();
|
canvas->get_selection().start_dragging();
|
||||||
canvas->get_selection().translate(displacement);
|
canvas->get_selection().translate(position - cache_position);
|
||||||
canvas->do_move();
|
canvas->do_move();
|
||||||
|
|
||||||
cache_position = position;
|
cache_position = position;
|
||||||
|
@ -442,28 +370,43 @@ void ObjectManipulation::change_position_value(const Vec3d& position)
|
||||||
|
|
||||||
void ObjectManipulation::change_rotation_value(const Vec3d& rotation)
|
void ObjectManipulation::change_rotation_value(const Vec3d& rotation)
|
||||||
{
|
{
|
||||||
|
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||||
|
const GLCanvas3D::Selection& selection = canvas->get_selection();
|
||||||
|
|
||||||
Vec3d rad_rotation;
|
Vec3d rad_rotation;
|
||||||
for (size_t i = 0; i < 3; ++i)
|
for (size_t i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
rad_rotation(i) = Geometry::deg2rad(rotation(i));
|
rad_rotation(i) = Geometry::deg2rad(rotation(i));
|
||||||
auto canvas = wxGetApp().plater()->canvas3D();
|
}
|
||||||
|
|
||||||
canvas->get_selection().start_dragging();
|
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();
|
canvas->do_rotate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectManipulation::change_scale_value(const Vec3d& scale)
|
void ObjectManipulation::change_scale_value(const Vec3d& scale)
|
||||||
{
|
{
|
||||||
Vec3d scaling_factor;
|
Vec3d scaling_factor = scale;
|
||||||
if (m_is_percent_scale)
|
const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||||
scaling_factor = scale*0.01;
|
if (selection.requires_uniform_scale())
|
||||||
else {
|
{
|
||||||
int selection = ol_selection();
|
Vec3d abs_scale_diff = (scale - cache_scale).cwiseAbs();
|
||||||
ModelObjectPtrs& objects = *wxGetApp().model_objects();
|
double max_diff = abs_scale_diff(X);
|
||||||
|
Axis max_diff_axis = X;
|
||||||
auto size = objects[selection]->instance_bounding_box(0).size();
|
if (max_diff < abs_scale_diff(Y))
|
||||||
for (size_t i = 0; i < 3; ++i)
|
{
|
||||||
scaling_factor(i) = scale(i) / size(i);
|
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();
|
auto canvas = wxGetApp().plater()->canvas3D();
|
||||||
canvas->get_selection().start_dragging();
|
canvas->get_selection().start_dragging();
|
||||||
|
@ -471,9 +414,18 @@ void ObjectManipulation::change_scale_value(const Vec3d& scale)
|
||||||
canvas->do_scale();
|
canvas->do_scale();
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = 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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
} //namespace GUI
|
} //namespace GUI
|
||||||
|
|
|
@ -14,12 +14,14 @@ namespace GUI {
|
||||||
|
|
||||||
class ObjectManipulation : public OG_Settings
|
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
|
|
||||||
|
|
||||||
Vec3d cache_position { 0., 0., 0. };
|
Vec3d cache_position { 0., 0., 0. };
|
||||||
|
Vec3d cache_rotation { 0., 0., 0. };
|
||||||
|
Vec3d cache_scale { 100., 100., 100. };
|
||||||
|
Vec3d cache_size { 0., 0., 0. };
|
||||||
|
|
||||||
wxStaticText* m_move_Label = nullptr;
|
wxStaticText* m_move_Label = nullptr;
|
||||||
|
wxStaticText* m_scale_Label = nullptr;
|
||||||
|
wxStaticText* m_rotate_Label = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ObjectManipulation(wxWindow* parent);
|
ObjectManipulation(wxWindow* parent);
|
||||||
|
@ -36,31 +38,22 @@ public:
|
||||||
void reset_position_value();
|
void reset_position_value();
|
||||||
void reset_rotation_value();
|
void reset_rotation_value();
|
||||||
void reset_scale_value();
|
void reset_scale_value();
|
||||||
|
void reset_size_value();
|
||||||
|
|
||||||
void update_values();
|
|
||||||
// update position values displacements or "gizmos"
|
// update position values displacements or "gizmos"
|
||||||
void update_position_values();
|
|
||||||
void update_position_value(const Vec3d& position);
|
void update_position_value(const Vec3d& position);
|
||||||
// update scale values after scale unit changing or "gizmos"
|
// update scale values after scale unit changing or "gizmos"
|
||||||
void update_scale_values();
|
|
||||||
void update_scale_value(const Vec3d& scaling_factor);
|
void update_scale_value(const Vec3d& scaling_factor);
|
||||||
// update rotation values object selection changing
|
// update size values after scale unit changing or "gizmos"
|
||||||
void update_rotation_values();
|
void update_size_value(const Vec3d& size);
|
||||||
// update rotation value after "gizmos"
|
// update rotation value after "gizmos"
|
||||||
void update_rotation_value(double angle, Axis axis);
|
|
||||||
void update_rotation_value(const Vec3d& rotation);
|
void update_rotation_value(const Vec3d& rotation);
|
||||||
|
|
||||||
void set_uniform_scaling(const bool uniform_scale) { m_is_uniform_scale = uniform_scale; }
|
|
||||||
|
|
||||||
|
|
||||||
// change values
|
// change values
|
||||||
void change_position_value(const Vec3d& position);
|
void change_position_value(const Vec3d& position);
|
||||||
void change_rotation_value(const Vec3d& rotation);
|
void change_rotation_value(const Vec3d& rotation);
|
||||||
void change_scale_value(const Vec3d& scale);
|
void change_scale_value(const Vec3d& scale);
|
||||||
|
void change_size_value(const Vec3d& size);
|
||||||
|
|
||||||
private:
|
|
||||||
void print_cashe_value(const std::string& label, const Vec3d& value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -92,7 +92,11 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
void View3D::set_view_toolbar(GLToolbar* toolbar)
|
||||||
|
#else
|
||||||
void View3D::set_view_toolbar(GLRadioToolbar* toolbar)
|
void View3D::set_view_toolbar(GLRadioToolbar* toolbar)
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
{
|
{
|
||||||
if (m_canvas != nullptr)
|
if (m_canvas != nullptr)
|
||||||
m_canvas->set_view_toolbar(toolbar);
|
m_canvas->set_view_toolbar(toolbar);
|
||||||
|
@ -365,7 +369,11 @@ Preview::~Preview()
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#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)
|
void Preview::set_view_toolbar(GLRadioToolbar* toolbar)
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
{
|
{
|
||||||
if (m_canvas != nullptr)
|
if (m_canvas != nullptr)
|
||||||
m_canvas->set_view_toolbar(toolbar);
|
m_canvas->set_view_toolbar(toolbar);
|
||||||
|
@ -377,9 +385,6 @@ void Preview::set_number_extruders(unsigned int number_extruders)
|
||||||
if (m_number_extruders != number_extruders)
|
if (m_number_extruders != number_extruders)
|
||||||
{
|
{
|
||||||
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 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
|
int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type
|
||||||
m_choice_view_type->SetSelection(type);
|
m_choice_view_type->SetSelection(type);
|
||||||
|
@ -389,7 +394,6 @@ void Preview::set_number_extruders(unsigned int number_extruders)
|
||||||
m_preferred_color_mode = (type == tool_idx) ? "tool_or_feature" : "feature";
|
m_preferred_color_mode = (type == tool_idx) ? "tool_or_feature" : "feature";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void Preview::reset_gcode_preview_data()
|
void Preview::reset_gcode_preview_data()
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,9 +28,15 @@ class Model;
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
class GLCanvas3D;
|
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
|
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
class GLRadioToolbar;
|
class GLRadioToolbar;
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
class View3D : public wxPanel
|
class View3D : public wxPanel
|
||||||
|
@ -53,7 +59,11 @@ public:
|
||||||
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
|
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
|
||||||
GLCanvas3D* get_canvas3d() { return m_canvas; }
|
GLCanvas3D* get_canvas3d() { return m_canvas; }
|
||||||
|
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
void set_view_toolbar(GLToolbar* toolbar);
|
||||||
|
#else
|
||||||
void set_view_toolbar(GLRadioToolbar* toolbar);
|
void set_view_toolbar(GLRadioToolbar* toolbar);
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
|
||||||
void set_as_dirty();
|
void set_as_dirty();
|
||||||
void set_bed_shape(const Pointfs& shape);
|
void set_bed_shape(const Pointfs& shape);
|
||||||
|
@ -122,7 +132,11 @@ public:
|
||||||
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
|
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
|
||||||
|
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
void set_view_toolbar(GLToolbar* toolbar);
|
||||||
|
#else
|
||||||
void set_view_toolbar(GLRadioToolbar* toolbar);
|
void set_view_toolbar(GLRadioToolbar* toolbar);
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
|
|
||||||
void set_number_extruders(unsigned int number_extruders);
|
void set_number_extruders(unsigned int number_extruders);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "ProgressStatusBar.hpp"
|
#include "ProgressStatusBar.hpp"
|
||||||
#include "3DScene.hpp"
|
#include "3DScene.hpp"
|
||||||
#include "AppConfig.hpp"
|
#include "AppConfig.hpp"
|
||||||
|
#include "PrintHostDialogs.hpp"
|
||||||
#include "wxExtensions.hpp"
|
#include "wxExtensions.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
|
|
||||||
|
@ -30,7 +31,8 @@ namespace GUI {
|
||||||
MainFrame::MainFrame(const bool no_plater, const bool loaded) :
|
MainFrame::MainFrame(const bool no_plater, const bool loaded) :
|
||||||
wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE),
|
wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE),
|
||||||
m_no_plater(no_plater),
|
m_no_plater(no_plater),
|
||||||
m_loaded(loaded)
|
m_loaded(loaded),
|
||||||
|
m_printhost_queue_dlg(new PrintHostQueueDialog(this))
|
||||||
{
|
{
|
||||||
// Load the icon either from the exe, or from the ico file.
|
// Load the icon either from the exe, or from the ico file.
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
|
@ -326,7 +328,7 @@ void MainFrame::init_menubar()
|
||||||
size_t tab_offset = 0;
|
size_t tab_offset = 0;
|
||||||
if (m_plater) {
|
if (m_plater) {
|
||||||
#if ENABLE_REMOVE_TABS_FROM_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");
|
[this](wxCommandEvent&) { select_tab(0); }, "application_view_tile.png");
|
||||||
#else
|
#else
|
||||||
append_menu_item(windowMenu, wxID_ANY, L("Select Plater Tab\tCtrl+1"), L("Show the plater"),
|
append_menu_item(windowMenu, wxID_ANY, L("Select Plater Tab\tCtrl+1"), L("Show the plater"),
|
||||||
|
@ -338,22 +340,35 @@ void MainFrame::init_menubar()
|
||||||
windowMenu->AppendSeparator();
|
windowMenu->AppendSeparator();
|
||||||
}
|
}
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#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");
|
[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");
|
[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");
|
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png");
|
||||||
if (m_plater) {
|
if (m_plater) {
|
||||||
windowMenu->AppendSeparator();
|
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"); }, "");
|
[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"); }, "");
|
[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_3d->GetId());
|
||||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_preview->GetId());
|
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_preview->GetId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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
|
||||||
#else
|
#else
|
||||||
append_menu_item(windowMenu, wxID_ANY, L("Select Print Settings Tab\tCtrl+2"), L("Show the print settings"),
|
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");
|
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png");
|
||||||
|
@ -362,6 +377,10 @@ void MainFrame::init_menubar()
|
||||||
append_menu_item(windowMenu, wxID_ANY, L("Select Printer Settings Tab\tCtrl+4"), L("Show the printer settings"),
|
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");
|
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png");
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
|
|
||||||
|
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->ShowModal(); }, "arrow_up.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
// View menu
|
// View menu
|
||||||
|
|
|
@ -21,7 +21,9 @@ class ProgressStatusBar;
|
||||||
|
|
||||||
namespace GUI
|
namespace GUI
|
||||||
{
|
{
|
||||||
|
|
||||||
class Tab;
|
class Tab;
|
||||||
|
class PrintHostQueueDialog;
|
||||||
|
|
||||||
enum QuickSlice
|
enum QuickSlice
|
||||||
{
|
{
|
||||||
|
@ -52,6 +54,8 @@ class MainFrame : public wxFrame
|
||||||
wxMenuItem* m_menu_item_repeat { nullptr };
|
wxMenuItem* m_menu_item_repeat { nullptr };
|
||||||
wxMenuItem* m_menu_item_reslice_now { 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_base_name(const wxString full_name) const ;
|
||||||
std::string get_dir_name(const wxString full_name) const ;
|
std::string get_dir_name(const wxString full_name) const ;
|
||||||
|
|
||||||
|
@ -93,6 +97,8 @@ public:
|
||||||
void select_tab(size_t tab) const;
|
void select_tab(size_t tab) const;
|
||||||
void select_view(const std::string& direction);
|
void select_view(const std::string& direction);
|
||||||
|
|
||||||
|
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }
|
||||||
|
|
||||||
Plater* m_plater { nullptr };
|
Plater* m_plater { nullptr };
|
||||||
wxNotebook* m_tabpanel { nullptr };
|
wxNotebook* m_tabpanel { nullptr };
|
||||||
wxProgressDialog* m_progress_dialog { nullptr };
|
wxProgressDialog* m_progress_dialog { nullptr };
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <wx/button.h>
|
#include <wx/button.h>
|
||||||
#include <wx/statbmp.h>
|
#include <wx/statbmp.h>
|
||||||
#include <wx/scrolwin.h>
|
#include <wx/scrolwin.h>
|
||||||
|
#include <wx/clipbrd.h>
|
||||||
|
|
||||||
#include "libslic3r/libslic3r.h"
|
#include "libslic3r/libslic3r.h"
|
||||||
#include "libslic3r/Utils.hpp"
|
#include "libslic3r/Utils.hpp"
|
||||||
|
@ -61,8 +62,11 @@ MsgDialog::~MsgDialog() {}
|
||||||
|
|
||||||
// ErrorDialog
|
// ErrorDialog
|
||||||
|
|
||||||
ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) :
|
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))
|
: 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 *panel = new wxScrolledWindow(this);
|
||||||
auto *p_sizer = new wxBoxSizer(wxVERTICAL);
|
auto *p_sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
@ -77,6 +81,20 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) :
|
||||||
|
|
||||||
content_sizer->Add(panel, 1, wxEXPAND);
|
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));
|
SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT));
|
||||||
Fit();
|
Fit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,14 +50,18 @@ protected:
|
||||||
|
|
||||||
|
|
||||||
// Generic error dialog, used for displaying exceptions
|
// Generic error dialog, used for displaying exceptions
|
||||||
struct ErrorDialog : MsgDialog
|
class ErrorDialog : public MsgDialog
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
ErrorDialog(wxWindow *parent, const wxString &msg);
|
ErrorDialog(wxWindow *parent, const wxString &msg);
|
||||||
ErrorDialog(ErrorDialog &&) = delete;
|
ErrorDialog(ErrorDialog &&) = delete;
|
||||||
ErrorDialog(const ErrorDialog &) = delete;
|
ErrorDialog(const ErrorDialog &) = delete;
|
||||||
ErrorDialog &operator=(ErrorDialog &&) = delete;
|
ErrorDialog &operator=(ErrorDialog &&) = delete;
|
||||||
ErrorDialog &operator=(const ErrorDialog &) = delete;
|
ErrorDialog &operator=(const ErrorDialog &) = delete;
|
||||||
virtual ~ErrorDialog();
|
virtual ~ErrorDialog();
|
||||||
|
|
||||||
|
private:
|
||||||
|
wxString msg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -213,7 +213,7 @@ void SlicedInfo::SetTextAndShow(SlisedInfoIdx idx, const wxString& text, const w
|
||||||
}
|
}
|
||||||
|
|
||||||
PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) :
|
PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) :
|
||||||
wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY),
|
wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,-1), 0, nullptr, wxCB_READONLY),
|
||||||
preset_type(preset_type),
|
preset_type(preset_type),
|
||||||
last_selected(wxNOT_FOUND)
|
last_selected(wxNOT_FOUND)
|
||||||
{
|
{
|
||||||
|
@ -484,7 +484,7 @@ Sidebar::Sidebar(Plater *parent)
|
||||||
: wxPanel(parent), p(new priv(parent))
|
: wxPanel(parent), p(new priv(parent))
|
||||||
{
|
{
|
||||||
p->scrolled = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(400, -1));
|
p->scrolled = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(400, -1));
|
||||||
p->scrolled->SetScrollbars(0, 1, 1, 1);
|
p->scrolled->SetScrollbars(0, 20, 1, 2);
|
||||||
|
|
||||||
// Sizer in the scrolled area
|
// Sizer in the scrolled area
|
||||||
auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL);
|
auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
@ -732,8 +732,7 @@ void Sidebar::show_info_sizer()
|
||||||
p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast<int>(model_object->materials_count())));
|
p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast<int>(model_object->materials_count())));
|
||||||
|
|
||||||
auto& stats = model_object->volumes.front()->mesh.stl.stats;
|
auto& stats = model_object->volumes.front()->mesh.stl.stats;
|
||||||
auto sf = model_instance->get_scaling_factor();
|
p->object_info->info_volume->SetLabel(wxString::Format("%.2f", size(0) * size(1) * size(2)));
|
||||||
p->object_info->info_volume->SetLabel(wxString::Format("%.2f", size(0) * size(1) * size(2) * sf(0) * sf(1) * sf(2)));
|
|
||||||
p->object_info->info_facets->SetLabel(wxString::Format(_(L("%d (%d shells)")), static_cast<int>(model_object->facets_count()), stats.number_of_parts));
|
p->object_info->info_facets->SetLabel(wxString::Format(_(L("%d (%d shells)")), static_cast<int>(model_object->facets_count()), stats.number_of_parts));
|
||||||
|
|
||||||
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
|
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
|
||||||
|
@ -913,7 +912,11 @@ struct Plater::priv
|
||||||
Sidebar *sidebar;
|
Sidebar *sidebar;
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
View3D* view3D;
|
View3D* view3D;
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
GLToolbar view_toolbar;
|
||||||
|
#else
|
||||||
GLRadioToolbar view_toolbar;
|
GLRadioToolbar view_toolbar;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
#else
|
#else
|
||||||
#if !ENABLE_IMGUI
|
#if !ENABLE_IMGUI
|
||||||
wxPanel *panel3d;
|
wxPanel *panel3d;
|
||||||
|
@ -1030,6 +1033,7 @@ private:
|
||||||
bool can_decrease_instances() const;
|
bool can_decrease_instances() const;
|
||||||
bool can_split_to_objects() const;
|
bool can_split_to_objects() const;
|
||||||
bool can_split_to_volumes() const;
|
bool can_split_to_volumes() const;
|
||||||
|
bool can_split() const;
|
||||||
bool layers_height_allowed() const;
|
bool layers_height_allowed() const;
|
||||||
bool can_delete_all() const;
|
bool can_delete_all() const;
|
||||||
bool can_arrange() const;
|
bool can_arrange() const;
|
||||||
|
@ -1068,6 +1072,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||||
#endif // !ENABLE_REMOVE_TABS_FROM_PLATER
|
#endif // !ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
, delayed_scene_refresh(false)
|
, delayed_scene_refresh(false)
|
||||||
, project_filename(wxEmptyString)
|
, project_filename(wxEmptyString)
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
, view_toolbar(GLToolbar::Radio)
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
{
|
{
|
||||||
arranging.store(false);
|
arranging.store(false);
|
||||||
rotoptimizing.store(false);
|
rotoptimizing.store(false);
|
||||||
|
@ -1179,7 +1186,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||||
view3D_canvas->Bind(EVT_GLCANVAS_MODEL_UPDATE, [this](SimpleEvent&) { this->schedule_background_process(); });
|
view3D_canvas->Bind(EVT_GLCANVAS_MODEL_UPDATE, [this](SimpleEvent&) { this->schedule_background_process(); });
|
||||||
view3D_canvas->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); });
|
view3D_canvas->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); });
|
||||||
view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); });
|
view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); });
|
||||||
view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [q](Event<int> &evt) { evt.data == 1 ? q->increase_instances() : q->decrease_instances(); });
|
view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event<int> &evt)
|
||||||
|
{ if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); });
|
||||||
view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); });
|
view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); });
|
||||||
view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this);
|
view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this);
|
||||||
view3D_canvas->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event<bool> &evt) { this->sidebar->enable_buttons(evt.data); });
|
view3D_canvas->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event<bool> &evt) { this->sidebar->enable_buttons(evt.data); });
|
||||||
|
@ -1205,7 +1213,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||||
canvas3Dwidget->Bind(EVT_GLCANVAS_MODEL_UPDATE, [this](SimpleEvent&) { this->schedule_background_process(); });
|
canvas3Dwidget->Bind(EVT_GLCANVAS_MODEL_UPDATE, [this](SimpleEvent&) { this->schedule_background_process(); });
|
||||||
canvas3Dwidget->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); });
|
canvas3Dwidget->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); });
|
||||||
canvas3Dwidget->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); });
|
canvas3Dwidget->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); });
|
||||||
canvas3Dwidget->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [q](Event<int> &evt) { evt.data == 1 ? q->increase_instances() : q->decrease_instances(); });
|
canvas3Dwidget->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event<int> &evt)
|
||||||
|
{ if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); });
|
||||||
canvas3Dwidget->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); });
|
canvas3Dwidget->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); });
|
||||||
canvas3Dwidget->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this);
|
canvas3Dwidget->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this);
|
||||||
canvas3Dwidget->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event<bool> &evt) { this->sidebar->enable_buttons(evt.data); });
|
canvas3Dwidget->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event<bool> &evt) { this->sidebar->enable_buttons(evt.data); });
|
||||||
|
@ -1286,7 +1295,9 @@ void Plater::priv::select_view_3D(const std::string& name)
|
||||||
else if (name == "Preview")
|
else if (name == "Preview")
|
||||||
set_current_panel(preview);
|
set_current_panel(preview);
|
||||||
|
|
||||||
|
#if !ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
view_toolbar.set_selection(name);
|
view_toolbar.set_selection(name);
|
||||||
|
#endif // !ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
void Plater::priv::select_view(const std::string& direction)
|
void Plater::priv::select_view(const std::string& direction)
|
||||||
|
@ -1485,7 +1496,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
|
||||||
#if !ENABLE_MODELVOLUME_TRANSFORM
|
#if !ENABLE_MODELVOLUME_TRANSFORM
|
||||||
const Vec3d bed_center = Slic3r::to_3d(bed_shape.center().cast<double>(), 0.0);
|
const Vec3d bed_center = Slic3r::to_3d(bed_shape.center().cast<double>(), 0.0);
|
||||||
#endif // !ENABLE_MODELVOLUME_TRANSFORM
|
#endif // !ENABLE_MODELVOLUME_TRANSFORM
|
||||||
const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast<double>(), 1.0);
|
const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast<double>(), 1.0) - 2.0 * Vec3d::Ones();
|
||||||
|
|
||||||
bool need_arrange = false;
|
bool need_arrange = false;
|
||||||
bool scaled_down = false;
|
bool scaled_down = false;
|
||||||
|
@ -1517,9 +1528,10 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
|
||||||
if (max_ratio > 10000) {
|
if (max_ratio > 10000) {
|
||||||
// the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
|
// the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
|
||||||
// so scale down the mesh
|
// so scale down the mesh
|
||||||
// const Vec3d inverse = ratio.cwiseInverse();
|
double inv = 1. / max_ratio;
|
||||||
// object->scale(inverse);
|
object->scale_mesh(Vec3d(inv, inv, inv));
|
||||||
object->scale(ratio.cwiseInverse());
|
object->origin_translation = Vec3d::Zero();
|
||||||
|
object->center_around_origin();
|
||||||
scaled_down = true;
|
scaled_down = true;
|
||||||
} else if (max_ratio > 5) {
|
} else if (max_ratio > 5) {
|
||||||
const Vec3d inverse = ratio.cwiseInverse();
|
const Vec3d inverse = ratio.cwiseInverse();
|
||||||
|
@ -1644,8 +1656,8 @@ void Plater::priv::selection_changed()
|
||||||
view3D->enable_toolbar_item("delete", can_delete_object());
|
view3D->enable_toolbar_item("delete", can_delete_object());
|
||||||
view3D->enable_toolbar_item("more", can_increase_instances());
|
view3D->enable_toolbar_item("more", can_increase_instances());
|
||||||
view3D->enable_toolbar_item("fewer", can_decrease_instances());
|
view3D->enable_toolbar_item("fewer", can_decrease_instances());
|
||||||
view3D->enable_toolbar_item("splitobjects", can_split_to_objects());
|
view3D->enable_toolbar_item("splitobjects", can_split/*_to_objects*/());
|
||||||
view3D->enable_toolbar_item("splitvolumes", can_split_to_volumes());
|
view3D->enable_toolbar_item("splitvolumes", can_split/*_to_volumes*/());
|
||||||
view3D->enable_toolbar_item("layersediting", layers_height_allowed());
|
view3D->enable_toolbar_item("layersediting", layers_height_allowed());
|
||||||
// forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears)
|
// forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears)
|
||||||
view3D->render();
|
view3D->render();
|
||||||
|
@ -1653,8 +1665,8 @@ void Plater::priv::selection_changed()
|
||||||
this->canvas3D->enable_toolbar_item("delete", can_delete_object());
|
this->canvas3D->enable_toolbar_item("delete", can_delete_object());
|
||||||
this->canvas3D->enable_toolbar_item("more", can_increase_instances());
|
this->canvas3D->enable_toolbar_item("more", can_increase_instances());
|
||||||
this->canvas3D->enable_toolbar_item("fewer", can_decrease_instances());
|
this->canvas3D->enable_toolbar_item("fewer", can_decrease_instances());
|
||||||
this->canvas3D->enable_toolbar_item("splitobjects", can_split_to_objects());
|
this->canvas3D->enable_toolbar_item("splitobjects", can_split/*_to_objects*/());
|
||||||
this->canvas3D->enable_toolbar_item("splitvolumes", can_split_to_volumes());
|
this->canvas3D->enable_toolbar_item("splitvolumes", can_split/*_to_volumes*/());
|
||||||
this->canvas3D->enable_toolbar_item("layersediting", layers_height_allowed());
|
this->canvas3D->enable_toolbar_item("layersediting", layers_height_allowed());
|
||||||
// forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears)
|
// forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears)
|
||||||
this->canvas3D->render();
|
this->canvas3D->render();
|
||||||
|
@ -2446,8 +2458,8 @@ void Plater::priv::on_action_layersediting(SimpleEvent&)
|
||||||
|
|
||||||
void Plater::priv::on_object_select(SimpleEvent& evt)
|
void Plater::priv::on_object_select(SimpleEvent& evt)
|
||||||
{
|
{
|
||||||
selection_changed();
|
|
||||||
wxGetApp().obj_list()->update_selections();
|
wxGetApp().obj_list()->update_selections();
|
||||||
|
selection_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::priv::on_viewport_changed(SimpleEvent& evt)
|
void Plater::priv::on_viewport_changed(SimpleEvent& evt)
|
||||||
|
@ -2597,9 +2609,9 @@ bool Plater::priv::complit_init_object_menu()
|
||||||
// ui updates needs to be binded to the parent panel
|
// ui updates needs to be binded to the parent panel
|
||||||
if (q != nullptr)
|
if (q != nullptr)
|
||||||
{
|
{
|
||||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects() || can_split_to_volumes()); }, item_split->GetId());
|
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects() || can_split_to_volumes*/()); }, item_split->GetId());
|
||||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects()); }, item_split_objects->GetId());
|
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split_objects->GetId());
|
||||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_volumes()); }, item_split_volumes->GetId());
|
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split_volumes->GetId());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2618,7 +2630,7 @@ bool Plater::priv::complit_init_sla_object_menu()
|
||||||
// ui updates needs to be binded to the parent panel
|
// ui updates needs to be binded to the parent panel
|
||||||
if (q != nullptr)
|
if (q != nullptr)
|
||||||
{
|
{
|
||||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects()); }, item_split->GetId());
|
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split->GetId());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -2637,7 +2649,7 @@ bool Plater::priv::complit_init_part_menu()
|
||||||
// ui updates needs to be binded to the parent panel
|
// ui updates needs to be binded to the parent panel
|
||||||
if (q != nullptr)
|
if (q != nullptr)
|
||||||
{
|
{
|
||||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_volumes()); }, item_split->GetId());
|
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split->GetId());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -2646,9 +2658,58 @@ bool Plater::priv::complit_init_part_menu()
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
void Plater::priv::init_view_toolbar()
|
void Plater::priv::init_view_toolbar()
|
||||||
{
|
{
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
ItemsIconsTexture::Metadata icons_data;
|
||||||
|
icons_data.filename = "view_toolbar.png";
|
||||||
|
icons_data.icon_size = 64;
|
||||||
|
icons_data.icon_border_size = 0;
|
||||||
|
icons_data.icon_gap_size = 0;
|
||||||
|
|
||||||
|
BackgroundTexture::Metadata background_data;
|
||||||
|
background_data.filename = "toolbar_background.png";
|
||||||
|
background_data.left = 16;
|
||||||
|
background_data.top = 16;
|
||||||
|
background_data.right = 16;
|
||||||
|
background_data.bottom = 16;
|
||||||
|
|
||||||
|
if (!view_toolbar.init(icons_data, background_data))
|
||||||
|
#else
|
||||||
if (!view_toolbar.init("view_toolbar.png", 64, 0, 0))
|
if (!view_toolbar.init("view_toolbar.png", 64, 0, 0))
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
view_toolbar.set_layout_orientation(GLToolbar::Layout::Bottom);
|
||||||
|
view_toolbar.set_border(5.0f);
|
||||||
|
view_toolbar.set_gap_size(1.0f);
|
||||||
|
|
||||||
|
GLToolbarItem::Data item;
|
||||||
|
|
||||||
|
item.name = "3D";
|
||||||
|
item.tooltip = GUI::L_str("3D editor view");
|
||||||
|
item.sprite_id = 0;
|
||||||
|
item.action_event = EVT_GLVIEWTOOLBAR_3D;
|
||||||
|
item.is_toggable = false;
|
||||||
|
if (!view_toolbar.add_item(item))
|
||||||
|
return;
|
||||||
|
|
||||||
|
item.name = "Preview";
|
||||||
|
item.tooltip = GUI::L_str("Preview");
|
||||||
|
item.sprite_id = 1;
|
||||||
|
item.action_event = EVT_GLVIEWTOOLBAR_PREVIEW;
|
||||||
|
item.is_toggable = false;
|
||||||
|
if (!view_toolbar.add_item(item))
|
||||||
|
return;
|
||||||
|
|
||||||
|
view_toolbar.enable_item("3D");
|
||||||
|
view_toolbar.enable_item("Preview");
|
||||||
|
|
||||||
|
view_toolbar.select_item("3D");
|
||||||
|
view_toolbar.set_enabled(true);
|
||||||
|
|
||||||
|
view3D->set_view_toolbar(&view_toolbar);
|
||||||
|
preview->set_view_toolbar(&view_toolbar);
|
||||||
|
#else
|
||||||
GLRadioToolbarItem::Data item;
|
GLRadioToolbarItem::Data item;
|
||||||
|
|
||||||
item.name = "3D";
|
item.name = "3D";
|
||||||
|
@ -2669,6 +2730,7 @@ void Plater::priv::init_view_toolbar()
|
||||||
preview->set_view_toolbar(&view_toolbar);
|
preview->set_view_toolbar(&view_toolbar);
|
||||||
|
|
||||||
view_toolbar.set_selection("3D");
|
view_toolbar.set_selection("3D");
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
}
|
}
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
|
|
||||||
|
@ -2705,6 +2767,13 @@ bool Plater::priv::can_split_to_volumes() const
|
||||||
return sidebar->obj_list()->is_splittable();
|
return sidebar->obj_list()->is_splittable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Plater::priv::can_split() const
|
||||||
|
{
|
||||||
|
if (printer_technology == ptSLA)
|
||||||
|
return false;
|
||||||
|
return sidebar->obj_list()->is_splittable();
|
||||||
|
}
|
||||||
|
|
||||||
bool Plater::priv::layers_height_allowed() const
|
bool Plater::priv::layers_height_allowed() const
|
||||||
{
|
{
|
||||||
int obj_idx = get_selected_object_idx();
|
int obj_idx = get_selected_object_idx();
|
||||||
|
@ -3086,7 +3155,7 @@ void Plater::send_gcode()
|
||||||
}
|
}
|
||||||
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
|
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
|
||||||
|
|
||||||
Slic3r::PrintHostSendDialog dlg(default_output_file);
|
PrintHostSendDialog dlg(default_output_file);
|
||||||
if (dlg.ShowModal() == wxID_OK) {
|
if (dlg.ShowModal() == wxID_OK) {
|
||||||
upload_job.upload_data.upload_path = dlg.filename();
|
upload_job.upload_data.upload_path = dlg.filename();
|
||||||
upload_job.upload_data.start_print = dlg.start_print();
|
upload_job.upload_data.start_print = dlg.start_print();
|
||||||
|
@ -3178,6 +3247,8 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
if (p->preview) p->preview->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values);
|
if (p->preview) p->preview->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values);
|
||||||
update_scheduled = true;
|
update_scheduled = true;
|
||||||
|
} else if (opt_key == "host_type" && this->p->printer_technology == ptSLA) {
|
||||||
|
p->config->option<ConfigOptionEnum<PrintHostType>>(opt_key)->value = htSL1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -366,7 +366,8 @@ const std::vector<std::string>& Preset::printer_options()
|
||||||
"host_type", "print_host", "printhost_apikey", "printhost_cafile",
|
"host_type", "print_host", "printhost_apikey", "printhost_cafile",
|
||||||
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
|
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
|
||||||
"between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction",
|
"between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction",
|
||||||
"cooling_tube_length", "parking_pos_retraction", "extra_loading_move", "max_print_height", "default_print_profile", "inherits",
|
"cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "max_print_height",
|
||||||
|
"default_print_profile", "inherits",
|
||||||
"remaining_times", "silent_mode", "machine_max_acceleration_extruding", "machine_max_acceleration_retracting",
|
"remaining_times", "silent_mode", "machine_max_acceleration_extruding", "machine_max_acceleration_retracting",
|
||||||
"machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e",
|
"machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e",
|
||||||
"machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e",
|
"machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e",
|
||||||
|
@ -455,6 +456,7 @@ const std::vector<std::string>& Preset::sla_printer_options()
|
||||||
"display_width", "display_height", "display_pixels_x", "display_pixels_y",
|
"display_width", "display_height", "display_pixels_x", "display_pixels_y",
|
||||||
"display_orientation",
|
"display_orientation",
|
||||||
"printer_correction",
|
"printer_correction",
|
||||||
|
"print_host", "printhost_apikey", "printhost_cafile",
|
||||||
"printer_notes",
|
"printer_notes",
|
||||||
"inherits"
|
"inherits"
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,20 +1,28 @@
|
||||||
#include "PrintHostDialogs.hpp"
|
#include "PrintHostDialogs.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <wx/frame.h>
|
#include <wx/frame.h>
|
||||||
#include <wx/event.h>
|
|
||||||
#include <wx/progdlg.h>
|
#include <wx/progdlg.h>
|
||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
#include <wx/stattext.h>
|
#include <wx/stattext.h>
|
||||||
#include <wx/textctrl.h>
|
#include <wx/textctrl.h>
|
||||||
#include <wx/checkbox.h>
|
#include <wx/checkbox.h>
|
||||||
|
#include <wx/button.h>
|
||||||
|
#include <wx/dataview.h>
|
||||||
|
#include <wx/wupdlock.h>
|
||||||
|
#include <wx/debug.h>
|
||||||
|
|
||||||
#include "slic3r/GUI/GUI.hpp"
|
#include "GUI.hpp"
|
||||||
#include "slic3r/GUI/MsgDialog.hpp"
|
#include "MsgDialog.hpp"
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "I18N.hpp"
|
||||||
|
#include "../Utils/PrintHost.hpp"
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
namespace GUI {
|
||||||
|
|
||||||
|
|
||||||
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path)
|
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path)
|
||||||
: MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE)
|
: MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE)
|
||||||
|
@ -45,5 +53,95 @@ fs::path PrintHostSendDialog::filename() const
|
||||||
|
|
||||||
bool PrintHostSendDialog::start_print() const
|
bool PrintHostSendDialog::start_print() const
|
||||||
{
|
{
|
||||||
return box_print->GetValue(); }
|
return box_print->GetValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||||
|
wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
||||||
|
|
||||||
|
PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id)
|
||||||
|
: wxEvent(winid, eventType)
|
||||||
|
, job_id(job_id)
|
||||||
|
{}
|
||||||
|
|
||||||
|
PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id, int progress)
|
||||||
|
: wxEvent(winid, eventType)
|
||||||
|
, job_id(job_id)
|
||||||
|
, progress(progress)
|
||||||
|
{}
|
||||||
|
|
||||||
|
PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id, wxString error)
|
||||||
|
: wxEvent(winid, eventType)
|
||||||
|
, job_id(job_id)
|
||||||
|
, error(std::move(error))
|
||||||
|
{}
|
||||||
|
|
||||||
|
wxEvent *PrintHostQueueDialog::Event::Clone() const
|
||||||
|
{
|
||||||
|
return new Event(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
||||||
|
: wxDialog(parent, wxID_ANY, _(L("Print host upload queue")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||||
|
, on_progress_evt(this, EVT_PRINTHOST_PROGRESS, &PrintHostQueueDialog::on_progress, this)
|
||||||
|
, on_error_evt(this, EVT_PRINTHOST_ERROR, &PrintHostQueueDialog::on_error, this)
|
||||||
|
{
|
||||||
|
enum { HEIGHT = 800, WIDTH = 400, SPACING = 5 };
|
||||||
|
|
||||||
|
SetMinSize(wxSize(HEIGHT, WIDTH));
|
||||||
|
|
||||||
|
auto *topsizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
|
job_list = new wxDataViewListCtrl(this, wxID_ANY);
|
||||||
|
job_list->AppendTextColumn("ID", wxDATAVIEW_CELL_INERT);
|
||||||
|
job_list->AppendProgressColumn("Progress", wxDATAVIEW_CELL_INERT);
|
||||||
|
job_list->AppendTextColumn("Status", wxDATAVIEW_CELL_INERT);
|
||||||
|
job_list->AppendTextColumn("Host", wxDATAVIEW_CELL_INERT);
|
||||||
|
job_list->AppendTextColumn("Filename", wxDATAVIEW_CELL_INERT);
|
||||||
|
|
||||||
|
auto *btnsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
auto *btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected")));
|
||||||
|
auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close")));
|
||||||
|
btnsizer->Add(btn_cancel, 0, wxRIGHT, SPACING);
|
||||||
|
btnsizer->AddStretchSpacer();
|
||||||
|
btnsizer->Add(btn_close);
|
||||||
|
|
||||||
|
topsizer->Add(job_list, 1, wxEXPAND | wxBOTTOM, SPACING);
|
||||||
|
topsizer->Add(btnsizer, 0, wxEXPAND);
|
||||||
|
SetSizer(topsizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostQueueDialog::append_job(const PrintHostJob &job)
|
||||||
|
{
|
||||||
|
wxCHECK_RET(!job.empty(), "PrintHostQueueDialog: Attempt to append an empty job");
|
||||||
|
|
||||||
|
wxVector<wxVariant> fields;
|
||||||
|
fields.push_back(wxVariant(wxString::Format("%d", job_list->GetItemCount() + 1)));
|
||||||
|
fields.push_back(wxVariant(0));
|
||||||
|
fields.push_back(wxVariant(_(L("Enqueued"))));
|
||||||
|
fields.push_back(wxVariant(job.printhost->get_host()));
|
||||||
|
fields.push_back(wxVariant(job.upload_data.upload_path.string()));
|
||||||
|
job_list->AppendItem(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostQueueDialog::on_progress(Event &evt)
|
||||||
|
{
|
||||||
|
wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list");
|
||||||
|
|
||||||
|
const wxVariant status(evt.progress < 100 ? _(L("Uploading")) : _(L("Complete")));
|
||||||
|
|
||||||
|
job_list->SetValue(wxVariant(evt.progress), evt.job_id, 1);
|
||||||
|
job_list->SetValue(status, evt.job_id, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostQueueDialog::on_error(Event &evt)
|
||||||
|
{
|
||||||
|
wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list");
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
|
@ -2,24 +2,27 @@
|
||||||
#define slic3r_PrintHostSendDialog_hpp_
|
#define slic3r_PrintHostSendDialog_hpp_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
#include <wx/frame.h>
|
|
||||||
#include <wx/event.h>
|
#include <wx/event.h>
|
||||||
#include <wx/progdlg.h>
|
#include <wx/dialog.h>
|
||||||
#include <wx/sizer.h>
|
|
||||||
#include <wx/stattext.h>
|
|
||||||
#include <wx/textctrl.h>
|
|
||||||
#include <wx/checkbox.h>
|
|
||||||
|
|
||||||
#include "slic3r/GUI/GUI.hpp"
|
#include "GUI.hpp"
|
||||||
#include "slic3r/GUI/MsgDialog.hpp"
|
#include "GUI_Utils.hpp"
|
||||||
|
#include "MsgDialog.hpp"
|
||||||
|
#include "../Utils/PrintHost.hpp"
|
||||||
|
|
||||||
|
class wxTextCtrl;
|
||||||
|
class wxCheckBox;
|
||||||
|
class wxDataViewListCtrl;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
struct PrintHostJob;
|
||||||
|
|
||||||
|
namespace GUI {
|
||||||
|
|
||||||
|
|
||||||
class PrintHostSendDialog : public GUI::MsgDialog
|
class PrintHostSendDialog : public GUI::MsgDialog
|
||||||
{
|
{
|
||||||
|
@ -38,12 +41,38 @@ private:
|
||||||
class PrintHostQueueDialog : public wxDialog
|
class PrintHostQueueDialog : public wxDialog
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PrintHostQueueDialog();
|
class Event : public wxEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
size_t job_id;
|
||||||
|
int progress = 0; // in percent
|
||||||
|
wxString error;
|
||||||
|
|
||||||
private:
|
Event(wxEventType eventType, int winid, size_t job_id);
|
||||||
|
Event(wxEventType eventType, int winid, size_t job_id, int progress);
|
||||||
|
Event(wxEventType eventType, int winid, size_t job_id, wxString error);
|
||||||
|
|
||||||
|
virtual wxEvent *Clone() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
PrintHostQueueDialog(wxWindow *parent);
|
||||||
|
|
||||||
|
void append_job(const PrintHostJob &job);
|
||||||
|
private:
|
||||||
|
wxDataViewListCtrl *job_list;
|
||||||
|
// Note: EventGuard prevents delivery of progress evts to a freed PrintHostQueueDialog
|
||||||
|
EventGuard on_progress_evt;
|
||||||
|
EventGuard on_error_evt;
|
||||||
|
|
||||||
|
void on_progress(Event&);
|
||||||
|
void on_error(Event&);
|
||||||
|
};
|
||||||
|
|
||||||
|
wxDECLARE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||||
|
wxDECLARE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
||||||
|
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -270,7 +270,7 @@ Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::str
|
||||||
auto panel = this;
|
auto panel = this;
|
||||||
#endif
|
#endif
|
||||||
PageShp page(new Page(panel, title, icon_idx));
|
PageShp page(new Page(panel, title, icon_idx));
|
||||||
page->SetScrollbars(1, 1, 1, 2);
|
page->SetScrollbars(1, 20, 1, 2);
|
||||||
page->Hide();
|
page->Hide();
|
||||||
m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5);
|
m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5);
|
||||||
|
|
||||||
|
@ -1538,6 +1538,108 @@ bool Tab::current_preset_is_dirty()
|
||||||
return m_presets->current_is_dirty();
|
return m_presets->current_is_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
|
||||||
|
{
|
||||||
|
const bool sla = m_presets->get_selected_preset().printer_technology() == ptSLA;
|
||||||
|
|
||||||
|
// Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment)
|
||||||
|
if (! sla) {
|
||||||
|
optgroup->append_single_option_line("host_type");
|
||||||
|
} else {
|
||||||
|
m_config->option<ConfigOptionEnum<PrintHostType>>("host_type", true)->value = htSL1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto printhost_browse = [this, optgroup] (wxWindow* parent) {
|
||||||
|
|
||||||
|
// TODO: SLA
|
||||||
|
|
||||||
|
auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||||
|
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
|
||||||
|
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
sizer->Add(btn);
|
||||||
|
|
||||||
|
btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) {
|
||||||
|
BonjourDialog dialog(parent);
|
||||||
|
if (dialog.show_and_lookup()) {
|
||||||
|
optgroup->set_value("print_host", std::move(dialog.get_selected()), true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return sizer;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto print_host_test = [this](wxWindow* parent) {
|
||||||
|
auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
|
||||||
|
wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
|
||||||
|
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG));
|
||||||
|
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
sizer->Add(btn);
|
||||||
|
|
||||||
|
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {
|
||||||
|
std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config));
|
||||||
|
if (! host) {
|
||||||
|
const auto text = wxString::Format("%s",
|
||||||
|
_(L("Could not get a valid Printer Host reference")));
|
||||||
|
show_error(this, text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wxString msg;
|
||||||
|
if (host->test(msg)) {
|
||||||
|
show_info(this, host->get_test_ok_msg(), _(L("Success!")));
|
||||||
|
} else {
|
||||||
|
show_error(this, host->get_test_failed_msg(msg));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return sizer;
|
||||||
|
};
|
||||||
|
|
||||||
|
Line host_line = optgroup->create_single_option_line("print_host");
|
||||||
|
host_line.append_widget(printhost_browse);
|
||||||
|
host_line.append_widget(print_host_test);
|
||||||
|
optgroup->append_line(host_line);
|
||||||
|
optgroup->append_single_option_line("printhost_apikey");
|
||||||
|
|
||||||
|
if (Http::ca_file_supported()) {
|
||||||
|
|
||||||
|
Line cafile_line = optgroup->create_single_option_line("printhost_cafile");
|
||||||
|
|
||||||
|
auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) {
|
||||||
|
auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||||
|
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
|
||||||
|
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
sizer->Add(btn);
|
||||||
|
|
||||||
|
btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e) {
|
||||||
|
static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*"));
|
||||||
|
wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||||
|
if (openFileDialog.ShowModal() != wxID_CANCEL) {
|
||||||
|
optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return sizer;
|
||||||
|
};
|
||||||
|
|
||||||
|
cafile_line.append_widget(printhost_cafile_browse);
|
||||||
|
optgroup->append_line(cafile_line);
|
||||||
|
|
||||||
|
auto printhost_cafile_hint = [this, optgroup] (wxWindow* parent) {
|
||||||
|
auto txt = new wxStaticText(parent, wxID_ANY,
|
||||||
|
_(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.")));
|
||||||
|
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
sizer->Add(txt);
|
||||||
|
return sizer;
|
||||||
|
};
|
||||||
|
|
||||||
|
Line cafile_hint { "", "" };
|
||||||
|
cafile_hint.full_width = 1;
|
||||||
|
cafile_hint.widget = std::move(printhost_cafile_hint);
|
||||||
|
optgroup->append_line(cafile_hint);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TabPrinter::build()
|
void TabPrinter::build()
|
||||||
{
|
{
|
||||||
m_presets = &m_preset_bundle->printers;
|
m_presets = &m_preset_bundle->printers;
|
||||||
|
@ -1665,96 +1767,8 @@ void TabPrinter::build_fff()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
optgroup = page->new_optgroup(_(L("Printer Host upload")));
|
optgroup = page->new_optgroup(_(L("Print Host upload")));
|
||||||
|
build_printhost(optgroup.get());
|
||||||
optgroup->append_single_option_line("host_type");
|
|
||||||
|
|
||||||
auto printhost_browse = [this, optgroup] (wxWindow* parent) {
|
|
||||||
auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
|
||||||
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
|
|
||||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
|
||||||
sizer->Add(btn);
|
|
||||||
|
|
||||||
btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) {
|
|
||||||
BonjourDialog dialog(parent);
|
|
||||||
if (dialog.show_and_lookup()) {
|
|
||||||
optgroup->set_value("print_host", std::move(dialog.get_selected()), true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return sizer;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto print_host_test = [this](wxWindow* parent) {
|
|
||||||
auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
|
|
||||||
wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
|
|
||||||
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG));
|
|
||||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
|
||||||
sizer->Add(btn);
|
|
||||||
|
|
||||||
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {
|
|
||||||
std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config));
|
|
||||||
if (! host) {
|
|
||||||
const auto text = wxString::Format("%s",
|
|
||||||
_(L("Could not get a valid Printer Host reference")));
|
|
||||||
show_error(this, text);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wxString msg;
|
|
||||||
if (host->test(msg)) {
|
|
||||||
show_info(this, host->get_test_ok_msg(), _(L("Success!")));
|
|
||||||
} else {
|
|
||||||
show_error(this, host->get_test_failed_msg(msg));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return sizer;
|
|
||||||
};
|
|
||||||
|
|
||||||
Line host_line = optgroup->create_single_option_line("print_host");
|
|
||||||
host_line.append_widget(printhost_browse);
|
|
||||||
host_line.append_widget(print_host_test);
|
|
||||||
optgroup->append_line(host_line);
|
|
||||||
optgroup->append_single_option_line("printhost_apikey");
|
|
||||||
|
|
||||||
if (Http::ca_file_supported()) {
|
|
||||||
|
|
||||||
Line cafile_line = optgroup->create_single_option_line("printhost_cafile");
|
|
||||||
|
|
||||||
auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) {
|
|
||||||
auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
|
||||||
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
|
|
||||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
|
||||||
sizer->Add(btn);
|
|
||||||
|
|
||||||
btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e) {
|
|
||||||
static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*"));
|
|
||||||
wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
|
||||||
if (openFileDialog.ShowModal() != wxID_CANCEL) {
|
|
||||||
optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return sizer;
|
|
||||||
};
|
|
||||||
|
|
||||||
cafile_line.append_widget(printhost_cafile_browse);
|
|
||||||
optgroup->append_line(cafile_line);
|
|
||||||
|
|
||||||
auto printhost_cafile_hint = [this, optgroup] (wxWindow* parent) {
|
|
||||||
auto txt = new wxStaticText(parent, wxID_ANY,
|
|
||||||
_(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.")));
|
|
||||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
|
||||||
sizer->Add(txt);
|
|
||||||
return sizer;
|
|
||||||
};
|
|
||||||
|
|
||||||
Line cafile_hint { "", "" };
|
|
||||||
cafile_hint.full_width = 1;
|
|
||||||
cafile_hint.widget = std::move(printhost_cafile_hint);
|
|
||||||
optgroup->append_line(cafile_hint);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
optgroup = page->new_optgroup(_(L("Firmware")));
|
optgroup = page->new_optgroup(_(L("Firmware")));
|
||||||
optgroup->append_single_option_line("gcode_flavor");
|
optgroup->append_single_option_line("gcode_flavor");
|
||||||
|
@ -1897,6 +1911,9 @@ void TabPrinter::build_sla()
|
||||||
}
|
}
|
||||||
optgroup->append_line(line);
|
optgroup->append_line(line);
|
||||||
|
|
||||||
|
optgroup = page->new_optgroup(_(L("Print Host upload")));
|
||||||
|
build_printhost(optgroup.get());
|
||||||
|
|
||||||
page = add_options_page(_(L("Notes")), "note.png");
|
page = add_options_page(_(L("Notes")), "note.png");
|
||||||
optgroup = page->new_optgroup(_(L("Notes")), 0);
|
optgroup = page->new_optgroup(_(L("Notes")), 0);
|
||||||
option = optgroup->get_option("printer_notes");
|
option = optgroup->get_option("printer_notes");
|
||||||
|
@ -2041,6 +2058,7 @@ void TabPrinter::build_extruder_pages()
|
||||||
optgroup->append_single_option_line("cooling_tube_length");
|
optgroup->append_single_option_line("cooling_tube_length");
|
||||||
optgroup->append_single_option_line("parking_pos_retraction");
|
optgroup->append_single_option_line("parking_pos_retraction");
|
||||||
optgroup->append_single_option_line("extra_loading_move");
|
optgroup->append_single_option_line("extra_loading_move");
|
||||||
|
optgroup->append_single_option_line("high_current_on_filament_swap");
|
||||||
m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
|
m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
|
||||||
m_has_single_extruder_MM_page = true;
|
m_has_single_extruder_MM_page = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -325,6 +325,8 @@ class TabPrinter : public Tab
|
||||||
|
|
||||||
std::vector<PageShp> m_pages_fff;
|
std::vector<PageShp> m_pages_fff;
|
||||||
std::vector<PageShp> m_pages_sla;
|
std::vector<PageShp> m_pages_sla;
|
||||||
|
|
||||||
|
void build_printhost(ConfigOptionsGroup *optgroup);
|
||||||
public:
|
public:
|
||||||
wxButton* m_serial_test_btn = nullptr;
|
wxButton* m_serial_test_btn = nullptr;
|
||||||
wxButton* m_print_host_test_btn = nullptr;
|
wxButton* m_print_host_test_btn = nullptr;
|
||||||
|
|
|
@ -953,6 +953,44 @@ void PrusaObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType&
|
||||||
type = itUndef;
|
type = itUndef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int PrusaObjectDataViewModel::GetRowByItem(const wxDataViewItem& item) const
|
||||||
|
{
|
||||||
|
if (m_objects.empty())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int row_num = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < m_objects.size(); i++)
|
||||||
|
{
|
||||||
|
row_num++;
|
||||||
|
if (item == wxDataViewItem(m_objects[i]))
|
||||||
|
return row_num;
|
||||||
|
|
||||||
|
for (int j = 0; j < m_objects[i]->GetChildCount(); j++)
|
||||||
|
{
|
||||||
|
row_num++;
|
||||||
|
PrusaObjectDataViewModelNode* cur_node = m_objects[i]->GetNthChild(j);
|
||||||
|
if (item == wxDataViewItem(cur_node))
|
||||||
|
return row_num;
|
||||||
|
|
||||||
|
if (cur_node->m_type == itVolume && cur_node->GetChildCount() == 1)
|
||||||
|
row_num++;
|
||||||
|
if (cur_node->m_type == itInstanceRoot)
|
||||||
|
{
|
||||||
|
row_num++;
|
||||||
|
for (int t = 0; t < cur_node->GetChildCount(); t++)
|
||||||
|
{
|
||||||
|
row_num++;
|
||||||
|
if (item == wxDataViewItem(cur_node->GetNthChild(t)))
|
||||||
|
return row_num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
wxString PrusaObjectDataViewModel::GetName(const wxDataViewItem &item) const
|
wxString PrusaObjectDataViewModel::GetName(const wxDataViewItem &item) const
|
||||||
{
|
{
|
||||||
PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
|
PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
|
||||||
|
@ -1240,6 +1278,16 @@ IMPLEMENT_VARIANT_OBJECT(PrusaDataViewBitmapText)
|
||||||
// PrusaIconTextRenderer
|
// PrusaIconTextRenderer
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||||
|
PrusaBitmapTextRenderer::PrusaBitmapTextRenderer(wxDataViewCellMode mode /*= wxDATAVIEW_CELL_EDITABLE*/,
|
||||||
|
int align /*= wxDVR_DEFAULT_ALIGNMENT*/):
|
||||||
|
wxDataViewRenderer(wxT("PrusaDataViewBitmapText"), mode, align)
|
||||||
|
{
|
||||||
|
SetMode(mode);
|
||||||
|
SetAlignment(align);
|
||||||
|
}
|
||||||
|
#endif // ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||||
|
|
||||||
bool PrusaBitmapTextRenderer::SetValue(const wxVariant &value)
|
bool PrusaBitmapTextRenderer::SetValue(const wxVariant &value)
|
||||||
{
|
{
|
||||||
m_value << value;
|
m_value << value;
|
||||||
|
@ -1251,6 +1299,13 @@ bool PrusaBitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY
|
||||||
|
wxString PrusaBitmapTextRenderer::GetAccessibleDescription() const
|
||||||
|
{
|
||||||
|
return m_value.GetText();
|
||||||
|
}
|
||||||
|
#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||||
|
|
||||||
bool PrusaBitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state)
|
bool PrusaBitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state)
|
||||||
{
|
{
|
||||||
int xoffset = 0;
|
int xoffset = 0;
|
||||||
|
@ -1291,12 +1346,12 @@ wxWindow* PrusaBitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect lab
|
||||||
|
|
||||||
PrusaDataViewBitmapText data;
|
PrusaDataViewBitmapText data;
|
||||||
data << value;
|
data << value;
|
||||||
m_bmp_from_editing_item = data.GetBitmap();
|
|
||||||
m_was_unusable_symbol = false;
|
m_was_unusable_symbol = false;
|
||||||
|
|
||||||
wxPoint position = labelRect.GetPosition();
|
wxPoint position = labelRect.GetPosition();
|
||||||
if (m_bmp_from_editing_item.IsOk()) {
|
if (data.GetBitmap().IsOk()) {
|
||||||
const int bmp_width = m_bmp_from_editing_item.GetWidth();
|
const int bmp_width = data.GetBitmap().GetWidth();
|
||||||
position.x += bmp_width;
|
position.x += bmp_width;
|
||||||
labelRect.SetWidth(labelRect.GetWidth() - bmp_width);
|
labelRect.SetWidth(labelRect.GetWidth() - bmp_width);
|
||||||
}
|
}
|
||||||
|
@ -1304,6 +1359,7 @@ wxWindow* PrusaBitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect lab
|
||||||
wxTextCtrl* text_editor = new wxTextCtrl(parent, wxID_ANY, data.GetText(),
|
wxTextCtrl* text_editor = new wxTextCtrl(parent, wxID_ANY, data.GetText(),
|
||||||
position, labelRect.GetSize(), wxTE_PROCESS_ENTER);
|
position, labelRect.GetSize(), wxTE_PROCESS_ENTER);
|
||||||
text_editor->SetInsertionPointEnd();
|
text_editor->SetInsertionPointEnd();
|
||||||
|
text_editor->SelectAll();
|
||||||
|
|
||||||
return text_editor;
|
return text_editor;
|
||||||
}
|
}
|
||||||
|
@ -1323,7 +1379,17 @@ bool PrusaBitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant&
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
value << PrusaDataViewBitmapText(text_editor->GetValue(), m_bmp_from_editing_item);
|
// The icon can't be edited so get its old value and reuse it.
|
||||||
|
wxVariant valueOld;
|
||||||
|
GetView()->GetModel()->GetValue(valueOld, m_item, 0);
|
||||||
|
|
||||||
|
PrusaDataViewBitmapText bmpText;
|
||||||
|
bmpText << valueOld;
|
||||||
|
|
||||||
|
// But replace the text with the value entered by user.
|
||||||
|
bmpText.SetText(text_editor->GetValue());
|
||||||
|
|
||||||
|
value << bmpText;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -463,6 +463,7 @@ public:
|
||||||
int GetVolumeIdByItem(const wxDataViewItem& item) const;
|
int GetVolumeIdByItem(const wxDataViewItem& item) const;
|
||||||
int GetInstanceIdByItem(const wxDataViewItem& item) const;
|
int GetInstanceIdByItem(const wxDataViewItem& item) const;
|
||||||
void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx);
|
void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx);
|
||||||
|
int GetRowByItem(const wxDataViewItem& item) const;
|
||||||
bool IsEmpty() { return m_objects.empty(); }
|
bool IsEmpty() { return m_objects.empty(); }
|
||||||
|
|
||||||
// helper method for wxLog
|
// helper method for wxLog
|
||||||
|
@ -518,21 +519,44 @@ public:
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// PrusaBitmapTextRenderer
|
// PrusaBitmapTextRenderer
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||||
|
class PrusaBitmapTextRenderer : public wxDataViewRenderer
|
||||||
|
#else
|
||||||
class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer
|
class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer
|
||||||
|
#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PrusaBitmapTextRenderer( wxDataViewCellMode mode = wxDATAVIEW_CELL_EDITABLE,
|
PrusaBitmapTextRenderer(wxDataViewCellMode mode =
|
||||||
int align = wxDVR_DEFAULT_ALIGNMENT):
|
#ifdef __WXOSX__
|
||||||
wxDataViewCustomRenderer(wxT("PrusaDataViewBitmapText"), mode, align) {}
|
wxDATAVIEW_CELL_INERT
|
||||||
|
#else
|
||||||
|
wxDATAVIEW_CELL_EDITABLE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
,int align = wxDVR_DEFAULT_ALIGNMENT
|
||||||
|
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
) : wxDataViewCustomRenderer(wxT("PrusaDataViewBitmapText"), mode, align) {}
|
||||||
|
#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||||
|
|
||||||
bool SetValue(const wxVariant &value);
|
bool SetValue(const wxVariant &value);
|
||||||
bool GetValue(wxVariant &value) const;
|
bool GetValue(wxVariant &value) const;
|
||||||
|
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING && wxUSE_ACCESSIBILITY
|
||||||
|
virtual wxString GetAccessibleDescription() const override;
|
||||||
|
#endif // wxUSE_ACCESSIBILITY && ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||||
|
|
||||||
virtual bool Render(wxRect cell, wxDC *dc, int state);
|
virtual bool Render(wxRect cell, wxDC *dc, int state);
|
||||||
virtual wxSize GetSize() const;
|
virtual wxSize GetSize() const;
|
||||||
|
|
||||||
bool HasEditorCtrl() const override { return true; }
|
bool HasEditorCtrl() const override
|
||||||
|
{
|
||||||
|
#ifdef __WXOSX__
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
wxWindow* CreateEditorCtrl(wxWindow* parent,
|
wxWindow* CreateEditorCtrl(wxWindow* parent,
|
||||||
wxRect labelRect,
|
wxRect labelRect,
|
||||||
const wxVariant& value) override;
|
const wxVariant& value) override;
|
||||||
|
@ -542,8 +566,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PrusaDataViewBitmapText m_value;
|
PrusaDataViewBitmapText m_value;
|
||||||
wxBitmap m_bmp_from_editing_item;
|
bool m_was_unusable_symbol {false};
|
||||||
bool m_was_unusable_symbol;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
#include "slic3r/GUI/GUI.hpp"
|
#include "slic3r/GUI/GUI.hpp"
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
#include "slic3r/GUI/MsgDialog.hpp"
|
#include "slic3r/GUI/MsgDialog.hpp"
|
||||||
#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX
|
|
||||||
#include "Http.hpp"
|
#include "Http.hpp"
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
|
@ -55,89 +54,90 @@ wxString Duet::get_test_failed_msg (wxString &msg) const
|
||||||
return wxString::Format("%s: %s", _(L("Could not connect to Duet")), msg);
|
return wxString::Format("%s: %s", _(L("Could not connect to Duet")), msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Duet::send_gcode(const std::string &filename) const
|
// bool Duet::send_gcode(const std::string &filename) const
|
||||||
{
|
// {
|
||||||
enum { PROGRESS_RANGE = 1000 };
|
// enum { PROGRESS_RANGE = 1000 };
|
||||||
|
|
||||||
const auto errortitle = _(L("Error while uploading to the Duet"));
|
// const auto errortitle = _(L("Error while uploading to the Duet"));
|
||||||
fs::path filepath(filename);
|
// fs::path filepath(filename);
|
||||||
|
|
||||||
PrintHostSendDialog send_dialog(filepath.filename());
|
// GUI::PrintHostSendDialog send_dialog(filepath.filename());
|
||||||
if (send_dialog.ShowModal() != wxID_OK) { return false; }
|
// if (send_dialog.ShowModal() != wxID_OK) { return false; }
|
||||||
|
|
||||||
const bool print = send_dialog.start_print();
|
// const bool print = send_dialog.start_print();
|
||||||
const auto upload_filepath = send_dialog.filename();
|
// const auto upload_filepath = send_dialog.filename();
|
||||||
const auto upload_filename = upload_filepath.filename();
|
// const auto upload_filename = upload_filepath.filename();
|
||||||
const auto upload_parent_path = upload_filepath.parent_path();
|
// const auto upload_parent_path = upload_filepath.parent_path();
|
||||||
|
|
||||||
wxProgressDialog progress_dialog(
|
// wxProgressDialog progress_dialog(
|
||||||
_(L("Duet upload")),
|
// _(L("Duet upload")),
|
||||||
_(L("Sending G-code file to Duet...")),
|
// _(L("Sending G-code file to Duet...")),
|
||||||
PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
|
// PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
|
||||||
progress_dialog.Pulse();
|
// progress_dialog.Pulse();
|
||||||
|
|
||||||
wxString connect_msg;
|
// wxString connect_msg;
|
||||||
if (!connect(connect_msg)) {
|
// if (!connect(connect_msg)) {
|
||||||
auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg);
|
// auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg);
|
||||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
bool res = true;
|
// bool res = true;
|
||||||
|
|
||||||
auto upload_cmd = get_upload_url(upload_filepath.string());
|
// auto upload_cmd = get_upload_url(upload_filepath.string());
|
||||||
BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%")
|
// BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%")
|
||||||
% filepath.string()
|
// % filepath.string()
|
||||||
% upload_filename.string()
|
// % upload_filename.string()
|
||||||
% upload_parent_path.string()
|
// % upload_parent_path.string()
|
||||||
% print
|
// % print
|
||||||
% upload_cmd;
|
// % upload_cmd;
|
||||||
|
|
||||||
auto http = Http::post(std::move(upload_cmd));
|
// auto http = Http::post(std::move(upload_cmd));
|
||||||
http.set_post_body(filename)
|
// http.set_post_body(filename)
|
||||||
.on_complete([&](std::string body, unsigned status) {
|
// .on_complete([&](std::string body, unsigned status) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body;
|
// BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body;
|
||||||
progress_dialog.Update(PROGRESS_RANGE);
|
// progress_dialog.Update(PROGRESS_RANGE);
|
||||||
|
|
||||||
int err_code = get_err_code_from_body(body);
|
// int err_code = get_err_code_from_body(body);
|
||||||
if (err_code != 0) {
|
// if (err_code != 0) {
|
||||||
auto msg = format_error(body, L("Unknown error occured"), 0);
|
// auto msg = format_error(body, L("Unknown error occured"), 0);
|
||||||
GUI::show_error(&progress_dialog, std::move(msg));
|
// GUI::show_error(&progress_dialog, std::move(msg));
|
||||||
res = false;
|
// res = false;
|
||||||
} else if (print) {
|
// } else if (print) {
|
||||||
wxString errormsg;
|
// wxString errormsg;
|
||||||
res = start_print(errormsg, upload_filepath.string());
|
// res = start_print(errormsg, upload_filepath.string());
|
||||||
if (!res) {
|
// if (!res) {
|
||||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
// .on_error([&](std::string body, std::string error, unsigned status) {
|
||||||
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
|
// BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
|
||||||
auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status));
|
// auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status));
|
||||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||||
res = false;
|
// res = false;
|
||||||
})
|
// })
|
||||||
.on_progress([&](Http::Progress progress, bool &cancel) {
|
// .on_progress([&](Http::Progress progress, bool &cancel) {
|
||||||
if (cancel) {
|
// if (cancel) {
|
||||||
// Upload was canceled
|
// // Upload was canceled
|
||||||
res = false;
|
// res = false;
|
||||||
} else if (progress.ultotal > 0) {
|
// } else if (progress.ultotal > 0) {
|
||||||
int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal;
|
// int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal;
|
||||||
cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing
|
// cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing
|
||||||
} else {
|
// } else {
|
||||||
cancel = !progress_dialog.Pulse();
|
// cancel = !progress_dialog.Pulse();
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
.perform_sync();
|
// .perform_sync();
|
||||||
|
|
||||||
disconnect();
|
// disconnect();
|
||||||
|
|
||||||
return res;
|
// return res;
|
||||||
}
|
// }
|
||||||
|
|
||||||
bool Duet::upload(PrintHostUpload upload_data) const
|
bool Duet::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const
|
||||||
{
|
{
|
||||||
|
// XXX: TODO
|
||||||
throw "unimplemented";
|
throw "unimplemented";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,10 @@ public:
|
||||||
bool test(wxString &curl_msg) const;
|
bool test(wxString &curl_msg) const;
|
||||||
wxString get_test_ok_msg () const;
|
wxString get_test_ok_msg () const;
|
||||||
wxString get_test_failed_msg (wxString &msg) const;
|
wxString get_test_failed_msg (wxString &msg) const;
|
||||||
// Send gcode file to duet, filename is expected to be in UTF-8
|
bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const;
|
||||||
bool send_gcode(const std::string &filename) const;
|
|
||||||
bool upload(PrintHostUpload upload_data) const;
|
|
||||||
bool has_auto_discovery() const;
|
bool has_auto_discovery() const;
|
||||||
bool can_test() const;
|
bool can_test() const;
|
||||||
|
virtual std::string get_host() const { return host; }
|
||||||
private:
|
private:
|
||||||
std::string host;
|
std::string host;
|
||||||
std::string password;
|
std::string password;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
|
|
||||||
#include "libslic3r/libslic3r.h"
|
#include "libslic3r/libslic3r.h"
|
||||||
|
#include "libslic3r/Utils.hpp"
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
|
@ -44,6 +45,7 @@ struct Http::priv
|
||||||
// Using a deque here because unlike vector it doesn't ivalidate pointers on insertion
|
// Using a deque here because unlike vector it doesn't ivalidate pointers on insertion
|
||||||
std::deque<fs::ifstream> form_files;
|
std::deque<fs::ifstream> form_files;
|
||||||
std::string postfields;
|
std::string postfields;
|
||||||
|
std::string error_buffer; // Used for CURLOPT_ERRORBUFFER
|
||||||
size_t limit;
|
size_t limit;
|
||||||
bool cancel;
|
bool cancel;
|
||||||
|
|
||||||
|
@ -69,13 +71,14 @@ struct Http::priv
|
||||||
void http_perform();
|
void http_perform();
|
||||||
};
|
};
|
||||||
|
|
||||||
Http::priv::priv(const std::string &url) :
|
Http::priv::priv(const std::string &url)
|
||||||
curl(::curl_easy_init()),
|
: curl(::curl_easy_init())
|
||||||
form(nullptr),
|
, form(nullptr)
|
||||||
form_end(nullptr),
|
, form_end(nullptr)
|
||||||
headerlist(nullptr),
|
, headerlist(nullptr)
|
||||||
limit(0),
|
, error_buffer(CURL_ERROR_SIZE + 1, '\0')
|
||||||
cancel(false)
|
, limit(0)
|
||||||
|
, cancel(false)
|
||||||
{
|
{
|
||||||
if (curl == nullptr) {
|
if (curl == nullptr) {
|
||||||
throw std::runtime_error(std::string("Could not construct Curl object"));
|
throw std::runtime_error(std::string("Could not construct Curl object"));
|
||||||
|
@ -83,6 +86,7 @@ Http::priv::priv(const std::string &url) :
|
||||||
|
|
||||||
::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally
|
::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally
|
||||||
::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION);
|
::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION);
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer.front());
|
||||||
}
|
}
|
||||||
|
|
||||||
Http::priv::~priv()
|
Http::priv::~priv()
|
||||||
|
@ -199,9 +203,10 @@ void Http::priv::set_post_body(const fs::path &path)
|
||||||
|
|
||||||
std::string Http::priv::curl_error(CURLcode curlcode)
|
std::string Http::priv::curl_error(CURLcode curlcode)
|
||||||
{
|
{
|
||||||
return (boost::format("%1% (%2%)")
|
return (boost::format("%1% (%2%): %3%")
|
||||||
% ::curl_easy_strerror(curlcode)
|
% ::curl_easy_strerror(curlcode)
|
||||||
% curlcode
|
% curlcode
|
||||||
|
% error_buffer
|
||||||
).str();
|
).str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,9 +232,7 @@ void Http::priv::http_perform()
|
||||||
::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, static_cast<void*>(this));
|
::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, static_cast<void*>(this));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef NDEBUG
|
::curl_easy_setopt(curl, CURLOPT_VERBOSE, get_logging_level() >= 4);
|
||||||
::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (headerlist != nullptr) {
|
if (headerlist != nullptr) {
|
||||||
::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
|
::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
#include <wx/progdlg.h>
|
||||||
|
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX
|
|
||||||
#include "Http.hpp"
|
#include "Http.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,6 +43,9 @@ bool OctoPrint::test(wxString &msg) const
|
||||||
})
|
})
|
||||||
.on_complete([&](std::string body, unsigned) {
|
.on_complete([&](std::string body, unsigned) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: Got version: %1%") % body;
|
BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: Got version: %1%") % body;
|
||||||
|
|
||||||
|
// TODO: parse body, call validate_version_text
|
||||||
|
|
||||||
})
|
})
|
||||||
.perform_sync();
|
.perform_sync();
|
||||||
|
|
||||||
|
@ -59,32 +63,19 @@ wxString OctoPrint::get_test_failed_msg (wxString &msg) const
|
||||||
_(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required.")));
|
_(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OctoPrint::send_gcode(const std::string &filename) const
|
bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const
|
||||||
{
|
{
|
||||||
enum { PROGRESS_RANGE = 1000 };
|
const auto upload_filename = upload_data.upload_path.filename();
|
||||||
|
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||||
const auto errortitle = _(L("Error while uploading to the OctoPrint server"));
|
|
||||||
fs::path filepath(filename);
|
|
||||||
|
|
||||||
PrintHostSendDialog send_dialog(filepath.filename());
|
|
||||||
if (send_dialog.ShowModal() != wxID_OK) { return false; }
|
|
||||||
|
|
||||||
const bool print = send_dialog.start_print();
|
|
||||||
const auto upload_filepath = send_dialog.filename();
|
|
||||||
const auto upload_filename = upload_filepath.filename();
|
|
||||||
const auto upload_parent_path = upload_filepath.parent_path();
|
|
||||||
|
|
||||||
wxProgressDialog progress_dialog(
|
|
||||||
_(L("OctoPrint upload")),
|
|
||||||
_(L("Sending G-code file to the OctoPrint server...")),
|
|
||||||
PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
|
|
||||||
progress_dialog.Pulse();
|
|
||||||
|
|
||||||
wxString test_msg;
|
wxString test_msg;
|
||||||
if (! test(test_msg)) {
|
if (! test(test_msg)) {
|
||||||
auto errormsg = wxString::Format("%s: %s", errortitle, test_msg);
|
|
||||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
// TODO:
|
||||||
return false;
|
|
||||||
|
// auto errormsg = wxString::Format("%s: %s", errortitle, test_msg);
|
||||||
|
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||||
|
// return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool res = true;
|
bool res = true;
|
||||||
|
@ -92,36 +83,31 @@ bool OctoPrint::send_gcode(const std::string &filename) const
|
||||||
auto url = make_url("api/files/local");
|
auto url = make_url("api/files/local");
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%")
|
BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%")
|
||||||
% filepath.string()
|
% upload_data.source_path.string()
|
||||||
% url
|
% url
|
||||||
% upload_filename.string()
|
% upload_filename.string()
|
||||||
% upload_parent_path.string()
|
% upload_parent_path.string()
|
||||||
% print;
|
% upload_data.start_print;
|
||||||
|
|
||||||
auto http = Http::post(std::move(url));
|
auto http = Http::post(std::move(url));
|
||||||
set_auth(http);
|
set_auth(http);
|
||||||
http.form_add("print", print ? "true" : "false")
|
http.form_add("print", upload_data.start_print ? "true" : "false")
|
||||||
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
||||||
.form_add_file("file", filename, upload_filename.string())
|
.form_add_file("file", upload_data.source_path.string(), upload_filename.string())
|
||||||
.on_complete([&](std::string body, unsigned status) {
|
.on_complete([&](std::string body, unsigned status) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body;
|
BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body;
|
||||||
progress_dialog.Update(PROGRESS_RANGE);
|
|
||||||
})
|
})
|
||||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||||
BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
|
BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
|
||||||
auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status));
|
error_fn(std::move(body), std::move(error), status);
|
||||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
|
||||||
res = false;
|
res = false;
|
||||||
})
|
})
|
||||||
.on_progress([&](Http::Progress progress, bool &cancel) {
|
.on_progress([&](Http::Progress progress, bool &cancel) {
|
||||||
|
prorgess_fn(std::move(progress), cancel);
|
||||||
if (cancel) {
|
if (cancel) {
|
||||||
// Upload was canceled
|
// Upload was canceled
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Octoprint: Upload canceled";
|
||||||
res = false;
|
res = false;
|
||||||
} else if (progress.ultotal > 0) {
|
|
||||||
int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal;
|
|
||||||
cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing
|
|
||||||
} else {
|
|
||||||
cancel = !progress_dialog.Pulse();
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.perform_sync();
|
.perform_sync();
|
||||||
|
@ -129,11 +115,6 @@ bool OctoPrint::send_gcode(const std::string &filename) const
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OctoPrint::upload(PrintHostUpload upload_data) const
|
|
||||||
{
|
|
||||||
throw "unimplemented";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OctoPrint::has_auto_discovery() const
|
bool OctoPrint::has_auto_discovery() const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
@ -144,6 +125,12 @@ bool OctoPrint::can_test() const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OctoPrint::validate_version_text(const std::string &version_text)
|
||||||
|
{
|
||||||
|
// FIXME
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void OctoPrint::set_auth(Http &http) const
|
void OctoPrint::set_auth(Http &http) const
|
||||||
{
|
{
|
||||||
http.header("X-Api-Key", apikey);
|
http.header("X-Api-Key", apikey);
|
||||||
|
@ -177,4 +164,15 @@ wxString OctoPrint::format_error(const std::string &body, const std::string &err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// SL1
|
||||||
|
|
||||||
|
SL1Host::~SL1Host() {}
|
||||||
|
|
||||||
|
bool SL1Host::validate_version_text(const std::string &version_text)
|
||||||
|
{
|
||||||
|
// FIXME
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,11 +22,14 @@ public:
|
||||||
bool test(wxString &curl_msg) const;
|
bool test(wxString &curl_msg) const;
|
||||||
wxString get_test_ok_msg () const;
|
wxString get_test_ok_msg () const;
|
||||||
wxString get_test_failed_msg (wxString &msg) const;
|
wxString get_test_failed_msg (wxString &msg) const;
|
||||||
// Send gcode file to octoprint, filename is expected to be in UTF-8
|
bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const;
|
||||||
bool send_gcode(const std::string &filename) const;
|
|
||||||
bool upload(PrintHostUpload upload_data) const;
|
|
||||||
bool has_auto_discovery() const;
|
bool has_auto_discovery() const;
|
||||||
bool can_test() const;
|
bool can_test() const;
|
||||||
|
virtual std::string get_host() const { return host; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool validate_version_text(const std::string &version_text);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string host;
|
std::string host;
|
||||||
std::string apikey;
|
std::string apikey;
|
||||||
|
@ -38,6 +41,17 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class SL1Host: public OctoPrint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SL1Host(DynamicPrintConfig *config) : OctoPrint(config) {}
|
||||||
|
virtual ~SL1Host();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool validate_version_text(const std::string &version_text);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,15 +1,22 @@
|
||||||
#include "OctoPrint.hpp"
|
#include "PrintHost.hpp"
|
||||||
#include "Duet.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
|
#include <wx/app.h>
|
||||||
|
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
#include "libslic3r/Channel.hpp"
|
#include "libslic3r/Channel.hpp"
|
||||||
|
#include "OctoPrint.hpp"
|
||||||
|
#include "Duet.hpp"
|
||||||
|
#include "../GUI/PrintHostDialogs.hpp"
|
||||||
|
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
using boost::optional;
|
using boost::optional;
|
||||||
|
using Slic3r::GUI::PrintHostQueueDialog;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -18,42 +25,152 @@ PrintHost::~PrintHost() {}
|
||||||
|
|
||||||
PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
|
PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
|
||||||
{
|
{
|
||||||
PrintHostType kind = config->option<ConfigOptionEnum<PrintHostType>>("host_type")->value;
|
const auto opt = config->option<ConfigOptionEnum<PrintHostType>>("host_type");
|
||||||
if (kind == htOctoPrint) {
|
if (opt == nullptr) { return nullptr; }
|
||||||
return new OctoPrint(config);
|
|
||||||
} else if (kind == htDuet) {
|
switch (opt->value) {
|
||||||
return new Duet(config);
|
case htOctoPrint: return new OctoPrint(config);
|
||||||
|
case htSL1: return new SL1Host(config);
|
||||||
|
case htDuet: return new Duet(config);
|
||||||
|
default: return nullptr;
|
||||||
}
|
}
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct PrintHostJobQueue::priv
|
struct PrintHostJobQueue::priv
|
||||||
{
|
{
|
||||||
std::vector<PrintHostJob> jobs;
|
// XXX: comment on how bg thread works
|
||||||
Channel<unsigned> channel;
|
|
||||||
|
PrintHostJobQueue *q;
|
||||||
|
|
||||||
|
Channel<PrintHostJob> channel_jobs;
|
||||||
|
Channel<size_t> channel_cancels;
|
||||||
|
size_t job_id = 0;
|
||||||
|
int prev_progress = -1;
|
||||||
|
|
||||||
std::thread bg_thread;
|
std::thread bg_thread;
|
||||||
optional<PrintHostJob> bg_job;
|
bool bg_exit = false;
|
||||||
|
|
||||||
|
PrintHostQueueDialog *queue_dialog;
|
||||||
|
|
||||||
|
priv(PrintHostJobQueue *q) : q(q) {}
|
||||||
|
|
||||||
|
void start_bg_thread();
|
||||||
|
void bg_thread_main();
|
||||||
|
void progress_fn(Http::Progress progress, bool &cancel);
|
||||||
|
void error_fn(std::string body, std::string error, unsigned http_status);
|
||||||
|
void perform_job(PrintHostJob the_job);
|
||||||
};
|
};
|
||||||
|
|
||||||
PrintHostJobQueue::PrintHostJobQueue()
|
PrintHostJobQueue::PrintHostJobQueue(PrintHostQueueDialog *queue_dialog)
|
||||||
: p(new priv())
|
: p(new priv(this))
|
||||||
{
|
{
|
||||||
std::shared_ptr<priv> p2 = p;
|
p->queue_dialog = queue_dialog;
|
||||||
p->bg_thread = std::thread([p2]() {
|
|
||||||
// Wait for commands on the channel:
|
|
||||||
auto cmd = p2->channel.pop();
|
|
||||||
// TODO
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintHostJobQueue::~PrintHostJobQueue()
|
PrintHostJobQueue::~PrintHostJobQueue()
|
||||||
{
|
{
|
||||||
// TODO: stop the thread
|
if (p && p->bg_thread.joinable()) {
|
||||||
// if (p && p->bg_thread.joinable()) {
|
p->bg_exit = true;
|
||||||
// p->bg_thread.detach();
|
p->channel_jobs.push(PrintHostJob()); // Push an empty job to wake up bg_thread in case it's sleeping
|
||||||
// }
|
p->bg_thread.detach(); // Let the background thread go, it should exit on its own
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::priv::start_bg_thread()
|
||||||
|
{
|
||||||
|
if (bg_thread.joinable()) { return; }
|
||||||
|
|
||||||
|
std::shared_ptr<priv> p2 = q->p;
|
||||||
|
bg_thread = std::thread([p2]() {
|
||||||
|
p2->bg_thread_main();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::priv::bg_thread_main()
|
||||||
|
{
|
||||||
|
// bg thread entry point
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Pick up jobs from the job channel:
|
||||||
|
while (! bg_exit) {
|
||||||
|
auto job = channel_jobs.pop(); // Sleeps in a cond var if there are no jobs
|
||||||
|
if (! job.cancelled) {
|
||||||
|
perform_job(std::move(job));
|
||||||
|
}
|
||||||
|
job_id++;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
wxTheApp->OnUnhandledException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel)
|
||||||
|
{
|
||||||
|
if (bg_exit) {
|
||||||
|
cancel = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channel_cancels.size_hint() > 0) {
|
||||||
|
// Lock both queues
|
||||||
|
auto cancels = channel_cancels.lock_rw();
|
||||||
|
auto jobs = channel_jobs.lock_rw();
|
||||||
|
|
||||||
|
for (size_t cancel_id : *cancels) {
|
||||||
|
if (cancel_id == job_id) {
|
||||||
|
cancel = true;
|
||||||
|
} else if (cancel_id > job_id) {
|
||||||
|
jobs->at(cancel_id - job_id).cancelled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancels->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
int gui_progress = progress.ultotal > 0 ? 100*progress.ulnow / progress.ultotal : 0;
|
||||||
|
if (gui_progress != prev_progress) {
|
||||||
|
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, gui_progress);
|
||||||
|
wxQueueEvent(queue_dialog, evt);
|
||||||
|
prev_progress = gui_progress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::priv::error_fn(std::string body, std::string error, unsigned http_status)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job)
|
||||||
|
{
|
||||||
|
if (bg_exit || the_job.empty()) { return; }
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue/bg_thread: Got job: `%1%` -> `%1%`")
|
||||||
|
% the_job.upload_data.upload_path
|
||||||
|
% the_job.printhost->get_host();
|
||||||
|
|
||||||
|
const fs::path gcode_path = the_job.upload_data.source_path;
|
||||||
|
|
||||||
|
the_job.printhost->upload(std::move(the_job.upload_data),
|
||||||
|
[this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); },
|
||||||
|
[this](std::string body, std::string error, unsigned http_status) { this->error_fn(std::move(body), std::move(error), http_status); }
|
||||||
|
);
|
||||||
|
|
||||||
|
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, 100);
|
||||||
|
wxQueueEvent(queue_dialog, evt);
|
||||||
|
|
||||||
|
boost::system::error_code ec;
|
||||||
|
fs::remove(gcode_path, ec);
|
||||||
|
if (ec) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << boost::format("PrintHostJobQueue: Error removing file `%1%`: %2%") % gcode_path % ec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::enqueue(PrintHostJob job)
|
||||||
|
{
|
||||||
|
p->start_bg_thread();
|
||||||
|
p->queue_dialog->append_job(job);
|
||||||
|
p->channel_jobs.push(std::move(job));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
|
|
||||||
|
#include "Http.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -29,11 +31,10 @@ public:
|
||||||
virtual bool test(wxString &curl_msg) const = 0;
|
virtual bool test(wxString &curl_msg) const = 0;
|
||||||
virtual wxString get_test_ok_msg () const = 0;
|
virtual wxString get_test_ok_msg () const = 0;
|
||||||
virtual wxString get_test_failed_msg (wxString &msg) const = 0;
|
virtual wxString get_test_failed_msg (wxString &msg) const = 0;
|
||||||
// Send gcode file to print host, filename is expected to be in UTF-8
|
virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const = 0;
|
||||||
virtual bool send_gcode(const std::string &filename) const = 0; // XXX: remove in favor of upload()
|
|
||||||
virtual bool upload(PrintHostUpload upload_data) const = 0;
|
|
||||||
virtual bool has_auto_discovery() const = 0;
|
virtual bool has_auto_discovery() const = 0;
|
||||||
virtual bool can_test() const = 0;
|
virtual bool can_test() const = 0;
|
||||||
|
virtual std::string get_host() const = 0;
|
||||||
|
|
||||||
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
||||||
};
|
};
|
||||||
|
@ -43,6 +44,7 @@ struct PrintHostJob
|
||||||
{
|
{
|
||||||
PrintHostUpload upload_data;
|
PrintHostUpload upload_data;
|
||||||
std::unique_ptr<PrintHost> printhost;
|
std::unique_ptr<PrintHost> printhost;
|
||||||
|
bool cancelled = false;
|
||||||
|
|
||||||
PrintHostJob() {}
|
PrintHostJob() {}
|
||||||
PrintHostJob(const PrintHostJob&) = delete;
|
PrintHostJob(const PrintHostJob&) = delete;
|
||||||
|
@ -68,10 +70,12 @@ struct PrintHostJob
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace GUI { class PrintHostQueueDialog; }
|
||||||
|
|
||||||
class PrintHostJobQueue
|
class PrintHostJobQueue
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PrintHostJobQueue();
|
PrintHostJobQueue(GUI::PrintHostQueueDialog *queue_dialog);
|
||||||
PrintHostJobQueue(const PrintHostJobQueue &) = delete;
|
PrintHostJobQueue(const PrintHostJobQueue &) = delete;
|
||||||
PrintHostJobQueue(PrintHostJobQueue &&other) = delete;
|
PrintHostJobQueue(PrintHostJobQueue &&other) = delete;
|
||||||
~PrintHostJobQueue();
|
~PrintHostJobQueue();
|
||||||
|
@ -79,6 +83,9 @@ public:
|
||||||
PrintHostJobQueue& operator=(const PrintHostJobQueue &) = delete;
|
PrintHostJobQueue& operator=(const PrintHostJobQueue &) = delete;
|
||||||
PrintHostJobQueue& operator=(PrintHostJobQueue &&other) = delete;
|
PrintHostJobQueue& operator=(PrintHostJobQueue &&other) = delete;
|
||||||
|
|
||||||
|
void enqueue(PrintHostJob job);
|
||||||
|
void cancel(size_t id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct priv;
|
struct priv;
|
||||||
std::shared_ptr<priv> p;
|
std::shared_ptr<priv> p;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue