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)
|
||||
|
||||
if (SLIC3R_GUI)
|
||||
if(WIN32)
|
||||
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)
|
||||
include(${wxWidgets_USE_FILE})
|
||||
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)
|
||||
endif()
|
||||
# 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)
|
||||
else()
|
||||
|
|
|
@ -194,6 +194,10 @@ target_link_libraries(libslic3r
|
|||
tbb
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(libslic3r Psapi.lib)
|
||||
endif()
|
||||
|
||||
if(SLIC3R_PROFILE)
|
||||
target_link_libraries(slic3r Shiny)
|
||||
endif()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef slic3r_Channel_hpp_
|
||||
#define slic3r_Channel_hpp_
|
||||
|
||||
#include <memory>
|
||||
#include <deque>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
@ -13,32 +14,26 @@ namespace Slic3r {
|
|||
|
||||
template<class T> class Channel
|
||||
{
|
||||
private:
|
||||
using UniqueLock = std::unique_lock<std::mutex>;
|
||||
using Queue = std::deque<T>;
|
||||
public:
|
||||
class Guard
|
||||
using UniqueLock = std::unique_lock<std::mutex>;
|
||||
|
||||
template<class Ptr> class Unlocker
|
||||
{
|
||||
public:
|
||||
Guard(UniqueLock lock, const Queue &queue) : m_lock(std::move(lock)), m_queue(queue) {}
|
||||
Guard(const Guard &other) = delete;
|
||||
Guard(Guard &&other) = delete;
|
||||
~Guard() {}
|
||||
Unlocker(UniqueLock lock) : m_lock(std::move(lock)) {}
|
||||
Unlocker(const Unlocker &other) noexcept : m_lock(std::move(other.m_lock)) {} // XXX: done beacuse of MSVC 2013 not supporting init of deleter by move
|
||||
Unlocker(Unlocker &&other) noexcept : m_lock(std::move(other.m_lock)) {}
|
||||
Unlocker& operator=(const Unlocker &other) = delete;
|
||||
Unlocker& operator=(Unlocker &&other) { m_lock = std::move(other.m_lock); }
|
||||
|
||||
// Access trampolines
|
||||
size_t size() const noexcept { return m_queue.size(); }
|
||||
bool empty() const noexcept { return m_queue.empty(); }
|
||||
typename Queue::const_iterator begin() const noexcept { return m_queue.begin(); }
|
||||
typename Queue::const_iterator end() const noexcept { return m_queue.end(); }
|
||||
typename Queue::const_reference operator[](size_t i) const { return m_queue[i]; }
|
||||
|
||||
Guard& operator=(const Guard &other) = delete;
|
||||
Guard& operator=(Guard &&other) = delete;
|
||||
void operator()(Ptr*) { m_lock.unlock(); }
|
||||
private:
|
||||
UniqueLock m_lock;
|
||||
const Queue &m_queue;
|
||||
mutable UniqueLock m_lock; // XXX: mutable: see above
|
||||
};
|
||||
|
||||
using Queue = std::deque<T>;
|
||||
using LockedConstPtr = std::unique_ptr<const Queue, Unlocker<const Queue>>;
|
||||
using LockedPtr = std::unique_ptr<Queue, Unlocker<Queue>>;
|
||||
|
||||
Channel() {}
|
||||
~Channel() {}
|
||||
|
@ -56,7 +51,7 @@ public:
|
|||
{
|
||||
{
|
||||
UniqueLock lock(m_mutex);
|
||||
m_queue.push_back(std::forward(item));
|
||||
m_queue.push_back(std::forward<T>(item));
|
||||
}
|
||||
if (! silent) { m_condition.notify_one(); }
|
||||
}
|
||||
|
@ -82,19 +77,22 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
// Unlocked observers
|
||||
// Thread unsafe! Keep in mind you need to re-verify the result after acquiring lock!
|
||||
size_t size() const noexcept { return m_queue.size(); }
|
||||
bool empty() const noexcept { return m_queue.empty(); }
|
||||
// Unlocked observers/hints
|
||||
// Thread unsafe! Keep in mind you need to re-verify the result after locking!
|
||||
size_t size_hint() const noexcept { return m_queue.size(); }
|
||||
|
||||
Guard read() const
|
||||
LockedConstPtr lock_read() const
|
||||
{
|
||||
return Guard(UniqueLock(m_mutex), m_queue);
|
||||
return LockedConstPtr(&m_queue, Unlocker<const Queue>(UniqueLock(m_mutex)));
|
||||
}
|
||||
|
||||
LockedPtr lock_rw()
|
||||
{
|
||||
return LockedPtr(&m_queue, Unlocker<Queue>(UniqueLock(m_mutex)));
|
||||
}
|
||||
private:
|
||||
Queue m_queue;
|
||||
std::mutex m_mutex;
|
||||
mutable std::mutex m_mutex;
|
||||
std::condition_variable m_condition;
|
||||
};
|
||||
|
||||
|
|
|
@ -96,7 +96,6 @@ static void extract_model_from_archive(
|
|||
const char *model_xml = strstr(scene_xml_data.data(), model_name_tag);
|
||||
const char *zero_tag = "<zero>";
|
||||
const char *zero_xml = strstr(scene_xml_data.data(), zero_tag);
|
||||
float trafo[3][4] = { 0 };
|
||||
Vec3d instance_rotation = Vec3d::Zero();
|
||||
Vec3d instance_scaling_factor = Vec3d::Ones();
|
||||
Vec3d instance_offset = Vec3d::Zero();
|
||||
|
@ -124,19 +123,7 @@ static void extract_model_from_archive(
|
|||
"[%f, %f, %f]", zero, zero+1, zero+2) == 3) {
|
||||
instance_scaling_factor = Vec3d((double)scale[0], (double)scale[1], (double)scale[2]);
|
||||
instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]);
|
||||
Eigen::Matrix3f mat_rot, mat_scale, mat_trafo;
|
||||
mat_rot = Eigen::AngleAxisf(-rotation[2], Eigen::Vector3f::UnitZ()) *
|
||||
Eigen::AngleAxisf(-rotation[1], Eigen::Vector3f::UnitY()) *
|
||||
Eigen::AngleAxisf(-rotation[0], Eigen::Vector3f::UnitX());
|
||||
mat_scale = Eigen::Scaling(scale[0], scale[1], scale[2]);
|
||||
mat_trafo = mat_rot * mat_scale;
|
||||
for (size_t r = 0; r < 3; ++ r) {
|
||||
for (size_t c = 0; c < 3; ++ c)
|
||||
trafo[r][c] += mat_trafo(r, c);
|
||||
}
|
||||
instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2]));
|
||||
// CHECK_ME -> Is the following correct ?
|
||||
trafo[2][3] = position[2] / (float)instance_scaling_factor(2);
|
||||
trafo_set = true;
|
||||
}
|
||||
const char *group_tag = "<group>";
|
||||
|
@ -189,8 +176,6 @@ static void extract_model_from_archive(
|
|||
// All the faces have been read.
|
||||
stl_get_size(&stl);
|
||||
mesh.repair();
|
||||
// Transform the model.
|
||||
stl_transform(&stl, &trafo[0][0]);
|
||||
if (std::abs(stl.stats.min(2)) < EPSILON)
|
||||
stl.stats.min(2) = 0.;
|
||||
// Add a mesh to a model.
|
||||
|
@ -274,8 +259,6 @@ static void extract_model_from_archive(
|
|||
memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50);
|
||||
stl_get_size(&stl);
|
||||
mesh.repair();
|
||||
// Transform the model.
|
||||
stl_transform(&stl, &trafo[0][0]);
|
||||
// Add a mesh to a model.
|
||||
if (mesh.facets_count() > 0)
|
||||
mesh_valid = true;
|
||||
|
@ -329,7 +312,7 @@ bool load_prus(const char *path, Model *model)
|
|||
if (! mz_zip_reader_file_stat(&archive, i, &stat))
|
||||
continue;
|
||||
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);
|
||||
if (res == MZ_FALSE)
|
||||
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);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Exporting G-code...";
|
||||
BOOST_LOG_TRIVIAL(info) << "Exporting G-code..." << log_memory_info();
|
||||
|
||||
// Remove the old g-code if it exists.
|
||||
boost::nowide::remove(path);
|
||||
|
@ -435,9 +435,11 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
|
|||
if (file == nullptr)
|
||||
throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
|
||||
|
||||
m_enable_analyzer = preview_data != nullptr;
|
||||
|
||||
try {
|
||||
m_placeholder_parser_failed_templates.clear();
|
||||
this->_do_export(*print, file, preview_data);
|
||||
this->_do_export(*print, file);
|
||||
fflush(file);
|
||||
if (ferror(file)) {
|
||||
fclose(file);
|
||||
|
@ -453,15 +455,6 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
|
|||
}
|
||||
fclose(file);
|
||||
|
||||
if (print->config().remaining_times.value) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode";
|
||||
m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
|
||||
if (m_silent_time_estimator_enabled) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode";
|
||||
m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (! m_placeholder_parser_failed_templates.empty()) {
|
||||
// G-code export proceeded, but some of the PlaceholderParser substitutions failed.
|
||||
std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n";
|
||||
|
@ -475,12 +468,30 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
|
|||
throw std::runtime_error(msg);
|
||||
}
|
||||
|
||||
if (print->config().remaining_times.value) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode";
|
||||
m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
|
||||
m_normal_time_estimator.reset();
|
||||
if (m_silent_time_estimator_enabled) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode";
|
||||
m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
|
||||
m_silent_time_estimator.reset();
|
||||
}
|
||||
}
|
||||
|
||||
// starts analyzer calculations
|
||||
if (m_enable_analyzer) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data";
|
||||
m_analyzer.calc_gcode_preview_data(*preview_data);
|
||||
m_analyzer.reset();
|
||||
}
|
||||
|
||||
if (rename_file(path_tmp, path) != 0)
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' +
|
||||
"Is " + path_tmp + " locked?" + '\n');
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished";
|
||||
BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished" << log_memory_info();
|
||||
print->set_done(psGCodeExport);
|
||||
|
||||
// Write the profiler measurements to file
|
||||
|
@ -488,7 +499,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
|
|||
PROFILE_OUTPUT(debug_out_path("gcode-export-profile.txt").c_str());
|
||||
}
|
||||
|
||||
void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||
void GCode::_do_export(Print &print, FILE *file)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
|
||||
|
@ -558,7 +569,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
|
||||
// resets analyzer
|
||||
m_analyzer.reset();
|
||||
m_enable_analyzer = preview_data != nullptr;
|
||||
|
||||
// resets analyzer's tracking data
|
||||
m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm;
|
||||
|
@ -1034,12 +1044,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||
_write(file, full_config);
|
||||
}
|
||||
print.throw_if_canceled();
|
||||
|
||||
// starts analyzer calculations
|
||||
if (preview_data != nullptr) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data";
|
||||
m_analyzer.calc_gcode_preview_data(*preview_data);
|
||||
}
|
||||
}
|
||||
|
||||
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)
|
||||
|
@ -1644,6 +1648,11 @@ void GCode::process_layer(
|
|||
// printf("G-code after filter:\n%s\n", out.c_str());
|
||||
|
||||
_write(file, gcode);
|
||||
BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z <<
|
||||
", time estimator memory: " <<
|
||||
format_memsize_MB(m_normal_time_estimator.memory_used() + m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0) <<
|
||||
", analyzer memory: " <<
|
||||
format_memsize_MB(m_analyzer.memory_used());
|
||||
}
|
||||
|
||||
void GCode::apply_print_config(const PrintConfig &print_config)
|
||||
|
|
|
@ -180,7 +180,7 @@ public:
|
|||
static void append_full_config(const Print& print, std::string& str);
|
||||
|
||||
protected:
|
||||
void _do_export(Print &print, FILE *file, GCodePreviewData *preview_data);
|
||||
void _do_export(Print &print, FILE *file);
|
||||
|
||||
// Object and support extrusions of the same PrintObject at the same print_z.
|
||||
struct LayerToPrint
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "../libslic3r.h"
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../Utils.hpp"
|
||||
#include "Print.hpp"
|
||||
|
||||
#include "Analyzer.hpp"
|
||||
|
@ -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)
|
||||
{
|
||||
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
|
||||
void calc_gcode_preview_data(GCodePreviewData& preview_data);
|
||||
|
||||
// Return an estimate of the memory consumed by the time estimator.
|
||||
size_t memory_used() const;
|
||||
|
||||
static bool is_valid_extrusion_role(ExtrusionRole role);
|
||||
|
||||
private:
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "PreviewData.hpp"
|
||||
#include <float.h>
|
||||
#include <I18N.hpp>
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
|
@ -205,6 +206,18 @@ bool GCodePreviewData::Extrusion::is_role_flag_set(unsigned int flags, Extrusion
|
|||
return GCodeAnalyzer::is_valid_extrusion_role(role) && (flags & (1 << (role - erPerimeter))) != 0;
|
||||
}
|
||||
|
||||
size_t GCodePreviewData::Extrusion::memory_used() const
|
||||
{
|
||||
size_t out = sizeof(*this);
|
||||
out += SLIC3R_STDVEC_MEMSIZE(this->layers, Layer);
|
||||
for (const Layer &layer : this->layers) {
|
||||
out += SLIC3R_STDVEC_MEMSIZE(layer.paths, ExtrusionPath);
|
||||
for (const ExtrusionPath &path : layer.paths)
|
||||
out += SLIC3R_STDVEC_MEMSIZE(path.polyline.points, Point);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const float GCodePreviewData::Travel::Default_Width = 0.075f;
|
||||
const float GCodePreviewData::Travel::Default_Height = 0.075f;
|
||||
const GCodePreviewData::Color GCodePreviewData::Travel::Default_Type_Colors[Num_Types] =
|
||||
|
@ -224,6 +237,15 @@ void GCodePreviewData::Travel::set_default()
|
|||
is_visible = false;
|
||||
}
|
||||
|
||||
size_t GCodePreviewData::Travel::memory_used() const
|
||||
{
|
||||
size_t out = sizeof(*this);
|
||||
out += SLIC3R_STDVEC_MEMSIZE(this->polylines, Polyline);
|
||||
for (const Polyline &polyline : this->polylines)
|
||||
out += SLIC3R_STDVEC_MEMSIZE(polyline.polyline.points, Vec3crd);
|
||||
return out;
|
||||
}
|
||||
|
||||
const GCodePreviewData::Color GCodePreviewData::Retraction::Default_Color = GCodePreviewData::Color(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
GCodePreviewData::Retraction::Position::Position(const Vec3crd& position, float width, float height)
|
||||
|
@ -239,6 +261,11 @@ void GCodePreviewData::Retraction::set_default()
|
|||
is_visible = false;
|
||||
}
|
||||
|
||||
size_t GCodePreviewData::Retraction::memory_used() const
|
||||
{
|
||||
return sizeof(*this) + SLIC3R_STDVEC_MEMSIZE(this->positions, Position);
|
||||
}
|
||||
|
||||
void GCodePreviewData::Shell::set_default()
|
||||
{
|
||||
is_visible = false;
|
||||
|
@ -483,4 +510,15 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
|
|||
return items;
|
||||
}
|
||||
|
||||
// Return an estimate of the memory consumed by the time estimator.
|
||||
size_t GCodePreviewData::memory_used() const
|
||||
{
|
||||
return
|
||||
this->extrusion.memory_used() +
|
||||
this->travel.memory_used() +
|
||||
this->retraction.memory_used() +
|
||||
this->unretraction.memory_used() +
|
||||
sizeof(shell) + sizeof(ranges);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -99,6 +99,9 @@ public:
|
|||
void set_default();
|
||||
bool is_role_flag_set(ExtrusionRole role) const;
|
||||
|
||||
// Return an estimate of the memory consumed by the time estimator.
|
||||
size_t memory_used() const;
|
||||
|
||||
static bool is_role_flag_set(unsigned int flags, ExtrusionRole role);
|
||||
};
|
||||
|
||||
|
@ -144,6 +147,9 @@ public:
|
|||
size_t color_print_idx;
|
||||
|
||||
void set_default();
|
||||
|
||||
// Return an estimate of the memory consumed by the time estimator.
|
||||
size_t memory_used() const;
|
||||
};
|
||||
|
||||
struct Retraction
|
||||
|
@ -166,6 +172,9 @@ public:
|
|||
bool is_visible;
|
||||
|
||||
void set_default();
|
||||
|
||||
// Return an estimate of the memory consumed by the time estimator.
|
||||
size_t memory_used() const;
|
||||
};
|
||||
|
||||
struct Shell
|
||||
|
@ -199,6 +208,9 @@ public:
|
|||
|
||||
std::string get_legend_title() const;
|
||||
LegendItemsList get_legend_items(const std::vector<float>& tool_colors, const std::vector</*double*/std::pair<double, double>>& cp_values) const;
|
||||
|
||||
// Return an estimate of the memory consumed by the time estimator.
|
||||
size_t memory_used() const;
|
||||
};
|
||||
|
||||
GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2);
|
||||
|
|
|
@ -315,6 +315,20 @@ public:
|
|||
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
|
||||
Writer& set_extruder_trimpot(int current)
|
||||
{
|
||||
|
@ -473,7 +487,6 @@ WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *nam
|
|||
return INVALID;
|
||||
}
|
||||
|
||||
|
||||
// Returns gcode to prime the nozzles at the front edge of the print bed.
|
||||
WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
||||
// print_z of the first layer.
|
||||
|
@ -501,12 +514,15 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
|||
.set_initial_tool(m_current_tool)
|
||||
.append(";--------------------\n"
|
||||
"; CP PRIMING START\n")
|
||||
.append(";--------------------\n")
|
||||
.speed_override(100);
|
||||
.append(";--------------------\n");
|
||||
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
|
||||
.travel(cleaning_box.ld, 7200)
|
||||
.set_extruder_trimpot(750); // Increase the extruder driver current to allow fast ramming.
|
||||
.travel(cleaning_box.ld, 7200);
|
||||
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) {
|
||||
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)
|
||||
|
||||
// Reset the extruder current to a normal value.
|
||||
writer.set_extruder_trimpot(550)
|
||||
.feedrate(6000)
|
||||
if (m_set_extruder_trimpot)
|
||||
writer.set_extruder_trimpot(550);
|
||||
if (m_retain_speed_override)
|
||||
writer.speed_override_restore();
|
||||
writer.feedrate(6000)
|
||||
.flush_planner_queue()
|
||||
.reset_extruder()
|
||||
.append("; CP PRIMING END\n"
|
||||
|
@ -600,14 +619,17 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
|||
"; CP TOOLCHANGE START\n")
|
||||
.comment_with_value(" toolchange #", m_num_tool_changes + 1) // the number is zero-based
|
||||
.comment_material(m_filpar[m_current_tool].material)
|
||||
.append(";--------------------\n")
|
||||
.speed_override(100);
|
||||
.append(";--------------------\n");
|
||||
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);
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
.feedrate(6000)
|
||||
if (m_set_extruder_trimpot)
|
||||
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()
|
||||
.reset_extruder()
|
||||
.append("; CP TOOLCHANGE END\n"
|
||||
|
@ -881,14 +906,15 @@ void WipeTowerPrusaMM::toolchange_Change(
|
|||
case FLEX: speed_override = 35; break;
|
||||
default: speed_override = 100;
|
||||
}
|
||||
writer.set_tool(new_tool)
|
||||
.speed_override(speed_override)
|
||||
.flush_planner_queue();
|
||||
writer.set_tool(new_tool);
|
||||
if (m_retain_speed_override)
|
||||
assert(speed_override == 100);
|
||||
else
|
||||
writer.speed_override(speed_override);
|
||||
writer.flush_planner_queue();
|
||||
m_current_tool = new_tool;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void WipeTowerPrusaMM::toolchange_Load(
|
||||
PrusaMultiMaterial::Writer &writer,
|
||||
const box_coordinates &cleaning_box)
|
||||
|
@ -916,12 +942,10 @@ void WipeTowerPrusaMM::toolchange_Load(
|
|||
.resume_preview();
|
||||
|
||||
// Reset the extruder current to the normal value.
|
||||
if (m_set_extruder_trimpot)
|
||||
writer.set_extruder_trimpot(550);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Wipe the newly loaded filament until the end of the assigned wipe area.
|
||||
void WipeTowerPrusaMM::toolchange_Wipe(
|
||||
PrusaMultiMaterial::Writer &writer,
|
||||
|
|
|
@ -44,7 +44,8 @@ public:
|
|||
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
|
||||
// wipe_area -- space available for one toolchange in mm
|
||||
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) :
|
||||
m_wipe_tower_pos(x, y),
|
||||
m_wipe_tower_width(width),
|
||||
|
@ -57,6 +58,7 @@ public:
|
|||
m_parking_pos_retraction(parking_pos_retraction),
|
||||
m_extra_loading_move(extra_loading_move),
|
||||
m_bridging(bridging),
|
||||
m_set_extruder_trimpot(set_extruder_trimpot),
|
||||
m_current_tool(initial_tool),
|
||||
wipe_volumes(wiping_matrix)
|
||||
{}
|
||||
|
@ -73,6 +75,11 @@ public:
|
|||
m_filpar.push_back(FilamentParameters());
|
||||
|
||||
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].first_layer_temperature = first_layer_temp;
|
||||
m_filpar[idx].loading_speed = loading_speed;
|
||||
|
@ -212,6 +219,8 @@ private:
|
|||
float m_parking_pos_retraction = 0.f;
|
||||
float m_extra_loading_move = 0.f;
|
||||
float m_bridging = 0.f;
|
||||
bool m_set_extruder_trimpot = false;
|
||||
bool m_retain_speed_override = 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.
|
||||
|
|
|
@ -290,6 +290,7 @@ namespace Slic3r {
|
|||
// buffer line to export only when greater than 64K to reduce writing calls
|
||||
std::string export_line;
|
||||
char time_line[64];
|
||||
G1LineIdToBlockIdMap::const_iterator it_line_id = _g1_line_ids.begin();
|
||||
while (std::getline(in, gcode_line))
|
||||
{
|
||||
if (!in.good())
|
||||
|
@ -310,32 +311,32 @@ namespace Slic3r {
|
|||
|
||||
// add remaining time lines where needed
|
||||
_parser.parse_line(gcode_line,
|
||||
[this, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line)
|
||||
[this, &it_line_id, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
if (line.cmd_is("G1"))
|
||||
{
|
||||
++g1_lines_count;
|
||||
|
||||
if (!line.has_e())
|
||||
return;
|
||||
assert(it_line_id == _g1_line_ids.end() || it_line_id->first >= g1_lines_count);
|
||||
|
||||
G1LineIdToBlockIdMap::const_iterator it = _g1_line_ids.find(g1_lines_count);
|
||||
if ((it != _g1_line_ids.end()) && (it->second < (unsigned int)_blocks.size()))
|
||||
{
|
||||
const Block& block = _blocks[it->second];
|
||||
if (block.elapsed_time != -1.0f)
|
||||
{
|
||||
float block_remaining_time = _time - block.elapsed_time;
|
||||
const Block *block = nullptr;
|
||||
if (it_line_id != _g1_line_ids.end() && it_line_id->first == g1_lines_count) {
|
||||
if (line.has_e() && it_line_id->second < (unsigned int)_blocks.size())
|
||||
block = &_blocks[it_line_id->second];
|
||||
++it_line_id;
|
||||
}
|
||||
|
||||
if (block != nullptr && block->elapsed_time != -1.0f) {
|
||||
float block_remaining_time = _time - block->elapsed_time;
|
||||
if (std::abs(last_recorded_time - block_remaining_time) > interval)
|
||||
{
|
||||
sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block.elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str());
|
||||
sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str());
|
||||
gcode_line += time_line;
|
||||
|
||||
last_recorded_time = block_remaining_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export_line += gcode_line;
|
||||
|
@ -667,6 +668,15 @@ namespace Slic3r {
|
|||
return _get_time_minutes(get_time());
|
||||
}
|
||||
|
||||
// Return an estimate of the memory consumed by the time estimator.
|
||||
size_t GCodeTimeEstimator::memory_used() const
|
||||
{
|
||||
size_t out = sizeof(*this);
|
||||
out += SLIC3R_STDVEC_MEMSIZE(this->_blocks, Block);
|
||||
out += SLIC3R_STDVEC_MEMSIZE(this->_g1_line_ids, G1LineIdToBlockId);
|
||||
return out;
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_reset()
|
||||
{
|
||||
_curr.reset();
|
||||
|
@ -1072,7 +1082,7 @@ namespace Slic3r {
|
|||
|
||||
// adds block to blocks list
|
||||
_blocks.emplace_back(block);
|
||||
_g1_line_ids.insert(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1));
|
||||
_g1_line_ids.emplace_back(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1));
|
||||
}
|
||||
|
||||
void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line)
|
||||
|
@ -1223,7 +1233,8 @@ namespace Slic3r {
|
|||
return;
|
||||
|
||||
// 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())
|
||||
set_axis_max_feedrate(X, line.x() * factor);
|
||||
|
|
|
@ -209,7 +209,8 @@ namespace Slic3r {
|
|||
typedef std::map<Block::EMoveType, MoveStats> MovesStatsMap;
|
||||
#endif // ENABLE_MOVE_STATS
|
||||
|
||||
typedef std::map<unsigned int, unsigned int> G1LineIdToBlockIdMap;
|
||||
typedef std::pair<unsigned int, unsigned int> G1LineIdToBlockId;
|
||||
typedef std::vector<G1LineIdToBlockId> G1LineIdToBlockIdMap;
|
||||
|
||||
private:
|
||||
EMode _mode;
|
||||
|
@ -338,6 +339,9 @@ namespace Slic3r {
|
|||
// Returns the estimated time, in minutes (integer)
|
||||
std::string get_time_minutes() const;
|
||||
|
||||
// Return an estimate of the memory consumed by the time estimator.
|
||||
size_t memory_used() const;
|
||||
|
||||
private:
|
||||
void _reset();
|
||||
void _reset_time();
|
||||
|
|
|
@ -17,6 +17,16 @@ Layer::~Layer()
|
|||
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)
|
||||
{
|
||||
m_regions.emplace_back(new LayerRegion(this, print_region));
|
||||
|
|
|
@ -114,7 +114,8 @@ public:
|
|||
LayerRegion* get_region(int idx) { return m_regions[idx]; }
|
||||
LayerRegion* add_region(PrintRegion* print_region);
|
||||
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 merge_slices();
|
||||
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;
|
||||
}
|
||||
|
||||
/* distance to the closest point of line */
|
||||
double Line::distance_to(const Point &point) const
|
||||
// Distance to the closest point of line.
|
||||
double Line::distance_to_squared(const Point &point, const Point &a, const Point &b)
|
||||
{
|
||||
const Line &line = *this;
|
||||
const Vec2d v = (line.b - line.a).cast<double>();
|
||||
const Vec2d va = (point - line.a).cast<double>();
|
||||
const Vec2d v = (b - a).cast<double>();
|
||||
const Vec2d va = (point - a).cast<double>();
|
||||
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||
if (l2 == 0.0)
|
||||
// line.a == line.b case
|
||||
return va.norm();
|
||||
// Consider the line extending the segment, parameterized as line.a + t (line.b - line.a).
|
||||
// a == b case
|
||||
return va.squaredNorm();
|
||||
// Consider the line extending the segment, parameterized as a + t (b - a).
|
||||
// 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;
|
||||
if (t < 0.0) return va.norm(); // 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
|
||||
return (t * v - va).norm();
|
||||
if (t < 0.0) return va.squaredNorm(); // beyond the 'a' 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).squaredNorm();
|
||||
}
|
||||
|
||||
double Line::perp_distance_to(const Point &point) const
|
||||
|
|
|
@ -31,7 +31,8 @@ public:
|
|||
Point midpoint() const { return (this->a + this->b) / 2; }
|
||||
bool intersection_infinite(const Line &other, Point* point) const;
|
||||
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;
|
||||
bool parallel_to(double angle) const;
|
||||
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;
|
||||
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 b;
|
||||
};
|
||||
|
|
|
@ -809,6 +809,25 @@ TriangleMesh ModelObject::raw_mesh() const
|
|||
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.
|
||||
// This bounding box is only used for the actual slicing.
|
||||
BoundingBoxf3 ModelObject::raw_bounding_box() const
|
||||
|
@ -964,6 +983,16 @@ void ModelObject::mirror(Axis axis)
|
|||
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
|
||||
{
|
||||
std::set<t_model_material_id> material_ids;
|
||||
|
@ -1495,6 +1524,12 @@ void ModelVolume::mirror(Axis axis)
|
|||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
||||
}
|
||||
|
||||
void ModelVolume::scale_geometry(const Vec3d& versor)
|
||||
{
|
||||
mesh.scale(versor);
|
||||
m_convex_hull.scale(versor);
|
||||
}
|
||||
|
||||
#if !ENABLE_MODELVOLUME_TRANSFORM
|
||||
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.
|
||||
// Currently used by ModelObject::mesh() and to calculate the 2D envelope for 2D platter.
|
||||
TriangleMesh raw_mesh() const;
|
||||
// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
|
||||
TriangleMesh full_raw_mesh() const;
|
||||
// A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
|
||||
// This bounding box is only used for the actual slicing.
|
||||
BoundingBoxf3 raw_bounding_box() const;
|
||||
|
@ -235,6 +237,9 @@ public:
|
|||
void rotate(double angle, Axis axis);
|
||||
void rotate(double angle, const Vec3d& axis);
|
||||
void mirror(Axis axis);
|
||||
|
||||
void scale_mesh(const Vec3d& versor);
|
||||
|
||||
size_t materials_count() const;
|
||||
size_t facets_count() const;
|
||||
bool needed_repair() const;
|
||||
|
@ -329,6 +334,8 @@ public:
|
|||
void rotate(double angle, const Vec3d& axis);
|
||||
void mirror(Axis axis);
|
||||
|
||||
void scale_geometry(const Vec3d& versor);
|
||||
|
||||
#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
|
||||
void center_geometry();
|
||||
|
|
|
@ -162,45 +162,51 @@ bool MultiPoint::first_intersection(const Line& line, Point* intersection) const
|
|||
return found;
|
||||
}
|
||||
|
||||
//FIXME This is very inefficient in term of memory use.
|
||||
// The recursive algorithm shall run in place, not allocating temporary data in each recursion.
|
||||
Points
|
||||
MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
|
||||
std::vector<Point> MultiPoint::_douglas_peucker(const std::vector<Point>& pts, const double tolerance)
|
||||
{
|
||||
assert(points.size() >= 2);
|
||||
Points results;
|
||||
double dmax = 0;
|
||||
size_t index = 0;
|
||||
Line full(points.front(), points.back());
|
||||
for (Points::const_iterator it = points.begin() + 1; it != points.end(); ++it) {
|
||||
// we use shortest distance, not perpendicular distance
|
||||
double d = full.distance_to(*it);
|
||||
if (d > dmax) {
|
||||
index = it - points.begin();
|
||||
dmax = d;
|
||||
std::vector<Point> result_pts;
|
||||
if (! pts.empty()) {
|
||||
const Point *anchor = &pts.front();
|
||||
size_t anchor_idx = 0;
|
||||
const Point *floater = &pts.back();
|
||||
size_t floater_idx = pts.size() - 1;
|
||||
result_pts.reserve(pts.size());
|
||||
result_pts.emplace_back(*anchor);
|
||||
if (anchor_idx != floater_idx) {
|
||||
assert(pts.size() > 1);
|
||||
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) {
|
||||
Points dp0;
|
||||
dp0.reserve(index + 1);
|
||||
dp0.insert(dp0.end(), points.begin(), points.begin() + index + 1);
|
||||
// Recursive call.
|
||||
Points dp1 = MultiPoint::_douglas_peucker(dp0, tolerance);
|
||||
results.reserve(results.size() + dp1.size() - 1);
|
||||
results.insert(results.end(), dp1.begin(), dp1.end() - 1);
|
||||
|
||||
dp0.clear();
|
||||
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());
|
||||
// remove point if less than tolerance
|
||||
if (max_distSq <= tolerance) {
|
||||
result_pts.emplace_back(*floater);
|
||||
anchor_idx = floater_idx;
|
||||
anchor = floater;
|
||||
assert(dpStack.back() == floater_idx);
|
||||
dpStack.pop_back();
|
||||
if (dpStack.empty())
|
||||
break;
|
||||
floater_idx = dpStack.back();
|
||||
} else {
|
||||
results.push_back(points.front());
|
||||
results.push_back(points.back());
|
||||
floater_idx = furthest_idx;
|
||||
dpStack.emplace_back(floater_idx);
|
||||
}
|
||||
return results;
|
||||
floater = &pts[floater_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
return result_pts;
|
||||
}
|
||||
|
||||
// Visivalingam simplification algorithm https://github.com/slic3r/Slic3r/pull/3825
|
||||
|
|
|
@ -8,13 +8,14 @@
|
|||
#include "SupportMaterial.hpp"
|
||||
#include "GCode.hpp"
|
||||
#include "GCode/WipeTowerPrusaMM.hpp"
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include "PrintExport.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
//! macro used to mark string used at localization,
|
||||
//! return same string
|
||||
|
@ -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_ramming_parameters"
|
||||
|| opt_key == "gcode_flavor"
|
||||
|| opt_key == "high_current_on_filament_swap"
|
||||
|| opt_key == "infill_first"
|
||||
|| opt_key == "single_extruder_multi_material"
|
||||
|| opt_key == "spiral_vase"
|
||||
|
@ -1051,10 +1053,12 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
goto print_object_end;
|
||||
} else {
|
||||
this_region_config = region_config_from_model_volume(m_default_region_config, volume, num_extruders);
|
||||
for (size_t i = 0; i < region_id; ++ i)
|
||||
if (m_regions[i]->config().equals(this_region_config))
|
||||
for (size_t i = 0; i < region_id; ++i) {
|
||||
const PrintRegion ®ion_other = *m_regions[i];
|
||||
if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
|
||||
// Regions were merged. Reset this print_object.
|
||||
goto print_object_end;
|
||||
}
|
||||
this_region_config_set = true;
|
||||
}
|
||||
}
|
||||
|
@ -1092,8 +1096,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
bool fresh = print_object.region_volumes.empty();
|
||||
unsigned int volume_id = 0;
|
||||
for (const ModelVolume *volume : model_object.volumes) {
|
||||
if (! volume->is_model_part() && ! volume->is_modifier())
|
||||
if (! volume->is_model_part() && ! volume->is_modifier()) {
|
||||
++ volume_id;
|
||||
continue;
|
||||
}
|
||||
int region_id = -1;
|
||||
if (&print_object == &print_object0) {
|
||||
// Get the config applied to this volume.
|
||||
|
@ -1101,9 +1107,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
// Find an existing print region with the same config.
|
||||
int idx_empty_slot = -1;
|
||||
for (int i = 0; i < (int)m_regions.size(); ++ i) {
|
||||
if (m_regions[i]->m_refcnt == 0)
|
||||
if (m_regions[i]->m_refcnt == 0) {
|
||||
if (idx_empty_slot == -1)
|
||||
idx_empty_slot = i;
|
||||
else if (config.equals(m_regions[i]->config())) {
|
||||
} else if (config.equals(m_regions[i]->config())) {
|
||||
region_id = i;
|
||||
break;
|
||||
}
|
||||
|
@ -1469,7 +1476,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
|
|||
// Slicing process, running at a background thread.
|
||||
void Print::process()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Staring the slicing process.";
|
||||
BOOST_LOG_TRIVIAL(info) << "Staring the slicing process." << log_memory_info();
|
||||
for (PrintObject *obj : m_objects)
|
||||
obj->make_perimeters();
|
||||
this->set_status(70, "Infilling layers");
|
||||
|
@ -1501,7 +1508,7 @@ void Print::process()
|
|||
}
|
||||
this->set_done(psWipeTower);
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing process finished.";
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info();
|
||||
}
|
||||
|
||||
// G-code export process, running at a background thread.
|
||||
|
@ -1768,7 +1775,8 @@ void Print::_make_wipe_tower()
|
|||
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.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());
|
||||
|
||||
//wipe_tower.set_retract();
|
||||
|
|
|
@ -108,7 +108,7 @@ public:
|
|||
void add_region_volume(unsigned int region_id, int volume_id) {
|
||||
if (region_id >= region_volumes.size())
|
||||
region_volumes.resize(region_id + 1);
|
||||
region_volumes[region_id].push_back(volume_id);
|
||||
region_volumes[region_id].emplace_back(volume_id);
|
||||
}
|
||||
// This is the *total* layer count (including support layers)
|
||||
// this value is not supposed to be compared with Layer::id
|
||||
|
|
|
@ -925,6 +925,15 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comExpert;
|
||||
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->label = L("Infill");
|
||||
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->cli = "display-orientation=s";
|
||||
def->enum_keys_map = &ConfigOptionEnum<SLADisplayOrientation>::get_enum_values();
|
||||
def->enum_values.push_back("Landscape");
|
||||
def->enum_values.push_back("Portrait");
|
||||
def->enum_values.push_back("landscape");
|
||||
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 = this->add("printer_correction", coFloats);
|
||||
|
|
|
@ -36,7 +36,7 @@ enum GCodeFlavor {
|
|||
};
|
||||
|
||||
enum PrintHostType {
|
||||
htOctoPrint, htDuet,
|
||||
htOctoPrint, htDuet, htSL1,
|
||||
};
|
||||
|
||||
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() {
|
||||
static const t_config_enum_values keys_map = {
|
||||
{ "Landscape", sladoLandscape},
|
||||
{ "Portrait", sladoPortrait}
|
||||
{ "landscape", sladoLandscape},
|
||||
{ "portrait", sladoPortrait}
|
||||
};
|
||||
|
||||
return keys_map;
|
||||
|
@ -627,6 +627,7 @@ public:
|
|||
ConfigOptionBool variable_layer_height;
|
||||
ConfigOptionFloat cooling_tube_retraction;
|
||||
ConfigOptionFloat cooling_tube_length;
|
||||
ConfigOptionBool high_current_on_filament_swap;
|
||||
ConfigOptionFloat parking_pos_retraction;
|
||||
ConfigOptionBool remaining_times;
|
||||
ConfigOptionBool silent_mode;
|
||||
|
@ -695,6 +696,7 @@ protected:
|
|||
OPT_PTR(variable_layer_height);
|
||||
OPT_PTR(cooling_tube_retraction);
|
||||
OPT_PTR(cooling_tube_length);
|
||||
OPT_PTR(high_current_on_filament_swap);
|
||||
OPT_PTR(parking_pos_retraction);
|
||||
OPT_PTR(remaining_times);
|
||||
OPT_PTR(silent_mode);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "SupportMaterial.hpp"
|
||||
#include "Surface.hpp"
|
||||
#include "Slicing.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
@ -132,7 +133,7 @@ void PrintObject::make_perimeters()
|
|||
return;
|
||||
|
||||
m_print->set_status(20, "Generating perimeters");
|
||||
BOOST_LOG_TRIVIAL(info) << "Generating perimeters...";
|
||||
BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info();
|
||||
|
||||
// merge slices if they were split into types
|
||||
if (this->typed_slices) {
|
||||
|
@ -253,7 +254,7 @@ void PrintObject::prepare_infill()
|
|||
// Decide what surfaces are to be filled.
|
||||
// Here the S_TYPE_TOP / S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is turned to just S_TYPE_INTERNAL if zero top / bottom infill layers are configured.
|
||||
// Also tiny S_TYPE_INTERNAL surfaces are turned to S_TYPE_INTERNAL_SOLID.
|
||||
BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces...";
|
||||
BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..." << log_memory_info();
|
||||
for (auto *layer : m_layers)
|
||||
for (auto *region : layer->m_regions) {
|
||||
region->prepare_fill_surfaces();
|
||||
|
@ -384,6 +385,14 @@ void PrintObject::generate_support_material()
|
|||
m_print->set_status(85, "Generating support material");
|
||||
this->_generate_support_material();
|
||||
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);
|
||||
}
|
||||
|
@ -522,11 +531,13 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
|||
|| opt_key == "perimeter_speed"
|
||||
|| opt_key == "small_perimeter_speed"
|
||||
|| opt_key == "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,
|
||||
|| opt_key == "wipe_into_objects" // which we already did at the very beginning - nothing more to be done
|
||||
) {
|
||||
// these options only affect G-code export, so nothing to invalidate
|
||||
|| opt_key == "top_solid_infill_speed") {
|
||||
invalidated |= m_print->invalidate_step(psGCodeExport);
|
||||
} else if (
|
||||
opt_key == "wipe_into_infill"
|
||||
|| opt_key == "wipe_into_objects") {
|
||||
invalidated |= m_print->invalidate_step(psWipeTower);
|
||||
invalidated |= m_print->invalidate_step(psGCodeExport);
|
||||
} else {
|
||||
// for legacy, if we can't handle this option let's invalidate all steps
|
||||
this->invalidate_all_steps();
|
||||
|
@ -547,15 +558,15 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
|
|||
|
||||
// propagate to dependent steps
|
||||
if (step == posPerimeters) {
|
||||
invalidated |= this->invalidate_step(posPrepareInfill);
|
||||
invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill });
|
||||
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
||||
} else if (step == posPrepareInfill) {
|
||||
invalidated |= this->invalidate_step(posInfill);
|
||||
} else if (step == posInfill) {
|
||||
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
||||
} else if (step == posSlice) {
|
||||
invalidated |= this->invalidate_steps({ posPerimeters, posSupportMaterial });
|
||||
invalidated |= m_print->invalidate_step(psWipeTower);
|
||||
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posSupportMaterial });
|
||||
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
||||
} else if (step == posSupportMaterial)
|
||||
invalidated |= m_print->invalidate_steps({ psSkirt, psBrim });
|
||||
|
||||
|
@ -591,7 +602,7 @@ bool PrintObject::has_support_material() const
|
|||
// If a part of a region is of stBottom and stTop, the stBottom wins.
|
||||
void PrintObject::detect_surfaces_type()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Detecting solid surfaces...";
|
||||
BOOST_LOG_TRIVIAL(info) << "Detecting solid surfaces..." << log_memory_info();
|
||||
|
||||
// Interface shells: the intersecting parts are treated as self standing objects supporting each other.
|
||||
// Each of the objects will have a full number of top / bottom layers, even if these top / bottom layers
|
||||
|
@ -783,7 +794,7 @@ void PrintObject::detect_surfaces_type()
|
|||
|
||||
void PrintObject::process_external_surfaces()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Processing external surfaces...";
|
||||
BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..." << log_memory_info();
|
||||
|
||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) {
|
||||
const PrintRegion ®ion = *m_print->regions()[region_id];
|
||||
|
@ -808,7 +819,7 @@ void PrintObject::discover_vertical_shells()
|
|||
{
|
||||
PROFILE_FUNC();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells...";
|
||||
BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells..." << log_memory_info();
|
||||
|
||||
struct DiscoverVerticalShellsCacheEntry
|
||||
{
|
||||
|
@ -1192,7 +1203,7 @@ void PrintObject::discover_vertical_shells()
|
|||
sparse infill */
|
||||
void PrintObject::bridge_over_infill()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Bridge over infill...";
|
||||
BOOST_LOG_TRIVIAL(info) << "Bridge over infill..." << log_memory_info();
|
||||
|
||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
||||
const PrintRegion ®ion = *m_print->regions()[region_id];
|
||||
|
@ -1377,7 +1388,7 @@ bool PrintObject::update_layer_height_profile()
|
|||
// this should be idempotent
|
||||
void PrintObject::_slice()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing objects...";
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info();
|
||||
|
||||
this->typed_slices = false;
|
||||
|
||||
|
@ -1463,9 +1474,7 @@ void PrintObject::_slice()
|
|||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - removing top empty layers";
|
||||
while (! m_layers.empty()) {
|
||||
const Layer *layer = m_layers.back();
|
||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
|
||||
if (layer->m_regions[region_id] != nullptr && ! layer->m_regions[region_id]->slices.empty())
|
||||
// Non empty layer.
|
||||
if (! layer->empty())
|
||||
goto end;
|
||||
delete layer;
|
||||
m_layers.pop_back();
|
||||
|
@ -1701,7 +1710,7 @@ void PrintObject::_make_perimeters()
|
|||
if (! this->set_started(posPerimeters))
|
||||
return;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Generating perimeters...";
|
||||
BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info();
|
||||
|
||||
// merge slices if they were split into types
|
||||
if (this->typed_slices) {
|
||||
|
|
|
@ -612,7 +612,9 @@ double ray_mesh_intersect(const Vec3d& s,
|
|||
const Vec3d& dir,
|
||||
const EigenMesh3D& m);
|
||||
|
||||
PointSet normals(const PointSet& points, const EigenMesh3D& mesh);
|
||||
PointSet normals(const PointSet& points, const EigenMesh3D& mesh,
|
||||
double eps = 0.05, // min distance from edges
|
||||
std::function<void()> throw_on_cancel = [](){});
|
||||
|
||||
inline Vec2d to_vec2(const Vec3d& v3) {
|
||||
return {v3(X), v3(Y)};
|
||||
|
@ -1049,7 +1051,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||
tifcl();
|
||||
|
||||
// calculate the normals to the triangles belonging to filtered points
|
||||
auto nmls = sla::normals(filt_pts, mesh);
|
||||
auto nmls = sla::normals(filt_pts, mesh, cfg.head_front_radius_mm, tifcl);
|
||||
|
||||
head_norm.resize(count, 3);
|
||||
head_pos.resize(count, 3);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <cmath>
|
||||
#include "SLA/SLASupportTree.hpp"
|
||||
#include "SLA/SLABoilerPlate.hpp"
|
||||
#include "SLA/SLASpatIndex.hpp"
|
||||
|
@ -9,15 +10,8 @@
|
|||
#include "boost/geometry/index/rtree.hpp"
|
||||
|
||||
#include <igl/ray_mesh_intersect.h>
|
||||
|
||||
//#if !defined(_MSC_VER) || defined(_WIN64)
|
||||
#if 1
|
||||
#define IGL_COMPATIBLE
|
||||
#endif
|
||||
|
||||
#ifdef IGL_COMPATIBLE
|
||||
#include <igl/point_mesh_squared_distance.h>
|
||||
#endif
|
||||
#include <igl/remove_duplicate_vertices.h>
|
||||
|
||||
#include "SLASpatIndex.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
|
@ -84,33 +78,124 @@ size_t SpatIndex::size() const
|
|||
return m_impl->m_store.size();
|
||||
}
|
||||
|
||||
PointSet normals(const PointSet& points, const EigenMesh3D& mesh) {
|
||||
if(points.rows() == 0 || mesh.V.rows() == 0 || mesh.F.rows() == 0) return {};
|
||||
#ifdef IGL_COMPATIBLE
|
||||
bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2,
|
||||
double eps = 0.05)
|
||||
{
|
||||
using Line3D = Eigen::ParametrizedLine<double, 3>;
|
||||
|
||||
auto line = Line3D::Through(e1, e2);
|
||||
double d = line.distance(p);
|
||||
return std::abs(d) < eps;
|
||||
}
|
||||
|
||||
template<class Vec> double distance(const Vec& pp1, const Vec& pp2) {
|
||||
auto p = pp2 - pp1;
|
||||
return std::sqrt(p.transpose() * p);
|
||||
}
|
||||
|
||||
PointSet normals(const PointSet& points, const EigenMesh3D& emesh,
|
||||
double eps,
|
||||
std::function<void()> throw_on_cancel) {
|
||||
if(points.rows() == 0 || emesh.V.rows() == 0 || emesh.F.rows() == 0)
|
||||
return {};
|
||||
|
||||
Eigen::VectorXd dists;
|
||||
Eigen::VectorXi I;
|
||||
PointSet C;
|
||||
|
||||
// We need to remove duplicate vertices and have a true index triangle
|
||||
// structure
|
||||
EigenMesh3D mesh;
|
||||
Eigen::VectorXi SVI, SVJ;
|
||||
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);
|
||||
|
||||
PointSet ret(I.rows(), 3);
|
||||
for(int i = 0; i < I.rows(); i++) {
|
||||
throw_on_cancel();
|
||||
auto idx = I(i);
|
||||
auto trindex = mesh.F.row(idx);
|
||||
|
||||
auto& p1 = mesh.V.row(trindex(0));
|
||||
auto& p2 = mesh.V.row(trindex(1));
|
||||
auto& p3 = mesh.V.row(trindex(2));
|
||||
const Vec3d& p1 = mesh.V.row(trindex(0));
|
||||
const Vec3d& p2 = mesh.V.row(trindex(1));
|
||||
const Vec3d& p3 = mesh.V.row(trindex(2));
|
||||
|
||||
// We should check if the point lies on an edge of the hosting triangle.
|
||||
// If it does than all the other triangles using the same two points
|
||||
// have to be searched and the final normal should be some kind of
|
||||
// aggregation of the participating triangle normals. We should also
|
||||
// consider the cases where the support point lies right on a vertex
|
||||
// of its triangle. The procedure is the same, get the neighbor
|
||||
// triangles and calculate an average normal.
|
||||
|
||||
const Vec3d& p = C.row(i);
|
||||
|
||||
// mark the vertex indices of the edge. ia and ib marks and edge ic
|
||||
// will mark a single vertex.
|
||||
int ia = -1, ib = -1, ic = -1;
|
||||
|
||||
if(std::abs(distance(p, p1)) < eps) {
|
||||
ic = trindex(0);
|
||||
}
|
||||
else if(std::abs(distance(p, p2)) < eps) {
|
||||
ic = trindex(1);
|
||||
}
|
||||
else if(std::abs(distance(p, p3)) < eps) {
|
||||
ic = trindex(2);
|
||||
}
|
||||
else if(point_on_edge(p, p1, p2, eps)) {
|
||||
ia = trindex(0); ib = trindex(1);
|
||||
}
|
||||
else if(point_on_edge(p, p2, p3, eps)) {
|
||||
ia = trindex(1); ib = trindex(2);
|
||||
}
|
||||
else if(point_on_edge(p, p1, p3, eps)) {
|
||||
ia = trindex(0); ib = trindex(2);
|
||||
}
|
||||
|
||||
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 V = p3 - p1;
|
||||
ret.row(i) = U.cross(V).normalized();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
#else // TODO: do something on 32 bit windows
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
double ray_mesh_intersect(const Vec3d& s,
|
||||
|
@ -223,7 +308,7 @@ Segments model_boundary(const EigenMesh3D& emesh, double offs)
|
|||
pp.emplace_back(p);
|
||||
}
|
||||
|
||||
ExPolygons merged = union_ex(offset(pp, float(scale_(offs))), true);
|
||||
ExPolygons merged = union_ex(Slic3r::offset(pp, float(scale_(offs))), true);
|
||||
|
||||
for(auto& expoly : merged) {
|
||||
auto lines = expoly.lines();
|
||||
|
|
|
@ -436,10 +436,17 @@ std::vector<float> SLAPrint::calculate_heights(const BoundingBoxf3& bb3d, float
|
|||
return heights;
|
||||
}
|
||||
|
||||
template<class...Args>
|
||||
void report_status(SLAPrint& p, int st, const std::string& msg, Args&&...args) {
|
||||
BOOST_LOG_TRIVIAL(info) << st << "% " << msg;
|
||||
p.set_status(st, msg, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
void SLAPrint::process()
|
||||
{
|
||||
using namespace sla;
|
||||
using ExPolygon = Slic3r::ExPolygon;
|
||||
|
||||
// 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
|
||||
|
@ -556,9 +563,11 @@ void SLAPrint::process()
|
|||
// scaling for the sub operations
|
||||
double d = *stthis / (objcount * 100.0);
|
||||
|
||||
ctl.statuscb = [this, init, d](unsigned st, const std::string& msg){
|
||||
set_status(int(init + st*d), msg);
|
||||
ctl.statuscb = [this, init, d](unsigned st, const std::string& msg)
|
||||
{
|
||||
report_status(*this, int(init + st*d), msg);
|
||||
};
|
||||
|
||||
ctl.stopcondition = [this](){ return canceled(); };
|
||||
ctl.cancelfn = [this]() { throw_if_canceled(); };
|
||||
|
||||
|
@ -568,9 +577,9 @@ void SLAPrint::process()
|
|||
|
||||
// Create the unified mesh
|
||||
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();
|
||||
set_status(-1, L("Visualizing supports"), rc);
|
||||
report_status(*this, -1, L("Visualizing supports"), rc);
|
||||
} catch(sla::SLASupportsStoppedException&) {
|
||||
// no need to rethrow
|
||||
// throw_if_canceled();
|
||||
|
@ -613,7 +622,7 @@ void SLAPrint::process()
|
|||
|
||||
po.throw_if_canceled();
|
||||
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.
|
||||
|
@ -772,8 +781,12 @@ void SLAPrint::process()
|
|||
auto lvlcnt = unsigned(m_printer_input.size());
|
||||
printer.layers(lvlcnt);
|
||||
|
||||
// slot is the portion of 100% that is realted to rasterization
|
||||
unsigned slot = PRINT_STEP_LEVELS[slapsRasterize];
|
||||
// ist: initial state; pst: previous state
|
||||
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;
|
||||
SpinMutex slck;
|
||||
|
||||
|
@ -810,11 +823,11 @@ void SLAPrint::process()
|
|||
// Finish the layer for later saving it.
|
||||
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());
|
||||
{ std::lock_guard<SpinMutex> lck(slck);
|
||||
if( st > pst) {
|
||||
set_status(int(st), PRINT_STEP_LABELS[slapsRasterize]);
|
||||
report_status(*this, int(st), PRINT_STEP_LABELS[slapsRasterize]);
|
||||
pst = st;
|
||||
}
|
||||
}
|
||||
|
@ -862,9 +875,14 @@ void SLAPrint::process()
|
|||
unsigned st = min_objstatus;
|
||||
unsigned incr = 0;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Start slicing process.";
|
||||
|
||||
// TODO: this loop could run in parallel but should not exhaust all the CPU
|
||||
// power available
|
||||
for(SLAPrintObject * po : m_objects) {
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing object " << po->model_object()->name;
|
||||
|
||||
for(size_t s = 0; s < objectsteps.size(); ++s) {
|
||||
auto currentstep = objectsteps[s];
|
||||
|
||||
|
@ -876,8 +894,7 @@ void SLAPrint::process()
|
|||
st += unsigned(incr * ostepd);
|
||||
|
||||
if(po->m_stepmask[currentstep] && po->set_started(currentstep)) {
|
||||
|
||||
set_status(int(st), OBJ_STEP_LABELS[currentstep]);
|
||||
report_status(*this, int(st), OBJ_STEP_LABELS[currentstep]);
|
||||
pobj_program[currentstep](*po);
|
||||
po->set_done(currentstep);
|
||||
}
|
||||
|
@ -902,7 +919,7 @@ void SLAPrint::process()
|
|||
|
||||
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]();
|
||||
set_done(currentstep);
|
||||
}
|
||||
|
@ -911,7 +928,7 @@ void SLAPrint::process()
|
|||
}
|
||||
|
||||
// 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)
|
||||
|
|
|
@ -36,6 +36,12 @@
|
|||
#define ENABLE_REMOVE_TABS_FROM_PLATER (1 && ENABLE_1_42_0)
|
||||
// Constrains the camera target into the scene bounding box
|
||||
#define ENABLE_CONSTRAINED_CAMERA_TARGET (1 && ENABLE_1_42_0)
|
||||
// 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_
|
||||
|
||||
|
|
|
@ -11,7 +11,13 @@
|
|||
namespace Slic3r {
|
||||
|
||||
extern void set_logging_level(unsigned int level);
|
||||
extern unsigned get_logging_level();
|
||||
extern void trace(unsigned int level, const char *message);
|
||||
// Format memory allocated, separate thousands by comma.
|
||||
extern std::string format_memsize_MB(size_t n);
|
||||
// Return string to be added to the boost::log output to inform about the current process memory allocation.
|
||||
// The string is non-empty only if the loglevel >= info (3).
|
||||
extern std::string log_memory_info();
|
||||
extern void disable_multi_threading();
|
||||
|
||||
// Set a path with GUI resource files.
|
||||
|
@ -182,7 +188,12 @@ public:
|
|||
void reset() { closure = Closure(); }
|
||||
};
|
||||
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#if WIN32
|
||||
#define SLIC3R_STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + __alignof(TYPE) - 1) / __alignof(TYPE)) * __alignof(TYPE)
|
||||
#else
|
||||
#define SLIC3R_STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + alignof(TYPE) - 1) / alignof(TYPE)) * alignof(TYPE)
|
||||
#endif
|
||||
|
||||
#endif // slic3r_Utils_hpp_
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
@ -58,6 +59,18 @@ void set_logging_level(unsigned int level)
|
|||
);
|
||||
}
|
||||
|
||||
unsigned get_logging_level()
|
||||
{
|
||||
switch (logSeverity) {
|
||||
case boost::log::trivial::fatal : return 0;
|
||||
case boost::log::trivial::error : return 1;
|
||||
case boost::log::trivial::warning : return 2;
|
||||
case boost::log::trivial::info : return 3;
|
||||
case boost::log::trivial::debug : return 4;
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Force set_logging_level(<=error) after loading of the DLL.
|
||||
// Switch boost::filesystem to utf8.
|
||||
static struct RunOnInit {
|
||||
|
@ -365,4 +378,71 @@ std::string xml_escape(std::string text)
|
|||
return text;
|
||||
}
|
||||
|
||||
std::string format_memsize_MB(size_t n)
|
||||
{
|
||||
std::string out;
|
||||
size_t n2 = 0;
|
||||
size_t scale = 1;
|
||||
// Round to MB
|
||||
n += 500000;
|
||||
n /= 1000000;
|
||||
while (n >= 1000) {
|
||||
n2 = n2 + scale * (n % 1000);
|
||||
n /= 1000;
|
||||
scale *= 1000;
|
||||
}
|
||||
char buf[8];
|
||||
sprintf(buf, "%d", n);
|
||||
out = buf;
|
||||
while (scale != 1) {
|
||||
scale /= 1000;
|
||||
n = n2 / scale;
|
||||
n2 = n2 % scale;
|
||||
sprintf(buf, ",%03d", n);
|
||||
out += buf;
|
||||
}
|
||||
return out + "MB";
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#ifndef PROCESS_MEMORY_COUNTERS_EX
|
||||
// MingW32 doesn't have this struct in psapi.h
|
||||
typedef struct _PROCESS_MEMORY_COUNTERS_EX {
|
||||
DWORD cb;
|
||||
DWORD PageFaultCount;
|
||||
SIZE_T PeakWorkingSetSize;
|
||||
SIZE_T WorkingSetSize;
|
||||
SIZE_T QuotaPeakPagedPoolUsage;
|
||||
SIZE_T QuotaPagedPoolUsage;
|
||||
SIZE_T QuotaPeakNonPagedPoolUsage;
|
||||
SIZE_T QuotaNonPagedPoolUsage;
|
||||
SIZE_T PagefileUsage;
|
||||
SIZE_T PeakPagefileUsage;
|
||||
SIZE_T PrivateUsage;
|
||||
} PROCESS_MEMORY_COUNTERS_EX, *PPROCESS_MEMORY_COUNTERS_EX;
|
||||
#endif /* PROCESS_MEMORY_COUNTERS_EX */
|
||||
|
||||
std::string log_memory_info()
|
||||
{
|
||||
std::string out;
|
||||
if (logSeverity <= boost::log::trivial::info) {
|
||||
HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ::GetCurrentProcessId());
|
||||
if (hProcess != nullptr) {
|
||||
PROCESS_MEMORY_COUNTERS_EX pmc;
|
||||
if (GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)))
|
||||
out = " WorkingSet: " + format_memsize_MB(pmc.WorkingSetSize) + " PrivateBytes: " + format_memsize_MB(pmc.PrivateUsage) + " Pagefile(peak): " + format_memsize_MB(pmc.PagefileUsage) + "(" + format_memsize_MB(pmc.PeakPagefileUsage) + ")";
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
#else
|
||||
std::string log_memory_info()
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
#endif
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
|
|
@ -65,11 +65,6 @@ PrinterTechnology BackgroundSlicingProcess::current_printer_technology() const
|
|||
return m_print->technology();
|
||||
}
|
||||
|
||||
static bool isspace(int ch)
|
||||
{
|
||||
return std::isspace(ch) != 0;
|
||||
}
|
||||
|
||||
// This function may one day be merged into the Print, but historically the print was separated
|
||||
// from the G-code generator.
|
||||
void BackgroundSlicingProcess::process_fff()
|
||||
|
@ -88,6 +83,27 @@ void BackgroundSlicingProcess::process_fff()
|
|||
m_print->set_status(95, "Running post-processing scripts");
|
||||
run_post_process_scripts(export_path, m_fff_print->config());
|
||||
m_print->set_status(100, "G-code file exported to " + export_path);
|
||||
} else if (! m_upload_job.empty()) {
|
||||
// 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 {
|
||||
m_print->set_status(100, "Slicing complete");
|
||||
}
|
||||
|
@ -373,13 +389,10 @@ void BackgroundSlicingProcess::schedule_upload(Slic3r::PrintHostJob upload_job)
|
|||
if (! m_export_path.empty())
|
||||
return;
|
||||
|
||||
const boost::filesystem::path path = boost::filesystem::temp_directory_path()
|
||||
/ boost::filesystem::unique_path(".upload.%%%%-%%%%-%%%%-%%%%.gcode");
|
||||
|
||||
// Guard against entering the export step before changing the export path.
|
||||
tbb::mutex::scoped_lock lock(m_print->state_mutex());
|
||||
this->invalidate_step(bspsGCodeFinalize);
|
||||
m_export_path = path.string();
|
||||
m_export_path = std::string();
|
||||
m_upload_job = std::move(upload_job);
|
||||
}
|
||||
|
||||
|
|
|
@ -163,17 +163,40 @@ void Field::get_value_by_opt_type(wxString& str)
|
|||
break; }
|
||||
case coString:
|
||||
case coStrings:
|
||||
case coFloatOrPercent:
|
||||
case coFloatOrPercent: {
|
||||
if (m_opt.type == coFloatOrPercent && !str.IsEmpty() && str.Last() != '%')
|
||||
{
|
||||
double val;
|
||||
if (!str.ToCDouble(&val))
|
||||
{
|
||||
show_error(m_parent, _(L("Input value contains incorrect symbol(s).\nUse, please, only digits")));
|
||||
set_value(double_to_string(val), true);
|
||||
}
|
||||
else if (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();
|
||||
break;
|
||||
break; }
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool TextCtrl::is_defined_input_value() const
|
||||
template<class T>
|
||||
bool is_defined_input_value(wxWindow* win, const ConfigOptionType& type)
|
||||
{
|
||||
if (static_cast<wxTextCtrl*>(window)->GetValue().empty() && m_opt.type != coString && m_opt.type != coStrings)
|
||||
if (static_cast<T*>(win)->GetValue().empty() && type != coString && type != coStrings)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -252,7 +275,7 @@ void TextCtrl::BUILD() {
|
|||
temp->GetToolTip()->Enable(true);
|
||||
#endif // __WXGTK__
|
||||
// if (!is_defined_input_value())
|
||||
if (is_defined_input_value())
|
||||
if (is_defined_input_value<wxTextCtrl>(window, m_opt.type))
|
||||
on_change_field();
|
||||
else
|
||||
on_kill_focus(e);
|
||||
|
@ -377,6 +400,9 @@ void SpinCtrl::BUILD() {
|
|||
0, min_val, max_val, default_value);
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (tmp_value < 0)
|
||||
|
@ -386,6 +412,7 @@ void SpinCtrl::BUILD() {
|
|||
on_change_field();
|
||||
}
|
||||
}), temp->GetId());
|
||||
|
||||
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e)
|
||||
{
|
||||
// # On OSX / Cocoa, wxSpinCtrl::GetValue() doesn't return the new value
|
||||
|
@ -398,9 +425,15 @@ void SpinCtrl::BUILD() {
|
|||
tmp_value = std::stoi(value);
|
||||
else tmp_value = -9999;
|
||||
// on_change_field();
|
||||
// # We don't reset tmp_value here because _on_change might put callbacks
|
||||
// # in the CallAfter queue, and we want the tmp value to be available from
|
||||
// # them as well.
|
||||
#ifdef __WXOSX__
|
||||
// #ys_FIXME_KILL_FOCUS so call on_change_field() inside wxEVT_TEXT
|
||||
if (tmp_value < 0) {
|
||||
if (m_on_kill_focus != nullptr)
|
||||
m_on_kill_focus(m_opt_id);
|
||||
}
|
||||
else
|
||||
on_change_field();
|
||||
#endif
|
||||
}), temp->GetId());
|
||||
|
||||
temp->SetToolTip(get_tooltip_text(text_value));
|
||||
|
@ -432,9 +465,24 @@ void Choice::BUILD() {
|
|||
}
|
||||
set_selection();
|
||||
}
|
||||
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
|
||||
// temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
|
||||
temp->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
|
||||
|
||||
if (temp->GetWindowStyle() != wxCB_READONLY) {
|
||||
temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) {
|
||||
e.Skip();
|
||||
double old_val = !m_value.empty() ? boost::any_cast<double>(m_value) : -99999;
|
||||
if (is_defined_input_value<wxComboBox>(window, m_opt.type)) {
|
||||
if (fabs(old_val - boost::any_cast<double>(get_value())) <= 0.0001)
|
||||
return;
|
||||
else
|
||||
on_change_field();
|
||||
}
|
||||
else
|
||||
on_kill_focus(e);
|
||||
}), temp->GetId());
|
||||
}
|
||||
|
||||
temp->SetToolTip(get_tooltip_text(temp->GetValue()));
|
||||
}
|
||||
|
||||
|
@ -611,9 +659,7 @@ boost::any& Choice::get_value()
|
|||
if (m_opt_id == rp_option)
|
||||
return m_value = boost::any(ret_str);
|
||||
|
||||
if (m_opt.type != coEnum)
|
||||
/*m_value = */get_value_by_opt_type(ret_str);
|
||||
else
|
||||
if (m_opt.type == coEnum)
|
||||
{
|
||||
int ret_enum = static_cast<wxComboBox*>(window)->GetSelection();
|
||||
if (m_opt_id.compare("external_fill_pattern") == 0)
|
||||
|
@ -638,7 +684,18 @@ boost::any& Choice::get_value()
|
|||
m_value = static_cast<SeamPosition>(ret_enum);
|
||||
else if (m_opt_id.compare("host_type") == 0)
|
||||
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;
|
||||
}
|
||||
|
@ -702,8 +759,11 @@ void PointCtrl::BUILD()
|
|||
temp->Add(new wxStaticText(m_parent, wxID_ANY, " y : "), 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
temp->Add(y_textctrl);
|
||||
|
||||
x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_textctrl->GetId());
|
||||
y_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), y_textctrl->GetId());
|
||||
// x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_textctrl->GetId());
|
||||
// y_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), y_textctrl->GetId());
|
||||
|
||||
x_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { OnKillFocus(e, x_textctrl); }), x_textctrl->GetId());
|
||||
y_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { OnKillFocus(e, x_textctrl); }), y_textctrl->GetId());
|
||||
|
||||
// // recast as a wxWindow to fit the calling convention
|
||||
sizer = dynamic_cast<wxSizer*>(temp);
|
||||
|
@ -712,6 +772,16 @@ void PointCtrl::BUILD()
|
|||
y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y));
|
||||
}
|
||||
|
||||
void PointCtrl::OnKillFocus(wxEvent& e, wxTextCtrl* win)
|
||||
{
|
||||
e.Skip();
|
||||
if (!win->GetValue().empty()) {
|
||||
on_change_field();
|
||||
}
|
||||
else
|
||||
on_kill_focus(e);
|
||||
}
|
||||
|
||||
void PointCtrl::set_value(const Vec2d& value, bool change_event)
|
||||
{
|
||||
m_disable_change_event = !change_event;
|
||||
|
|
|
@ -266,7 +266,6 @@ public:
|
|||
}
|
||||
|
||||
boost::any& get_value() override;
|
||||
bool is_defined_input_value() const ;
|
||||
|
||||
virtual void enable();
|
||||
virtual void disable();
|
||||
|
@ -395,6 +394,7 @@ public:
|
|||
|
||||
void BUILD() override;
|
||||
|
||||
void OnKillFocus(wxEvent& e, wxTextCtrl* win);
|
||||
void set_value(const Vec2d& value, bool change_event = false);
|
||||
void set_value(const boost::any& value, bool change_event = false);
|
||||
boost::any& get_value() override;
|
||||
|
|
|
@ -617,42 +617,71 @@ bool GLCanvas3D::Bed::_are_equal(const Pointfs& bed_1, const Pointfs& bed_2)
|
|||
return true;
|
||||
}
|
||||
|
||||
const double GLCanvas3D::Axes::Radius = 0.5;
|
||||
const double GLCanvas3D::Axes::ArrowBaseRadius = 2.5 * GLCanvas3D::Axes::Radius;
|
||||
const double GLCanvas3D::Axes::ArrowLength = 5.0;
|
||||
|
||||
GLCanvas3D::Axes::Axes()
|
||||
: origin(Vec3d::Zero())
|
||||
, length(0.0f)
|
||||
, length(Vec3d::Zero())
|
||||
{
|
||||
m_quadric = ::gluNewQuadric();
|
||||
if (m_quadric != nullptr)
|
||||
::gluQuadricDrawStyle(m_quadric, GLU_FILL);
|
||||
}
|
||||
|
||||
void GLCanvas3D::Axes::render(bool depth_test) const
|
||||
GLCanvas3D::Axes::~Axes()
|
||||
{
|
||||
if (depth_test)
|
||||
::glEnable(GL_DEPTH_TEST);
|
||||
else
|
||||
::glDisable(GL_DEPTH_TEST);
|
||||
if (m_quadric != nullptr)
|
||||
::gluDeleteQuadric(m_quadric);
|
||||
}
|
||||
|
||||
::glLineWidth(2.0f);
|
||||
::glBegin(GL_LINES);
|
||||
// draw line for x axis
|
||||
void GLCanvas3D::Axes::render() const
|
||||
{
|
||||
if (m_quadric == nullptr)
|
||||
return;
|
||||
|
||||
::glEnable(GL_DEPTH_TEST);
|
||||
::glEnable(GL_LIGHTING);
|
||||
|
||||
// x axis
|
||||
::glColor3f(1.0f, 0.0f, 0.0f);
|
||||
::glVertex3dv(origin.data());
|
||||
::glVertex3f((GLfloat)origin(0) + length, (GLfloat)origin(1), (GLfloat)origin(2));
|
||||
// draw line for y axis
|
||||
::glColor3f(0.0f, 1.0f, 0.0f);
|
||||
::glVertex3dv(origin.data());
|
||||
::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1) + length, (GLfloat)origin(2));
|
||||
::glEnd();
|
||||
// draw line for Z axis
|
||||
// (re-enable depth test so that axis is correctly shown when objects are behind it)
|
||||
if (!depth_test)
|
||||
::glEnable(GL_DEPTH_TEST);
|
||||
::glPushMatrix();
|
||||
::glTranslated(origin(0), origin(1), origin(2));
|
||||
::glRotated(90.0, 0.0, 1.0, 0.0);
|
||||
render_axis(length(0));
|
||||
::glPopMatrix();
|
||||
|
||||
::glBegin(GL_LINES);
|
||||
// y axis
|
||||
::glColor3f(0.0f, 1.0f, 0.0f);
|
||||
::glPushMatrix();
|
||||
::glTranslated(origin(0), origin(1), origin(2));
|
||||
::glRotated(-90.0, 1.0, 0.0, 0.0);
|
||||
render_axis(length(1));
|
||||
::glPopMatrix();
|
||||
|
||||
// z axis
|
||||
::glColor3f(0.0f, 0.0f, 1.0f);
|
||||
::glVertex3dv(origin.data());
|
||||
::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2) + length);
|
||||
::glEnd();
|
||||
::glPushMatrix();
|
||||
::glTranslated(origin(0), origin(1), origin(2));
|
||||
render_axis(length(2));
|
||||
::glPopMatrix();
|
||||
|
||||
::glDisable(GL_LIGHTING);
|
||||
}
|
||||
|
||||
void GLCanvas3D::Axes::render_axis(double length) const
|
||||
{
|
||||
::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
|
||||
::gluCylinder(m_quadric, Radius, Radius, length, 32, 1);
|
||||
::gluQuadricOrientation(m_quadric, GLU_INSIDE);
|
||||
::gluDisk(m_quadric, 0.0, Radius, 32, 1);
|
||||
::glTranslated(0.0, 0.0, length);
|
||||
::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
|
||||
::gluCylinder(m_quadric, ArrowBaseRadius, 0.0, ArrowLength, 32, 1);
|
||||
::gluQuadricOrientation(m_quadric, GLU_INSIDE);
|
||||
::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1);
|
||||
}
|
||||
|
||||
GLCanvas3D::Shader::Shader()
|
||||
: m_shader(nullptr)
|
||||
|
@ -1133,8 +1162,21 @@ GLCanvas3D::Selection::Selection()
|
|||
, m_valid(false)
|
||||
, 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)
|
||||
{
|
||||
m_volumes = volumes;
|
||||
|
@ -1436,6 +1478,14 @@ bool GLCanvas3D::Selection::is_from_single_object() const
|
|||
return (0 <= idx) && (idx < 1000);
|
||||
}
|
||||
|
||||
bool GLCanvas3D::Selection::requires_uniform_scale() const
|
||||
{
|
||||
if (is_single_full_instance() || is_single_modifier() || is_single_volume())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int GLCanvas3D::Selection::get_object_idx() const
|
||||
{
|
||||
return (m_cache.content.size() == 1) ? m_cache.content.begin()->first : -1;
|
||||
|
@ -1489,10 +1539,15 @@ void GLCanvas3D::Selection::translate(const Vec3d& displacement)
|
|||
{
|
||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||
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;
|
||||
(*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement);
|
||||
}
|
||||
}
|
||||
else if (m_mode == Instance)
|
||||
(*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement);
|
||||
#else
|
||||
|
@ -1519,11 +1574,16 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local)
|
|||
{
|
||||
if (is_single_full_instance())
|
||||
#if ENABLE_WORLD_ROTATIONS
|
||||
{
|
||||
if (local)
|
||||
(*m_volumes)[i]->set_instance_rotation(rotation);
|
||||
else
|
||||
{
|
||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
||||
Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix());
|
||||
(*m_volumes)[i]->set_instance_rotation(new_rotation);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||
(*m_volumes)[i]->set_instance_rotation(rotation);
|
||||
|
@ -1534,12 +1594,17 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local)
|
|||
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||
else if (is_single_volume() || is_single_modifier())
|
||||
#if ENABLE_WORLD_ROTATIONS
|
||||
{
|
||||
if (_requires_local_axes())
|
||||
(*m_volumes)[i]->set_volume_rotation(rotation);
|
||||
else
|
||||
{
|
||||
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
||||
const Transform3d& inst_m = m_cache.volumes_data[i].get_instance_rotation_matrix();
|
||||
Vec3d new_rotation = Geometry::extract_euler_angles(inst_m.inverse() * m * inst_m * m_cache.volumes_data[i].get_volume_rotation_matrix());
|
||||
(*m_volumes)[i]->set_volume_rotation(new_rotation);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(*m_volumes)[i]->set_volume_rotation(rotation);
|
||||
#endif // ENABLE_WORLD_ROTATIONS
|
||||
|
@ -1960,7 +2025,7 @@ void GLCanvas3D::Selection::erase()
|
|||
|
||||
void GLCanvas3D::Selection::render() const
|
||||
{
|
||||
if (is_empty())
|
||||
if (!m_valid || is_empty())
|
||||
return;
|
||||
|
||||
// render cumulative bounding box of selected volumes
|
||||
|
@ -1968,6 +2033,28 @@ void GLCanvas3D::Selection::render() const
|
|||
_render_synchronized_volumes();
|
||||
}
|
||||
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
void GLCanvas3D::Selection::render_center() const
|
||||
{
|
||||
if (!m_valid || is_empty() || (m_quadric == nullptr))
|
||||
return;
|
||||
|
||||
const Vec3d& center = get_bounding_box().center();
|
||||
|
||||
::glDisable(GL_DEPTH_TEST);
|
||||
|
||||
::glEnable(GL_LIGHTING);
|
||||
|
||||
::glColor3f(1.0f, 1.0f, 1.0f);
|
||||
::glPushMatrix();
|
||||
::glTranslated(center(0), center(1), center(2));
|
||||
::gluSphere(m_quadric, 0.75, 32, 32);
|
||||
::glPopMatrix();
|
||||
|
||||
::glDisable(GL_LIGHTING);
|
||||
}
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
|
||||
void GLCanvas3D::Selection::_update_valid()
|
||||
{
|
||||
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
|
||||
|
||||
const float GLCanvas3D::Gizmos::OverlayTexturesScale = 1.0f;
|
||||
const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f * OverlayTexturesScale;
|
||||
const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale;
|
||||
bool GLCanvas3D::Selection::_requires_local_axes() const
|
||||
{
|
||||
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()
|
||||
: m_enabled(false)
|
||||
|
@ -2584,6 +2676,23 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent)
|
|||
|
||||
m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo));
|
||||
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
m_background_texture.metadata.filename = "toolbar_background.png";
|
||||
m_background_texture.metadata.left = 16;
|
||||
m_background_texture.metadata.top = 16;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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 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)
|
||||
{
|
||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||
continue;
|
||||
|
||||
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
|
||||
float half_tex_size = 0.5f * tex_size;
|
||||
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale;
|
||||
|
||||
// we currently use circular icons for gizmo, so we check the radius
|
||||
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);
|
||||
if (inside)
|
||||
name = it->second->get_name();
|
||||
}
|
||||
top_y += (tex_size + OverlayGapY);
|
||||
top_y += (icon_size + OverlayGapY);
|
||||
}
|
||||
|
||||
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 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)
|
||||
{
|
||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||
continue;
|
||||
|
||||
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
|
||||
float half_tex_size = 0.5f * tex_size;
|
||||
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale;
|
||||
|
||||
// we currently use circular icons for gizmo, so we check the radius
|
||||
if (it->second->is_activable(selection) && ((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);
|
||||
if (it->second->is_activable(selection) && inside)
|
||||
{
|
||||
if ((it->second->get_state() == GLGizmoBase::On))
|
||||
{
|
||||
|
@ -2662,7 +2768,7 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec
|
|||
else
|
||||
it->second->set_state(GLGizmoBase::Off);
|
||||
|
||||
top_y += (tex_size + OverlayGapY);
|
||||
top_y += (icon_size + OverlayGapY);
|
||||
}
|
||||
|
||||
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 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)
|
||||
{
|
||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||
continue;
|
||||
|
||||
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
|
||||
float half_tex_size = 0.5f * tex_size;
|
||||
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale;
|
||||
|
||||
// we currently use circular icons for gizmo, so we check the radius
|
||||
if ((mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size)
|
||||
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))
|
||||
return true;
|
||||
|
||||
top_y += (tex_size + OverlayGapY);
|
||||
top_y += (icon_size + OverlayGapY);
|
||||
}
|
||||
|
||||
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 height = _get_total_overlay_height();
|
||||
float top_x = (OverlayOffsetX - 0.5f * cnv_w) * inv_zoom;
|
||||
float top_y = 0.5f * height * inv_zoom;
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
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;
|
||||
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||
{
|
||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||
continue;
|
||||
|
||||
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale * inv_zoom;
|
||||
GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + tex_size, top_y - tex_size, top_y);
|
||||
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale * inv_zoom;
|
||||
GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + icon_size, top_y - icon_size, top_y);
|
||||
#if ENABLE_IMGUI
|
||||
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
|
||||
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 height = 0.0f;
|
||||
float height = 2.0f * OverlayBorder;
|
||||
|
||||
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;
|
||||
|
||||
height += (float)it->second->get_textures_size() * OverlayTexturesScale + OverlayGapY;
|
||||
height += (float)it->second->get_textures_size() * OverlayIconsScale + 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
|
||||
{
|
||||
GizmosMap::const_iterator it = m_gizmos.find(m_current);
|
||||
|
@ -3434,11 +3635,16 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
|
|||
: m_canvas(canvas)
|
||||
, m_context(nullptr)
|
||||
, m_in_render(false)
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
, m_toolbar(GLToolbar::Normal)
|
||||
#else
|
||||
, m_toolbar(*this)
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
, m_view_toolbar(nullptr)
|
||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
, m_use_clipping_planes(false)
|
||||
, m_sidebar_field("")
|
||||
, m_config(nullptr)
|
||||
, m_process(nullptr)
|
||||
, m_model(nullptr)
|
||||
|
@ -3668,7 +3874,7 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape)
|
|||
|
||||
// Set the origin and size for painting of the coordinate system axes.
|
||||
m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z);
|
||||
set_axes_length(0.3f * (float)m_bed.get_bounding_box().max_size());
|
||||
set_bed_axes_length(0.1 * m_bed.get_bounding_box().max_size());
|
||||
|
||||
if (new_shape)
|
||||
zoom_to_bed();
|
||||
|
@ -3676,9 +3882,9 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape)
|
|||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::set_axes_length(float length)
|
||||
void GLCanvas3D::set_bed_axes_length(double length)
|
||||
{
|
||||
m_axes.length = length;
|
||||
m_axes.length = length * Vec3d::Ones();
|
||||
}
|
||||
|
||||
void GLCanvas3D::set_color_by(const std::string& value)
|
||||
|
@ -3938,21 +4144,20 @@ void GLCanvas3D::render()
|
|||
_render_background();
|
||||
|
||||
if (is_custom_bed) // untextured bed needs to be rendered before objects
|
||||
{
|
||||
_render_bed(theta);
|
||||
// disable depth testing so that axes are not covered by ground
|
||||
_render_axes(false);
|
||||
}
|
||||
|
||||
_render_objects();
|
||||
_render_sla_slices();
|
||||
_render_selection();
|
||||
|
||||
_render_axes();
|
||||
|
||||
if (!is_custom_bed) // textured bed needs to be rendered after objects
|
||||
{
|
||||
_render_axes(true);
|
||||
_render_bed(theta);
|
||||
}
|
||||
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
_render_selection_center();
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
|
||||
// we need to set the mouse's scene position here because the depth buffer
|
||||
// could be invalidated by the following gizmo render methods
|
||||
|
@ -4675,7 +4880,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1;
|
||||
m_layers_editing.last_object_id = layer_editing_object_idx;
|
||||
bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position);
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position, *this);
|
||||
#else
|
||||
int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
int view_toolbar_contains_mouse = (m_view_toolbar != nullptr) ? m_view_toolbar->contains_mouse(m_mouse.position, *this) : -1;
|
||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
|
@ -4699,7 +4908,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
else if (evt.LeftDClick() && (toolbar_contains_mouse != -1))
|
||||
{
|
||||
m_toolbar_action_running = true;
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
m_toolbar.do_action((unsigned int)toolbar_contains_mouse, *this);
|
||||
#else
|
||||
m_toolbar.do_action((unsigned int)toolbar_contains_mouse);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
}
|
||||
else if (evt.LeftDClick() && (m_gizmos.get_current_type() != Gizmos::Undefined))
|
||||
{
|
||||
|
@ -4778,7 +4991,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
else if (toolbar_contains_mouse != -1)
|
||||
{
|
||||
m_toolbar_action_running = true;
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
m_toolbar.do_action((unsigned int)toolbar_contains_mouse, *this);
|
||||
#else
|
||||
m_toolbar.do_action((unsigned int)toolbar_contains_mouse);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
m_mouse.left_down = false;
|
||||
}
|
||||
else
|
||||
|
@ -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())
|
||||
{
|
||||
// deselect and propagate event through callback
|
||||
if (m_picking_enabled && !m_toolbar_action_running && !m_mouse.ignore_up_event)
|
||||
if (!evt.ShiftDown() && m_picking_enabled && !m_toolbar_action_running && !m_mouse.ignore_up_event)
|
||||
{
|
||||
m_selection.clear();
|
||||
m_selection.set_mode(Selection::Instance);
|
||||
|
@ -5061,7 +5278,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
|
||||
// updates toolbar overlay
|
||||
if (tooltip.empty())
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
tooltip = m_toolbar.update_hover_state(m_mouse.position, *this);
|
||||
#else
|
||||
tooltip = m_toolbar.update_hover_state(m_mouse.position);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
|
||||
// updates view toolbar overlay
|
||||
if (tooltip.empty() && (m_view_toolbar != nullptr))
|
||||
|
@ -5413,6 +5634,17 @@ void GLCanvas3D::update_gizmos_on_off_state()
|
|||
m_gizmos.update_on_off_state(get_selection());
|
||||
}
|
||||
|
||||
void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool focus_on)
|
||||
{
|
||||
m_sidebar_field = focus_on ? opt_key : "";
|
||||
|
||||
if (!m_sidebar_field.empty())
|
||||
{
|
||||
m_gizmos.reset_all_states();
|
||||
m_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_is_shown_on_screen() const
|
||||
{
|
||||
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
|
||||
|
@ -5429,7 +5661,24 @@ bool GLCanvas3D::_init_toolbar()
|
|||
if (!m_toolbar.is_enabled())
|
||||
return true;
|
||||
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
ItemsIconsTexture::Metadata icons_data;
|
||||
icons_data.filename = "toolbar.png";
|
||||
icons_data.icon_size = 36;
|
||||
icons_data.icon_border_size = 1;
|
||||
icons_data.icon_gap_size = 1;
|
||||
|
||||
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))
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
{
|
||||
// unable to init the toolbar texture, disable it
|
||||
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::Horizontal);
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
m_toolbar.set_layout_orientation(GLToolbar::Layout::Top);
|
||||
m_toolbar.set_border(5.0f);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
m_toolbar.set_separator_size(5);
|
||||
m_toolbar.set_gap_size(2);
|
||||
|
||||
|
@ -5524,9 +5777,6 @@ bool GLCanvas3D::_init_toolbar()
|
|||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
if (!m_toolbar.add_separator())
|
||||
return false;
|
||||
|
||||
enable_toolbar_item("add", true);
|
||||
|
||||
return true;
|
||||
|
@ -5872,9 +6122,9 @@ void GLCanvas3D::_render_bed(float theta) const
|
|||
m_bed.render(theta);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_axes(bool depth_test) const
|
||||
void GLCanvas3D::_render_axes() const
|
||||
{
|
||||
m_axes.render(depth_test);
|
||||
m_axes.render();
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_objects() const
|
||||
|
@ -5950,6 +6200,14 @@ void GLCanvas3D::_render_selection() const
|
|||
m_selection.render();
|
||||
}
|
||||
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
void GLCanvas3D::_render_selection_center() const
|
||||
{
|
||||
if (!m_gizmos.is_running())
|
||||
m_selection.render_center();
|
||||
}
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
|
||||
void GLCanvas3D::_render_warning_texture() const
|
||||
{
|
||||
if (!m_warning_texture_enabled)
|
||||
|
@ -6063,7 +6321,11 @@ void GLCanvas3D::_render_toolbar() const
|
|||
#if !ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
_resize_toolbar();
|
||||
#endif // !ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
m_toolbar.render(*this);
|
||||
#else
|
||||
m_toolbar.render();
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
}
|
||||
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
|
@ -6093,9 +6355,7 @@ void GLCanvas3D::_render_camera_target() const
|
|||
::glColor3f(0.0f, 1.0f, 0.0f);
|
||||
::glVertex3d(target(0), target(1) - half_length, target(2));
|
||||
::glVertex3d(target(0), target(1) + half_length, target(2));
|
||||
::glEnd();
|
||||
|
||||
::glBegin(GL_LINES);
|
||||
// draw line for z axis
|
||||
::glColor3f(0.0f, 0.0f, 1.0f);
|
||||
::glVertex3d(target(0), target(1), target(2) - half_length);
|
||||
::glVertex3d(target(0), target(1), target(2) + half_length);
|
||||
|
@ -7721,25 +7981,54 @@ void GLCanvas3D::_resize_toolbar() const
|
|||
float zoom = get_camera_zoom();
|
||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation();
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
|
||||
switch (m_toolbar.get_layout_type())
|
||||
{
|
||||
default:
|
||||
case GLToolbar::Layout::Horizontal:
|
||||
{
|
||||
// centers the toolbar on the top edge of the 3d scene
|
||||
unsigned int toolbar_width = m_toolbar.get_width();
|
||||
float top = (0.5f * (float)cnv_size.get_height() - 2.0f) * inv_zoom;
|
||||
float left = -0.5f * (float)toolbar_width * inv_zoom;
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
float top, left;
|
||||
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);
|
||||
break;
|
||||
}
|
||||
case GLToolbar::Layout::Vertical:
|
||||
{
|
||||
// centers the toolbar on the right edge of the 3d scene
|
||||
unsigned int toolbar_width = m_toolbar.get_width();
|
||||
unsigned int toolbar_height = m_toolbar.get_height();
|
||||
float top = 0.5f * (float)toolbar_height * inv_zoom;
|
||||
float left = (0.5f * (float)cnv_size.get_width() - toolbar_width - 2.0f) * inv_zoom;
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
float top, left;
|
||||
if (orientation == GLToolbar::Layout::Left)
|
||||
{
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
@ -7748,6 +8037,7 @@ void GLCanvas3D::_resize_toolbar() const
|
|||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
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 left = -0.5f * (float)cnv_size.get_width() * inv_zoom;
|
||||
m_view_toolbar->set_position(top, left);
|
||||
|
|
|
@ -20,6 +20,8 @@ class wxTimerEvent;
|
|||
class wxPaintEvent;
|
||||
class wxGLCanvas;
|
||||
|
||||
class GLUquadric;
|
||||
typedef class GLUquadric GLUquadricObj;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -155,7 +157,7 @@ class GLCanvas3D
|
|||
// float distance;
|
||||
#if !ENABLE_CONSTRAINED_CAMERA_TARGET
|
||||
Vec3d target;
|
||||
#endif !// ENABLE_CONSTRAINED_CAMERA_TARGET
|
||||
#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
|
||||
|
||||
private:
|
||||
#if ENABLE_CONSTRAINED_CAMERA_TARGET
|
||||
|
@ -231,12 +233,20 @@ class GLCanvas3D
|
|||
|
||||
struct Axes
|
||||
{
|
||||
static const double Radius;
|
||||
static const double ArrowBaseRadius;
|
||||
static const double ArrowLength;
|
||||
Vec3d origin;
|
||||
float length;
|
||||
Vec3d length;
|
||||
GLUquadricObj* m_quadric;
|
||||
|
||||
Axes();
|
||||
~Axes();
|
||||
|
||||
void render(bool depth_test) const;
|
||||
void render() const;
|
||||
|
||||
private:
|
||||
void render_axis(double length) const;
|
||||
};
|
||||
|
||||
class Shader
|
||||
|
@ -481,8 +491,15 @@ public:
|
|||
mutable BoundingBoxf3 m_bounding_box;
|
||||
mutable bool m_bounding_box_dirty;
|
||||
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
GLUquadricObj* m_quadric;
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
|
||||
public:
|
||||
Selection();
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
~Selection();
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
|
||||
void set_volumes(GLVolumePtrs* volumes);
|
||||
|
||||
|
@ -515,6 +532,7 @@ public:
|
|||
bool is_wipe_tower() const { return m_type == WipeTower; }
|
||||
bool is_modifier() const { return (m_type == SingleModifier) || (m_type == MultipleModifier); }
|
||||
bool is_single_modifier() const { return m_type == SingleModifier; }
|
||||
bool is_multiple_modifier() const { return m_type == MultipleModifier; }
|
||||
bool is_single_full_instance() const;
|
||||
bool is_multiple_full_instance() const { return m_type == MultipleFullInstance; }
|
||||
bool is_single_full_object() const { return m_type == SingleFullObject; }
|
||||
|
@ -526,6 +544,7 @@ public:
|
|||
bool is_from_single_object() const;
|
||||
|
||||
bool contains_volume(unsigned int volume_idx) const { return std::find(m_list.begin(), m_list.end(), volume_idx) != m_list.end(); }
|
||||
bool requires_uniform_scale() const;
|
||||
|
||||
// Returns the the object id if the selection is from a single object, otherwise is -1
|
||||
int get_object_idx() const;
|
||||
|
@ -557,6 +576,9 @@ public:
|
|||
void erase();
|
||||
|
||||
void render() const;
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
void render_center() const;
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
|
||||
private:
|
||||
void _update_valid();
|
||||
|
@ -577,6 +599,7 @@ public:
|
|||
#if ENABLE_ENSURE_ON_BED_WHILE_SCALING
|
||||
void _ensure_on_bed();
|
||||
#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING
|
||||
bool _requires_local_axes() const;
|
||||
};
|
||||
|
||||
class ClippingPlane
|
||||
|
@ -607,8 +630,8 @@ public:
|
|||
private:
|
||||
class Gizmos
|
||||
{
|
||||
static const float OverlayTexturesScale;
|
||||
static const float OverlayOffsetX;
|
||||
static const float OverlayIconsScale;
|
||||
static const float OverlayBorder;
|
||||
static const float OverlayGapY;
|
||||
|
||||
public:
|
||||
|
@ -628,6 +651,9 @@ private:
|
|||
bool m_enabled;
|
||||
typedef std::map<EType, GLGizmoBase*> GizmosMap;
|
||||
GizmosMap m_gizmos;
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
BackgroundTexture m_background_texture;
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
EType m_current;
|
||||
|
||||
public:
|
||||
|
@ -696,6 +722,9 @@ private:
|
|||
void _render_current_gizmo(const Selection& selection) const;
|
||||
|
||||
float _get_total_overlay_height() const;
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
float _get_total_overlay_width() const;
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
GLGizmoBase* _get_current() const;
|
||||
};
|
||||
|
||||
|
@ -769,11 +798,16 @@ private:
|
|||
mutable Gizmos m_gizmos;
|
||||
mutable GLToolbar m_toolbar;
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
GLToolbar* m_view_toolbar;
|
||||
#else
|
||||
GLRadioToolbar* m_view_toolbar;
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
ClippingPlane m_clipping_planes[2];
|
||||
bool m_use_clipping_planes;
|
||||
mutable SlaCap m_sla_caps[2];
|
||||
std::string m_sidebar_field;
|
||||
|
||||
mutable GLVolumeCollection m_volumes;
|
||||
Selection m_selection;
|
||||
|
@ -824,7 +858,11 @@ public:
|
|||
wxGLCanvas* get_wxglcanvas() { return m_canvas; }
|
||||
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
void set_view_toolbar(GLToolbar* toolbar) { m_view_toolbar = toolbar; }
|
||||
#else
|
||||
void set_view_toolbar(GLRadioToolbar* toolbar) { m_view_toolbar = toolbar; }
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
|
||||
bool init(bool useVBOs, bool use_legacy_opengl);
|
||||
|
@ -856,8 +894,7 @@ public:
|
|||
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
|
||||
// Sets m_bed.m_polygon to limit the object placement.
|
||||
void set_bed_shape(const Pointfs& shape);
|
||||
|
||||
void set_axes_length(float length);
|
||||
void set_bed_axes_length(double length);
|
||||
|
||||
void set_clipping_plane(unsigned int id, const ClippingPlane& plane)
|
||||
{
|
||||
|
@ -972,7 +1009,7 @@ public:
|
|||
void viewport_changed();
|
||||
#endif // ENABLE_CONSTRAINED_CAMERA_TARGET
|
||||
|
||||
void handle_sidebar_focus_event(const std::string& opt_key) {}
|
||||
void handle_sidebar_focus_event(const std::string& opt_key, bool focus_on);
|
||||
|
||||
private:
|
||||
bool _is_shown_on_screen() const;
|
||||
|
@ -997,9 +1034,12 @@ private:
|
|||
void _picking_pass() const;
|
||||
void _render_background() const;
|
||||
void _render_bed(float theta) const;
|
||||
void _render_axes(bool depth_test) const;
|
||||
void _render_axes() const;
|
||||
void _render_objects() const;
|
||||
void _render_selection() const;
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
void _render_selection_center() const;
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
void _render_warning_texture() const;
|
||||
void _render_legend_texture() const;
|
||||
void _render_layer_editing_overlay() const;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -77,6 +77,9 @@ public:
|
|||
void do_action(wxEvtHandler *target);
|
||||
|
||||
bool is_enabled() const;
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
bool is_disabled() const;
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
bool is_hovered() const;
|
||||
bool is_pressed() const;
|
||||
|
||||
|
@ -94,7 +97,25 @@ private:
|
|||
// from left to right
|
||||
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;
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
Metadata metadata;
|
||||
#else
|
||||
// size of the square icons, in pixels
|
||||
unsigned int items_icon_size;
|
||||
// distance from the border, in pixels
|
||||
|
@ -103,25 +124,82 @@ struct ItemsIconsTexture
|
|||
unsigned int items_icon_gap_size;
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
enum EType : unsigned char
|
||||
{
|
||||
Normal,
|
||||
Radio,
|
||||
Num_Types
|
||||
};
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
|
||||
struct Layout
|
||||
{
|
||||
enum Type : unsigned char
|
||||
enum EType : unsigned char
|
||||
{
|
||||
Horizontal,
|
||||
Vertical,
|
||||
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 left;
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
float border;
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
float separator_size;
|
||||
float gap_size;
|
||||
float icons_scale;
|
||||
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
float width;
|
||||
float height;
|
||||
bool dirty;
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
|
||||
Layout();
|
||||
};
|
||||
|
@ -129,25 +207,50 @@ public:
|
|||
private:
|
||||
typedef std::vector<GLToolbarItem*> ItemsList;
|
||||
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
EType m_type;
|
||||
#else
|
||||
GLCanvas3D& m_parent;
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
bool m_enabled;
|
||||
ItemsIconsTexture m_icons_texture;
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
BackgroundTexture m_background_texture;
|
||||
mutable Layout m_layout;
|
||||
#else
|
||||
Layout m_layout;
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
|
||||
ItemsList m_items;
|
||||
|
||||
public:
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
explicit GLToolbar(EType type);
|
||||
#else
|
||||
explicit GLToolbar(GLCanvas3D& parent);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
~GLToolbar();
|
||||
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
bool init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture);
|
||||
#else
|
||||
bool init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
|
||||
Layout::Type get_layout_type() const;
|
||||
void set_layout_type(Layout::Type type);
|
||||
Layout::EType get_layout_type() const;
|
||||
void set_layout_type(Layout::EType type);
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
Layout::EOrientation get_layout_orientation() const;
|
||||
void set_layout_orientation(Layout::EOrientation orientation);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
|
||||
void set_position(float top, float left);
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
void set_border(float border);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
void set_separator_size(float size);
|
||||
void set_gap_size(float size);
|
||||
void set_icons_scale(float scale);
|
||||
|
||||
bool is_enabled() const;
|
||||
void set_enabled(bool enable);
|
||||
|
@ -160,42 +263,89 @@ public:
|
|||
|
||||
void enable_item(const std::string& name);
|
||||
void disable_item(const std::string& name);
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
void select_item(const std::string& name);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
|
||||
bool is_item_pressed(const std::string& name) const;
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
bool is_item_disabled(const std::string& name) const;
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
#else
|
||||
std::string update_hover_state(const Vec2d& mouse_pos);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
#else
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
void update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
#else
|
||||
void update_hover_state(const Vec2d& mouse_pos);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
// returns the id of the item under the given mouse position or -1 if none
|
||||
int contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const;
|
||||
|
||||
void do_action(unsigned int item_id, GLCanvas3D& parent);
|
||||
#else
|
||||
// returns the id of the item under the given mouse position or -1 if none
|
||||
int contains_mouse(const Vec2d& mouse_pos) const;
|
||||
|
||||
void do_action(unsigned int item_id);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
void render(const GLCanvas3D& parent) const;
|
||||
#else
|
||||
void render() const;
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
|
||||
private:
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
void calc_layout() const;
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
float get_width_horizontal() const;
|
||||
float get_width_vertical() const;
|
||||
float get_height_horizontal() const;
|
||||
float get_height_vertical() const;
|
||||
float get_main_size() const;
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
std::string update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
std::string update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
#else
|
||||
std::string update_hover_state_horizontal(const Vec2d& mouse_pos);
|
||||
std::string update_hover_state_vertical(const Vec2d& mouse_pos);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
#else
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
void update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
void update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent);
|
||||
#else
|
||||
void update_hover_state_horizontal(const Vec2d& mouse_pos);
|
||||
void update_hover_state_vertical(const Vec2d& mouse_pos);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
int contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const;
|
||||
int contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const;
|
||||
|
||||
void render_horizontal(const GLCanvas3D& parent) const;
|
||||
void render_vertical(const GLCanvas3D& parent) const;
|
||||
#else
|
||||
int contains_mouse_horizontal(const Vec2d& mouse_pos) const;
|
||||
int contains_mouse_vertical(const Vec2d& mouse_pos) const;
|
||||
|
||||
void render_horizontal() const;
|
||||
void render_vertical() const;
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
};
|
||||
|
||||
#if !ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
class GLRadioToolbarItem
|
||||
{
|
||||
public:
|
||||
|
@ -274,6 +424,7 @@ public:
|
|||
|
||||
void render(const GLCanvas3D& parent) const;
|
||||
};
|
||||
#endif // !ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
|
||||
} // namespace GUI
|
||||
} // 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)));
|
||||
else if (opt_key.compare("host_type") == 0)
|
||||
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;
|
||||
case coPoints:{
|
||||
|
|
|
@ -73,7 +73,6 @@ GUI_App::GUI_App()
|
|||
: wxApp()
|
||||
#if ENABLE_IMGUI
|
||||
, m_imgui(new ImGuiWrapper())
|
||||
, m_printhost_queue(new PrintHostJobQueue())
|
||||
#endif // ENABLE_IMGUI
|
||||
{}
|
||||
|
||||
|
@ -142,6 +141,8 @@ bool GUI_App::OnInit()
|
|||
update_mode();
|
||||
SetTopWindow(mainframe);
|
||||
|
||||
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
||||
|
||||
CallAfter([this]() {
|
||||
// temporary workaround for the correct behavior of the Scrolled sidebar panel
|
||||
auto& panel = sidebar();
|
||||
|
|
|
@ -92,7 +92,7 @@ class GUI_App : public wxApp
|
|||
std::unique_ptr<ImGuiWrapper> m_imgui;
|
||||
#endif // ENABLE_IMGUI
|
||||
|
||||
std::unique_ptr<PrintHostJobQueue> m_printhost_queue;
|
||||
std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue;
|
||||
|
||||
public:
|
||||
bool OnInit() override;
|
||||
|
@ -164,7 +164,7 @@ public:
|
|||
ImGuiWrapper* imgui() { return m_imgui.get(); }
|
||||
#endif // ENABLE_IMGUI
|
||||
|
||||
PrintHostJobQueue& printhost_queue() { return *m_printhost_queue.get(); }
|
||||
PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); }
|
||||
|
||||
};
|
||||
DECLARE_APP(GUI_App)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
if (obj_idx == 1000)
|
||||
// Cannot delete a wipe tower.
|
||||
return false;
|
||||
|
||||
if (type == itVolume) {
|
||||
const auto volume = (*m_objects)[obj_idx]->volumes[idx];
|
||||
|
||||
|
@ -1399,6 +1403,20 @@ void ObjectList::update_selections()
|
|||
auto& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||
wxDataViewItemArray sels;
|
||||
|
||||
// We doesn't update selection if SettingsItem for the current object/part is selected
|
||||
if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings )
|
||||
{
|
||||
const auto item = GetSelection();
|
||||
if (selection.is_single_full_object() &&
|
||||
m_objects_model->GetIdByItem(m_objects_model->GetParent(item)) == selection.get_object_idx())
|
||||
return;
|
||||
if (selection.is_single_volume() || selection.is_modifier()) {
|
||||
const auto gl_vol = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
if (m_objects_model->GetVolumeIdByItem(m_objects_model->GetParent(item)) == gl_vol->volume_idx())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (selection.is_single_full_object())
|
||||
{
|
||||
sels.Add(m_objects_model->GetItemById(selection.get_object_idx()));
|
||||
|
@ -1459,13 +1477,11 @@ void ObjectList::update_selections()
|
|||
|
||||
select_items(sels);
|
||||
|
||||
#ifdef __WXMSW__
|
||||
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);
|
||||
m_selected_row = sel_item_row;
|
||||
}
|
||||
#endif //__WXMSW__
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
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;
|
||||
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) {
|
||||
std::vector<std::string> axes{ "_x", "_y", "_z" };
|
||||
|
||||
if (opt_key == "scale_unit") {
|
||||
const wxString& selection = boost::any_cast<wxString>(value);
|
||||
for (auto axis : axes) {
|
||||
std::string key = "scale" + axis;
|
||||
m_og->set_side_text(key, selection);
|
||||
}
|
||||
|
||||
m_is_percent_scale = selection == _("%");
|
||||
update_scale_values();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string param;
|
||||
std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param));
|
||||
|
||||
|
@ -51,29 +39,51 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||
change_rotation_value(new_value);
|
||||
else if (param == "scale")
|
||||
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)
|
||||
{
|
||||
if (opt_key == "scale_unit")
|
||||
return;
|
||||
|
||||
std::string param;
|
||||
std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param));
|
||||
|
||||
double value = 0.0;
|
||||
|
||||
if (param == "position") {
|
||||
int axis = opt_key.back() == 'x' ? 0 :
|
||||
opt_key.back() == 'y' ? 1 : 2;
|
||||
|
||||
m_og->set_value(opt_key, double_to_string(cache_position(axis)));
|
||||
return;
|
||||
value = cache_position(axis);
|
||||
}
|
||||
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)
|
||||
{
|
||||
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key);
|
||||
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, true);
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
Line line = { _(option_name), "" };
|
||||
if (option_name == "Scale") {
|
||||
line.near_label_widget = [](wxWindow* parent) {
|
||||
auto btn = new PrusaLockButton(parent, wxID_ANY);
|
||||
btn->Bind(wxEVT_BUTTON, [btn](wxCommandEvent &event) {
|
||||
event.Skip();
|
||||
wxTheApp->CallAfter([btn]() {
|
||||
wxGetApp().obj_manipul()->set_uniform_scaling(btn->IsLocked());
|
||||
});
|
||||
});
|
||||
return btn;
|
||||
};
|
||||
}
|
||||
|
||||
ConfigOptionDef def;
|
||||
def.type = coFloat;
|
||||
def.default_value = new ConfigOptionFloat(0.0);
|
||||
def.width = 50;
|
||||
|
||||
if (option_name == "Rotation")
|
||||
{
|
||||
def.min = -360;
|
||||
def.max = 360;
|
||||
}
|
||||
|
||||
const std::string lower_name = boost::algorithm::to_lower_copy(option_name);
|
||||
|
||||
std::vector<std::string> axes{ "x", "y", "z" };
|
||||
for (auto axis : axes) {
|
||||
if (axis == "z" && option_name != "Scale")
|
||||
if (axis == "z")
|
||||
def.sidetext = sidetext;
|
||||
Option option = Option(def, lower_name + "_" + axis);
|
||||
option.opt.full_width = true;
|
||||
line.append_option(option);
|
||||
}
|
||||
|
||||
if (option_name == "Scale")
|
||||
{
|
||||
def.width = 45;
|
||||
def.type = coStrings;
|
||||
def.gui_type = "select_open";
|
||||
def.enum_labels.push_back(L("%"));
|
||||
def.enum_labels.push_back(L("mm"));
|
||||
def.default_value = new ConfigOptionStrings{ "mm" };
|
||||
|
||||
const Option option = Option(def, lower_name + "_unit");
|
||||
line.append_option(option);
|
||||
}
|
||||
|
||||
return line;
|
||||
};
|
||||
|
||||
|
||||
// Settings table
|
||||
m_og->append_line(add_og_to_object_settings(L("Position"), L("mm")), &m_move_Label);
|
||||
m_og->append_line(add_og_to_object_settings(L("Rotation"), "°"));
|
||||
m_og->append_line(add_og_to_object_settings(L("Scale"), "mm"));
|
||||
m_og->append_line(add_og_to_object_settings(L("Rotation"), "°"), &m_rotate_Label);
|
||||
m_og->append_line(add_og_to_object_settings(L("Scale"), "%"), &m_scale_Label);
|
||||
m_og->append_line(add_og_to_object_settings(L("Size"), "mm"));
|
||||
|
||||
/* Unused parameter at this time
|
||||
def.label = L("Place on bed");
|
||||
|
@ -204,9 +192,11 @@ int ObjectManipulation::ol_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 (selection.is_single_full_instance() || selection.is_single_full_object())
|
||||
if (selection.is_single_full_instance())
|
||||
#else
|
||||
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_rotation_value(volume->get_instance_rotation());
|
||||
update_scale_value(volume->get_instance_scaling_factor());
|
||||
update_size_value(volume->get_instance_transformation().get_matrix(true, true) * volume->bounding_box.size());
|
||||
#else
|
||||
update_position_value(volume->get_offset());
|
||||
update_rotation_value(volume->get_rotation());
|
||||
|
@ -239,19 +230,15 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele
|
|||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
||||
m_og->enable();
|
||||
}
|
||||
else if (selection.is_wipe_tower())
|
||||
else if (selection.is_single_full_object())
|
||||
{
|
||||
// the selection contains a single volume
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||
update_position_value(volume->get_volume_offset());
|
||||
update_rotation_value(volume->get_volume_rotation());
|
||||
update_scale_value(volume->get_volume_scaling_factor());
|
||||
#else
|
||||
update_position_value(volume->get_offset());
|
||||
update_rotation_value(volume->get_rotation());
|
||||
update_scale_value(volume->get_scaling_factor());
|
||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
||||
const BoundingBoxf3& box = selection.get_bounding_box();
|
||||
update_position_value(box.center());
|
||||
reset_rotation_value();
|
||||
reset_scale_value();
|
||||
update_size_value(box.size());
|
||||
rotate_label = _(L("Rotate:"));
|
||||
scale_label = _(L("Scale:"));
|
||||
m_og->enable();
|
||||
}
|
||||
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_rotation_value(volume->get_volume_rotation());
|
||||
update_scale_value(volume->get_volume_scaling_factor());
|
||||
update_size_value(volume->bounding_box.size());
|
||||
#else
|
||||
update_position_value(volume->get_offset());
|
||||
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())
|
||||
{
|
||||
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();
|
||||
}
|
||||
else
|
||||
reset_settings_value();
|
||||
|
||||
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()
|
||||
|
@ -299,7 +291,7 @@ void ObjectManipulation::reset_position_value()
|
|||
m_og->set_value("position_y", def_0);
|
||||
m_og->set_value("position_z", def_0);
|
||||
|
||||
cache_position = { 0., 0., 0. };
|
||||
cache_position = Vec3d::Zero();
|
||||
}
|
||||
|
||||
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_y", def_0);
|
||||
m_og->set_value("rotation_z", def_0);
|
||||
|
||||
cache_rotation = Vec3d::Zero();
|
||||
}
|
||||
|
||||
void ObjectManipulation::reset_scale_value()
|
||||
{
|
||||
m_is_percent_scale = true;
|
||||
m_og->set_value("scale_unit", _("%"));
|
||||
m_og->set_value("scale_x", def_100);
|
||||
m_og->set_value("scale_y", def_100);
|
||||
m_og->set_value("scale_z", def_100);
|
||||
|
||||
cache_scale = Vec3d(100.0, 100.0, 100.0);
|
||||
}
|
||||
|
||||
void ObjectManipulation::update_values()
|
||||
void ObjectManipulation::reset_size_value()
|
||||
{
|
||||
int selection = ol_selection();
|
||||
if (selection < 0 || wxGetApp().mainframe->m_plater->model().objects.size() <= selection) {
|
||||
m_og->set_value("position_x", def_0);
|
||||
m_og->set_value("position_y", def_0);
|
||||
m_og->set_value("position_z", def_0);
|
||||
m_og->set_value("scale_x" , def_0);
|
||||
m_og->set_value("scale_y" , def_0);
|
||||
m_og->set_value("scale_z" , def_0);
|
||||
m_og->set_value("rotation_x", def_0);
|
||||
m_og->set_value("rotation_y", def_0);
|
||||
m_og->set_value("rotation_z", def_0);
|
||||
m_og->disable();
|
||||
return;
|
||||
}
|
||||
m_is_percent_scale = boost::any_cast<wxString>(m_og->get_value("scale_unit")) == _("%");
|
||||
m_og->set_value("size_x", def_0);
|
||||
m_og->set_value("size_y", def_0);
|
||||
m_og->set_value("size_z", def_0);
|
||||
|
||||
update_position_values();
|
||||
update_scale_values();
|
||||
update_rotation_values();
|
||||
m_og->enable();
|
||||
}
|
||||
|
||||
void ObjectManipulation::update_scale_values()
|
||||
{
|
||||
int selection = ol_selection();
|
||||
ModelObjectPtrs& objects = wxGetApp().mainframe->m_plater->model().objects;
|
||||
|
||||
auto instance = objects[selection]->instances.front();
|
||||
auto size = objects[selection]->instance_bounding_box(0).size();
|
||||
|
||||
if (m_is_percent_scale) {
|
||||
m_og->set_value("scale_x", double_to_string(instance->get_scaling_factor(X) * 100, 2));
|
||||
m_og->set_value("scale_y", double_to_string(instance->get_scaling_factor(Y) * 100, 2));
|
||||
m_og->set_value("scale_z", double_to_string(instance->get_scaling_factor(Z) * 100, 2));
|
||||
}
|
||||
else {
|
||||
m_og->set_value("scale_x", double_to_string(size(0), 2));
|
||||
m_og->set_value("scale_y", double_to_string(size(1), 2));
|
||||
m_og->set_value("scale_z", double_to_string(size(2), 2));
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectManipulation::update_position_values()
|
||||
{
|
||||
auto instance = wxGetApp().mainframe->m_plater->model().objects[ol_selection()]->instances.front();
|
||||
|
||||
m_og->set_value("position_x", double_to_string(instance->get_offset(X), 2));
|
||||
m_og->set_value("position_y", double_to_string(instance->get_offset(Y), 2));
|
||||
m_og->set_value("position_z", double_to_string(instance->get_offset(Z), 2));
|
||||
cache_size = Vec3d::Zero();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// this is temporary
|
||||
// to be able to update the values as size
|
||||
// we need to store somewhere the original size
|
||||
// or have it passed as parameter
|
||||
if (!m_is_percent_scale) {
|
||||
m_is_percent_scale = true;
|
||||
m_og->set_value("scale_unit", _("%"));
|
||||
}
|
||||
|
||||
auto scale = scaling_factor * 100.0;
|
||||
m_og->set_value("scale_x", double_to_string(scale(0), 2));
|
||||
m_og->set_value("scale_y", double_to_string(scale(1), 2));
|
||||
m_og->set_value("scale_z", double_to_string(scale(2), 2));
|
||||
|
||||
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)
|
||||
{
|
||||
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));
|
||||
cache_size = size;
|
||||
}
|
||||
|
||||
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_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));
|
||||
}
|
||||
|
||||
cache_rotation = rotation;
|
||||
}
|
||||
|
||||
void ObjectManipulation::change_position_value(const Vec3d& position)
|
||||
{
|
||||
Vec3d displacement(position - cache_position);
|
||||
|
||||
auto canvas = wxGetApp().plater()->canvas3D();
|
||||
canvas->get_selection().start_dragging();
|
||||
canvas->get_selection().translate(displacement);
|
||||
canvas->get_selection().translate(position - cache_position);
|
||||
canvas->do_move();
|
||||
|
||||
cache_position = position;
|
||||
|
@ -442,28 +370,43 @@ void ObjectManipulation::change_position_value(const Vec3d& position)
|
|||
|
||||
void ObjectManipulation::change_rotation_value(const Vec3d& rotation)
|
||||
{
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
const GLCanvas3D::Selection& selection = canvas->get_selection();
|
||||
|
||||
Vec3d rad_rotation;
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
rad_rotation(i) = Geometry::deg2rad(rotation(i));
|
||||
auto canvas = wxGetApp().plater()->canvas3D();
|
||||
}
|
||||
|
||||
canvas->get_selection().start_dragging();
|
||||
canvas->get_selection().rotate(rad_rotation, false);
|
||||
canvas->get_selection().rotate(rad_rotation, selection.is_single_full_instance());
|
||||
canvas->do_rotate();
|
||||
}
|
||||
|
||||
void ObjectManipulation::change_scale_value(const Vec3d& scale)
|
||||
{
|
||||
Vec3d scaling_factor;
|
||||
if (m_is_percent_scale)
|
||||
scaling_factor = scale*0.01;
|
||||
else {
|
||||
int selection = ol_selection();
|
||||
ModelObjectPtrs& objects = *wxGetApp().model_objects();
|
||||
|
||||
auto size = objects[selection]->instance_bounding_box(0).size();
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
scaling_factor(i) = scale(i) / size(i);
|
||||
Vec3d scaling_factor = scale;
|
||||
const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||
if (selection.requires_uniform_scale())
|
||||
{
|
||||
Vec3d abs_scale_diff = (scale - cache_scale).cwiseAbs();
|
||||
double max_diff = abs_scale_diff(X);
|
||||
Axis max_diff_axis = X;
|
||||
if (max_diff < abs_scale_diff(Y))
|
||||
{
|
||||
max_diff = abs_scale_diff(Y);
|
||||
max_diff_axis = Y;
|
||||
}
|
||||
if (max_diff < abs_scale_diff(Z))
|
||||
{
|
||||
max_diff = abs_scale_diff(Z);
|
||||
max_diff_axis = Z;
|
||||
}
|
||||
scaling_factor = Vec3d(scale(max_diff_axis), scale(max_diff_axis), scale(max_diff_axis));
|
||||
}
|
||||
|
||||
scaling_factor *= 0.01;
|
||||
|
||||
auto canvas = wxGetApp().plater()->canvas3D();
|
||||
canvas->get_selection().start_dragging();
|
||||
|
@ -471,9 +414,18 @@ void ObjectManipulation::change_scale_value(const Vec3d& 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
|
||||
|
|
|
@ -14,12 +14,14 @@ namespace GUI {
|
|||
|
||||
class ObjectManipulation : public OG_Settings
|
||||
{
|
||||
bool m_is_percent_scale = false; // true -> percentage scale unit
|
||||
// false -> uniform scale unit
|
||||
bool m_is_uniform_scale = false; // It indicates if scale is uniform
|
||||
|
||||
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_scale_Label = nullptr;
|
||||
wxStaticText* m_rotate_Label = nullptr;
|
||||
|
||||
public:
|
||||
ObjectManipulation(wxWindow* parent);
|
||||
|
@ -36,31 +38,22 @@ public:
|
|||
void reset_position_value();
|
||||
void reset_rotation_value();
|
||||
void reset_scale_value();
|
||||
void reset_size_value();
|
||||
|
||||
void update_values();
|
||||
// update position values displacements or "gizmos"
|
||||
void update_position_values();
|
||||
void update_position_value(const Vec3d& position);
|
||||
// update scale values after scale unit changing or "gizmos"
|
||||
void update_scale_values();
|
||||
void update_scale_value(const Vec3d& scaling_factor);
|
||||
// update rotation values object selection changing
|
||||
void update_rotation_values();
|
||||
// update size values after scale unit changing or "gizmos"
|
||||
void update_size_value(const Vec3d& size);
|
||||
// update rotation value after "gizmos"
|
||||
void update_rotation_value(double angle, Axis axis);
|
||||
void update_rotation_value(const Vec3d& rotation);
|
||||
|
||||
void set_uniform_scaling(const bool uniform_scale) { m_is_uniform_scale = uniform_scale; }
|
||||
|
||||
|
||||
// change values
|
||||
void change_position_value(const Vec3d& position);
|
||||
void change_rotation_value(const Vec3d& rotation);
|
||||
void change_scale_value(const Vec3d& scale);
|
||||
|
||||
|
||||
private:
|
||||
void print_cashe_value(const std::string& label, const Vec3d& value);
|
||||
void change_size_value(const Vec3d& size);
|
||||
};
|
||||
|
||||
}}
|
||||
|
|
|
@ -92,7 +92,11 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba
|
|||
return true;
|
||||
}
|
||||
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
void View3D::set_view_toolbar(GLToolbar* toolbar)
|
||||
#else
|
||||
void View3D::set_view_toolbar(GLRadioToolbar* toolbar)
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
{
|
||||
if (m_canvas != nullptr)
|
||||
m_canvas->set_view_toolbar(toolbar);
|
||||
|
@ -365,7 +369,11 @@ Preview::~Preview()
|
|||
}
|
||||
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
void Preview::set_view_toolbar(GLToolbar* toolbar)
|
||||
#else
|
||||
void Preview::set_view_toolbar(GLRadioToolbar* toolbar)
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
{
|
||||
if (m_canvas != nullptr)
|
||||
m_canvas->set_view_toolbar(toolbar);
|
||||
|
@ -377,9 +385,6 @@ void Preview::set_number_extruders(unsigned int number_extruders)
|
|||
if (m_number_extruders != number_extruders)
|
||||
{
|
||||
m_number_extruders = number_extruders;
|
||||
int type = 0; // color by a feature type
|
||||
if (number_extruders > 1)
|
||||
{
|
||||
int tool_idx = m_choice_view_type->FindString(_(L("Tool")));
|
||||
int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type
|
||||
m_choice_view_type->SetSelection(type);
|
||||
|
@ -389,7 +394,6 @@ void Preview::set_number_extruders(unsigned int number_extruders)
|
|||
m_preferred_color_mode = (type == tool_idx) ? "tool_or_feature" : "feature";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Preview::reset_gcode_preview_data()
|
||||
{
|
||||
|
|
|
@ -28,9 +28,15 @@ class Model;
|
|||
namespace GUI {
|
||||
|
||||
class GLCanvas3D;
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
class GLToolbar;
|
||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
#else
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
class GLRadioToolbar;
|
||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
class View3D : public wxPanel
|
||||
|
@ -53,7 +59,11 @@ public:
|
|||
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
|
||||
GLCanvas3D* get_canvas3d() { return m_canvas; }
|
||||
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
void set_view_toolbar(GLToolbar* toolbar);
|
||||
#else
|
||||
void set_view_toolbar(GLRadioToolbar* toolbar);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
|
||||
void set_as_dirty();
|
||||
void set_bed_shape(const Pointfs& shape);
|
||||
|
@ -122,7 +132,11 @@ public:
|
|||
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
|
||||
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
void set_view_toolbar(GLToolbar* toolbar);
|
||||
#else
|
||||
void set_view_toolbar(GLRadioToolbar* toolbar);
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
|
||||
void set_number_extruders(unsigned int number_extruders);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "ProgressStatusBar.hpp"
|
||||
#include "3DScene.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
#include "PrintHostDialogs.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
|
@ -30,7 +31,8 @@ namespace GUI {
|
|||
MainFrame::MainFrame(const bool no_plater, const bool loaded) :
|
||||
wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE),
|
||||
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.
|
||||
#if _WIN32
|
||||
|
@ -326,7 +328,7 @@ void MainFrame::init_menubar()
|
|||
size_t tab_offset = 0;
|
||||
if (m_plater) {
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
append_menu_item(windowMenu, wxID_ANY, L("Plater Tab\tCtrl+1"), L("Show the plater"),
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 1, L("Plater Tab\tCtrl+1"), L("Show the plater"),
|
||||
[this](wxCommandEvent&) { select_tab(0); }, "application_view_tile.png");
|
||||
#else
|
||||
append_menu_item(windowMenu, wxID_ANY, L("Select Plater Tab\tCtrl+1"), L("Show the plater"),
|
||||
|
@ -338,22 +340,35 @@ void MainFrame::init_menubar()
|
|||
windowMenu->AppendSeparator();
|
||||
}
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
append_menu_item(windowMenu, wxID_ANY, L("Print Settings Tab\tCtrl+2"), L("Show the print settings"),
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 2, L("Print Settings Tab\tCtrl+2"), L("Show the print settings"),
|
||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png");
|
||||
append_menu_item(windowMenu, wxID_ANY, L("Filament Settings Tab\tCtrl+3"), L("Show the filament settings"),
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 3, L("Filament Settings Tab\tCtrl+3"), L("Show the filament settings"),
|
||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool.png");
|
||||
append_menu_item(windowMenu, wxID_ANY, L("Printer Settings Tab\tCtrl+4"), L("Show the printer settings"),
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 4, L("Printer Settings Tab\tCtrl+4"), L("Show the printer settings"),
|
||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png");
|
||||
if (m_plater) {
|
||||
windowMenu->AppendSeparator();
|
||||
wxMenuItem* item_3d = append_menu_item(windowMenu, wxID_ANY, L("3D\tCtrl+5"), L("Show the 3D editing view"),
|
||||
wxMenuItem* item_3d = append_menu_item(windowMenu, wxID_HIGHEST + 5, L("3D\tCtrl+5"), L("Show the 3D editing view"),
|
||||
[this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, "");
|
||||
wxMenuItem* item_preview = append_menu_item(windowMenu, wxID_ANY, L("Preview\tCtrl+6"), L("Show the 3D slices preview"),
|
||||
wxMenuItem* item_preview = append_menu_item(windowMenu, wxID_HIGHEST + 6, L("Preview\tCtrl+6"), L("Show the 3D slices preview"),
|
||||
[this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, "");
|
||||
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_3d->GetId());
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_preview->GetId());
|
||||
}
|
||||
|
||||
#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
|
||||
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");
|
||||
|
@ -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"),
|
||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png");
|
||||
#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
|
||||
|
|
|
@ -21,7 +21,9 @@ class ProgressStatusBar;
|
|||
|
||||
namespace GUI
|
||||
{
|
||||
|
||||
class Tab;
|
||||
class PrintHostQueueDialog;
|
||||
|
||||
enum QuickSlice
|
||||
{
|
||||
|
@ -52,6 +54,8 @@ class MainFrame : public wxFrame
|
|||
wxMenuItem* m_menu_item_repeat { nullptr };
|
||||
wxMenuItem* m_menu_item_reslice_now { nullptr };
|
||||
|
||||
PrintHostQueueDialog *m_printhost_queue_dlg;
|
||||
|
||||
std::string get_base_name(const wxString full_name) const ;
|
||||
std::string get_dir_name(const wxString full_name) const ;
|
||||
|
||||
|
@ -93,6 +97,8 @@ public:
|
|||
void select_tab(size_t tab) const;
|
||||
void select_view(const std::string& direction);
|
||||
|
||||
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }
|
||||
|
||||
Plater* m_plater { nullptr };
|
||||
wxNotebook* m_tabpanel { nullptr };
|
||||
wxProgressDialog* m_progress_dialog { nullptr };
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <wx/button.h>
|
||||
#include <wx/statbmp.h>
|
||||
#include <wx/scrolwin.h>
|
||||
#include <wx/clipbrd.h>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
@ -61,8 +62,11 @@ MsgDialog::~MsgDialog() {}
|
|||
|
||||
// ErrorDialog
|
||||
|
||||
ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) :
|
||||
MsgDialog(parent, _(L("Slic3r error")), _(L("Slic3r has encountered an error")), wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG))
|
||||
ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg)
|
||||
: MsgDialog(parent, _(L("Slic3r error")), _(L("Slic3r has encountered an error")),
|
||||
wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG),
|
||||
wxID_NONE)
|
||||
, msg(msg)
|
||||
{
|
||||
auto *panel = new wxScrolledWindow(this);
|
||||
auto *p_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
@ -77,6 +81,20 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) :
|
|||
|
||||
content_sizer->Add(panel, 1, wxEXPAND);
|
||||
|
||||
auto *btn_copy = new wxButton(this, wxID_ANY, _(L("Copy to clipboard")));
|
||||
btn_copy->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) {
|
||||
if (wxTheClipboard->Open()) {
|
||||
wxTheClipboard->SetData(new wxTextDataObject(this->msg)); // Note: the clipboard takes ownership of the pointer
|
||||
wxTheClipboard->Close();
|
||||
}
|
||||
});
|
||||
|
||||
auto *btn_ok = new wxButton(this, wxID_OK);
|
||||
btn_ok->SetFocus();
|
||||
|
||||
btn_sizer->Add(btn_copy, 0, wxRIGHT, HORIZ_SPACING);
|
||||
btn_sizer->Add(btn_ok);
|
||||
|
||||
SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT));
|
||||
Fit();
|
||||
}
|
||||
|
|
|
@ -50,14 +50,18 @@ protected:
|
|||
|
||||
|
||||
// Generic error dialog, used for displaying exceptions
|
||||
struct ErrorDialog : MsgDialog
|
||||
class ErrorDialog : public MsgDialog
|
||||
{
|
||||
public:
|
||||
ErrorDialog(wxWindow *parent, const wxString &msg);
|
||||
ErrorDialog(ErrorDialog &&) = delete;
|
||||
ErrorDialog(const ErrorDialog &) = delete;
|
||||
ErrorDialog &operator=(ErrorDialog &&) = delete;
|
||||
ErrorDialog &operator=(const ErrorDialog &) = delete;
|
||||
virtual ~ErrorDialog();
|
||||
|
||||
private:
|
||||
wxString msg;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -213,7 +213,7 @@ void SlicedInfo::SetTextAndShow(SlisedInfoIdx idx, const wxString& text, const w
|
|||
}
|
||||
|
||||
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),
|
||||
last_selected(wxNOT_FOUND)
|
||||
{
|
||||
|
@ -484,7 +484,7 @@ Sidebar::Sidebar(Plater *parent)
|
|||
: wxPanel(parent), p(new priv(parent))
|
||||
{
|
||||
p->scrolled = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(400, -1));
|
||||
p->scrolled->SetScrollbars(0, 1, 1, 1);
|
||||
p->scrolled->SetScrollbars(0, 20, 1, 2);
|
||||
|
||||
// Sizer in the scrolled area
|
||||
auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
@ -732,8 +732,7 @@ void Sidebar::show_info_sizer()
|
|||
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 sf = model_instance->get_scaling_factor();
|
||||
p->object_info->info_volume->SetLabel(wxString::Format("%.2f", size(0) * size(1) * size(2) * sf(0) * sf(1) * sf(2)));
|
||||
p->object_info->info_volume->SetLabel(wxString::Format("%.2f", size(0) * size(1) * size(2)));
|
||||
p->object_info->info_facets->SetLabel(wxString::Format(_(L("%d (%d shells)")), static_cast<int>(model_object->facets_count()), stats.number_of_parts));
|
||||
|
||||
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
|
||||
|
@ -913,7 +912,11 @@ struct Plater::priv
|
|||
Sidebar *sidebar;
|
||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
View3D* view3D;
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
GLToolbar view_toolbar;
|
||||
#else
|
||||
GLRadioToolbar view_toolbar;
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
#else
|
||||
#if !ENABLE_IMGUI
|
||||
wxPanel *panel3d;
|
||||
|
@ -1030,6 +1033,7 @@ private:
|
|||
bool can_decrease_instances() const;
|
||||
bool can_split_to_objects() const;
|
||||
bool can_split_to_volumes() const;
|
||||
bool can_split() const;
|
||||
bool layers_height_allowed() const;
|
||||
bool can_delete_all() const;
|
||||
bool can_arrange() const;
|
||||
|
@ -1068,6 +1072,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
#endif // !ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
, delayed_scene_refresh(false)
|
||||
, project_filename(wxEmptyString)
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
, view_toolbar(GLToolbar::Radio)
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
{
|
||||
arranging.store(false);
|
||||
rotoptimizing.store(false);
|
||||
|
@ -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_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [q](Event<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_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); });
|
||||
|
@ -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_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); });
|
||||
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_WIPETOWER_MOVED, &priv::on_wipetower_moved, this);
|
||||
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")
|
||||
set_current_panel(preview);
|
||||
|
||||
#if !ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
view_toolbar.set_selection(name);
|
||||
#endif // !ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
}
|
||||
#else
|
||||
void Plater::priv::select_view(const std::string& direction)
|
||||
|
@ -1485,7 +1496,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
|
|||
#if !ENABLE_MODELVOLUME_TRANSFORM
|
||||
const Vec3d bed_center = Slic3r::to_3d(bed_shape.center().cast<double>(), 0.0);
|
||||
#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 scaled_down = false;
|
||||
|
@ -1517,9 +1528,10 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
|
|||
if (max_ratio > 10000) {
|
||||
// the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
|
||||
// so scale down the mesh
|
||||
// const Vec3d inverse = ratio.cwiseInverse();
|
||||
// object->scale(inverse);
|
||||
object->scale(ratio.cwiseInverse());
|
||||
double inv = 1. / max_ratio;
|
||||
object->scale_mesh(Vec3d(inv, inv, inv));
|
||||
object->origin_translation = Vec3d::Zero();
|
||||
object->center_around_origin();
|
||||
scaled_down = true;
|
||||
} else if (max_ratio > 5) {
|
||||
const Vec3d inverse = ratio.cwiseInverse();
|
||||
|
@ -1644,8 +1656,8 @@ void Plater::priv::selection_changed()
|
|||
view3D->enable_toolbar_item("delete", can_delete_object());
|
||||
view3D->enable_toolbar_item("more", can_increase_instances());
|
||||
view3D->enable_toolbar_item("fewer", can_decrease_instances());
|
||||
view3D->enable_toolbar_item("splitobjects", can_split_to_objects());
|
||||
view3D->enable_toolbar_item("splitvolumes", can_split_to_volumes());
|
||||
view3D->enable_toolbar_item("splitobjects", can_split/*_to_objects*/());
|
||||
view3D->enable_toolbar_item("splitvolumes", can_split/*_to_volumes*/());
|
||||
view3D->enable_toolbar_item("layersediting", layers_height_allowed());
|
||||
// forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears)
|
||||
view3D->render();
|
||||
|
@ -1653,8 +1665,8 @@ void Plater::priv::selection_changed()
|
|||
this->canvas3D->enable_toolbar_item("delete", can_delete_object());
|
||||
this->canvas3D->enable_toolbar_item("more", can_increase_instances());
|
||||
this->canvas3D->enable_toolbar_item("fewer", can_decrease_instances());
|
||||
this->canvas3D->enable_toolbar_item("splitobjects", can_split_to_objects());
|
||||
this->canvas3D->enable_toolbar_item("splitvolumes", can_split_to_volumes());
|
||||
this->canvas3D->enable_toolbar_item("splitobjects", can_split/*_to_objects*/());
|
||||
this->canvas3D->enable_toolbar_item("splitvolumes", can_split/*_to_volumes*/());
|
||||
this->canvas3D->enable_toolbar_item("layersediting", layers_height_allowed());
|
||||
// forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears)
|
||||
this->canvas3D->render();
|
||||
|
@ -2446,8 +2458,8 @@ void Plater::priv::on_action_layersediting(SimpleEvent&)
|
|||
|
||||
void Plater::priv::on_object_select(SimpleEvent& evt)
|
||||
{
|
||||
selection_changed();
|
||||
wxGetApp().obj_list()->update_selections();
|
||||
selection_changed();
|
||||
}
|
||||
|
||||
void Plater::priv::on_viewport_changed(SimpleEvent& evt)
|
||||
|
@ -2597,9 +2609,9 @@ bool Plater::priv::complit_init_object_menu()
|
|||
// ui updates needs to be binded to the parent panel
|
||||
if (q != nullptr)
|
||||
{
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects() || can_split_to_volumes()); }, item_split->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects()); }, item_split_objects->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_volumes()); }, item_split_volumes->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects() || can_split_to_volumes*/()); }, item_split->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split_objects->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split_volumes->GetId());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -2618,7 +2630,7 @@ bool Plater::priv::complit_init_sla_object_menu()
|
|||
// ui updates needs to be binded to the parent panel
|
||||
if (q != nullptr)
|
||||
{
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects()); }, item_split->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split->GetId());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -2637,7 +2649,7 @@ bool Plater::priv::complit_init_part_menu()
|
|||
// ui updates needs to be binded to the parent panel
|
||||
if (q != nullptr)
|
||||
{
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_volumes()); }, item_split->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split->GetId());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -2646,9 +2658,58 @@ bool Plater::priv::complit_init_part_menu()
|
|||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
void Plater::priv::init_view_toolbar()
|
||||
{
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
ItemsIconsTexture::Metadata icons_data;
|
||||
icons_data.filename = "view_toolbar.png";
|
||||
icons_data.icon_size = 64;
|
||||
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))
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
return;
|
||||
|
||||
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
view_toolbar.set_layout_orientation(GLToolbar::Layout::Bottom);
|
||||
view_toolbar.set_border(5.0f);
|
||||
view_toolbar.set_gap_size(1.0f);
|
||||
|
||||
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;
|
||||
|
||||
item.name = "3D";
|
||||
|
@ -2669,6 +2730,7 @@ void Plater::priv::init_view_toolbar()
|
|||
preview->set_view_toolbar(&view_toolbar);
|
||||
|
||||
view_toolbar.set_selection("3D");
|
||||
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||
}
|
||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
|
||||
|
@ -2705,6 +2767,13 @@ bool Plater::priv::can_split_to_volumes() const
|
|||
return sidebar->obj_list()->is_splittable();
|
||||
}
|
||||
|
||||
bool Plater::priv::can_split() const
|
||||
{
|
||||
if (printer_technology == ptSLA)
|
||||
return false;
|
||||
return sidebar->obj_list()->is_splittable();
|
||||
}
|
||||
|
||||
bool Plater::priv::layers_height_allowed() const
|
||||
{
|
||||
int obj_idx = get_selected_object_idx();
|
||||
|
@ -3086,7 +3155,7 @@ void Plater::send_gcode()
|
|||
}
|
||||
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
|
||||
|
||||
Slic3r::PrintHostSendDialog dlg(default_output_file);
|
||||
PrintHostSendDialog dlg(default_output_file);
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
upload_job.upload_data.upload_path = dlg.filename();
|
||||
upload_job.upload_data.start_print = dlg.start_print();
|
||||
|
@ -3178,6 +3247,8 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
|||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||
if (p->preview) p->preview->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values);
|
||||
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",
|
||||
"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",
|
||||
"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",
|
||||
"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",
|
||||
|
@ -455,6 +456,7 @@ const std::vector<std::string>& Preset::sla_printer_options()
|
|||
"display_width", "display_height", "display_pixels_x", "display_pixels_y",
|
||||
"display_orientation",
|
||||
"printer_correction",
|
||||
"print_host", "printhost_apikey", "printhost_cafile",
|
||||
"printer_notes",
|
||||
"inherits"
|
||||
};
|
||||
|
|
|
@ -1,20 +1,28 @@
|
|||
#include "PrintHostDialogs.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <wx/frame.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/progdlg.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/textctrl.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 "slic3r/GUI/MsgDialog.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "../Utils/PrintHost.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
||||
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path)
|
||||
: MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE)
|
||||
|
@ -45,5 +53,95 @@ fs::path PrintHostSendDialog::filename() 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_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <wx/frame.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/progdlg.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/dialog.h>
|
||||
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/MsgDialog.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "../Utils/PrintHost.hpp"
|
||||
|
||||
class wxTextCtrl;
|
||||
class wxCheckBox;
|
||||
class wxDataViewListCtrl;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct PrintHostJob;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
|
||||
class PrintHostSendDialog : public GUI::MsgDialog
|
||||
{
|
||||
|
@ -38,12 +41,38 @@ private:
|
|||
class PrintHostQueueDialog : public wxDialog
|
||||
{
|
||||
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
|
||||
|
|
|
@ -270,7 +270,7 @@ Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::str
|
|||
auto panel = this;
|
||||
#endif
|
||||
PageShp page(new Page(panel, title, icon_idx));
|
||||
page->SetScrollbars(1, 1, 1, 2);
|
||||
page->SetScrollbars(1, 20, 1, 2);
|
||||
page->Hide();
|
||||
m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5);
|
||||
|
||||
|
@ -1538,6 +1538,108 @@ bool Tab::current_preset_is_dirty()
|
|||
return m_presets->current_is_dirty();
|
||||
}
|
||||
|
||||
void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
|
||||
{
|
||||
const bool sla = m_presets->get_selected_preset().printer_technology() == ptSLA;
|
||||
|
||||
// Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment)
|
||||
if (! sla) {
|
||||
optgroup->append_single_option_line("host_type");
|
||||
} else {
|
||||
m_config->option<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()
|
||||
{
|
||||
m_presets = &m_preset_bundle->printers;
|
||||
|
@ -1665,96 +1767,8 @@ void TabPrinter::build_fff()
|
|||
}
|
||||
#endif
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Printer Host upload")));
|
||||
|
||||
optgroup->append_single_option_line("host_type");
|
||||
|
||||
auto printhost_browse = [this, optgroup] (wxWindow* parent) {
|
||||
auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG));
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn);
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) {
|
||||
BonjourDialog dialog(parent);
|
||||
if (dialog.show_and_lookup()) {
|
||||
optgroup->set_value("print_host", std::move(dialog.get_selected()), true);
|
||||
}
|
||||
});
|
||||
|
||||
return sizer;
|
||||
};
|
||||
|
||||
auto print_host_test = [this](wxWindow* parent) {
|
||||
auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
|
||||
wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
|
||||
btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG));
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn);
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {
|
||||
std::unique_ptr<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("Print Host upload")));
|
||||
build_printhost(optgroup.get());
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Firmware")));
|
||||
optgroup->append_single_option_line("gcode_flavor");
|
||||
|
@ -1897,6 +1911,9 @@ void TabPrinter::build_sla()
|
|||
}
|
||||
optgroup->append_line(line);
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Print Host upload")));
|
||||
build_printhost(optgroup.get());
|
||||
|
||||
page = add_options_page(_(L("Notes")), "note.png");
|
||||
optgroup = page->new_optgroup(_(L("Notes")), 0);
|
||||
option = optgroup->get_option("printer_notes");
|
||||
|
@ -2041,6 +2058,7 @@ void TabPrinter::build_extruder_pages()
|
|||
optgroup->append_single_option_line("cooling_tube_length");
|
||||
optgroup->append_single_option_line("parking_pos_retraction");
|
||||
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_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_sla;
|
||||
|
||||
void build_printhost(ConfigOptionsGroup *optgroup);
|
||||
public:
|
||||
wxButton* m_serial_test_btn = nullptr;
|
||||
wxButton* m_print_host_test_btn = nullptr;
|
||||
|
|
|
@ -953,6 +953,44 @@ void PrusaObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType&
|
|||
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
|
||||
{
|
||||
PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
|
||||
|
@ -1240,6 +1278,16 @@ IMPLEMENT_VARIANT_OBJECT(PrusaDataViewBitmapText)
|
|||
// 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)
|
||||
{
|
||||
m_value << value;
|
||||
|
@ -1251,6 +1299,13 @@ bool PrusaBitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const
|
|||
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)
|
||||
{
|
||||
int xoffset = 0;
|
||||
|
@ -1291,12 +1346,12 @@ wxWindow* PrusaBitmapTextRenderer::CreateEditorCtrl(wxWindow* parent, wxRect lab
|
|||
|
||||
PrusaDataViewBitmapText data;
|
||||
data << value;
|
||||
m_bmp_from_editing_item = data.GetBitmap();
|
||||
|
||||
m_was_unusable_symbol = false;
|
||||
|
||||
wxPoint position = labelRect.GetPosition();
|
||||
if (m_bmp_from_editing_item.IsOk()) {
|
||||
const int bmp_width = m_bmp_from_editing_item.GetWidth();
|
||||
if (data.GetBitmap().IsOk()) {
|
||||
const int bmp_width = data.GetBitmap().GetWidth();
|
||||
position.x += 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(),
|
||||
position, labelRect.GetSize(), wxTE_PROCESS_ENTER);
|
||||
text_editor->SetInsertionPointEnd();
|
||||
text_editor->SelectAll();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -463,6 +463,7 @@ public:
|
|||
int GetVolumeIdByItem(const wxDataViewItem& item) const;
|
||||
int GetInstanceIdByItem(const wxDataViewItem& item) const;
|
||||
void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx);
|
||||
int GetRowByItem(const wxDataViewItem& item) const;
|
||||
bool IsEmpty() { return m_objects.empty(); }
|
||||
|
||||
// helper method for wxLog
|
||||
|
@ -518,21 +519,44 @@ public:
|
|||
// ----------------------------------------------------------------------------
|
||||
// PrusaBitmapTextRenderer
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||
class PrusaBitmapTextRenderer : public wxDataViewRenderer
|
||||
#else
|
||||
class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer
|
||||
#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||
{
|
||||
public:
|
||||
PrusaBitmapTextRenderer( wxDataViewCellMode mode = wxDATAVIEW_CELL_EDITABLE,
|
||||
int align = wxDVR_DEFAULT_ALIGNMENT):
|
||||
wxDataViewCustomRenderer(wxT("PrusaDataViewBitmapText"), mode, align) {}
|
||||
PrusaBitmapTextRenderer(wxDataViewCellMode mode =
|
||||
#ifdef __WXOSX__
|
||||
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 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 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,
|
||||
wxRect labelRect,
|
||||
const wxVariant& value) override;
|
||||
|
@ -542,8 +566,7 @@ public:
|
|||
|
||||
private:
|
||||
PrusaDataViewBitmapText m_value;
|
||||
wxBitmap m_bmp_from_editing_item;
|
||||
bool m_was_unusable_symbol;
|
||||
bool m_was_unusable_symbol {false};
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/MsgDialog.hpp"
|
||||
#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX
|
||||
#include "Http.hpp"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool Duet::send_gcode(const std::string &filename) const
|
||||
{
|
||||
enum { PROGRESS_RANGE = 1000 };
|
||||
|
||||
const auto errortitle = _(L("Error while uploading to the Duet"));
|
||||
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("Duet upload")),
|
||||
_(L("Sending G-code file to Duet...")),
|
||||
PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
|
||||
progress_dialog.Pulse();
|
||||
|
||||
wxString connect_msg;
|
||||
if (!connect(connect_msg)) {
|
||||
auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg);
|
||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = true;
|
||||
|
||||
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%")
|
||||
% filepath.string()
|
||||
% upload_filename.string()
|
||||
% upload_parent_path.string()
|
||||
% print
|
||||
% upload_cmd;
|
||||
|
||||
auto http = Http::post(std::move(upload_cmd));
|
||||
http.set_post_body(filename)
|
||||
.on_complete([&](std::string body, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body;
|
||||
progress_dialog.Update(PROGRESS_RANGE);
|
||||
|
||||
int err_code = get_err_code_from_body(body);
|
||||
if (err_code != 0) {
|
||||
auto msg = format_error(body, L("Unknown error occured"), 0);
|
||||
GUI::show_error(&progress_dialog, std::move(msg));
|
||||
res = false;
|
||||
} else if (print) {
|
||||
wxString errormsg;
|
||||
res = start_print(errormsg, upload_filepath.string());
|
||||
if (!res) {
|
||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||
}
|
||||
}
|
||||
})
|
||||
.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;
|
||||
auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status));
|
||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||
res = false;
|
||||
})
|
||||
.on_progress([&](Http::Progress progress, bool &cancel) {
|
||||
if (cancel) {
|
||||
// Upload was canceled
|
||||
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();
|
||||
|
||||
disconnect();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Duet::upload(PrintHostUpload upload_data) const
|
||||
// bool Duet::send_gcode(const std::string &filename) const
|
||||
// {
|
||||
// enum { PROGRESS_RANGE = 1000 };
|
||||
|
||||
// const auto errortitle = _(L("Error while uploading to the Duet"));
|
||||
// fs::path filepath(filename);
|
||||
|
||||
// GUI::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("Duet upload")),
|
||||
// _(L("Sending G-code file to Duet...")),
|
||||
// PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
|
||||
// progress_dialog.Pulse();
|
||||
|
||||
// wxString connect_msg;
|
||||
// if (!connect(connect_msg)) {
|
||||
// auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg);
|
||||
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// bool res = true;
|
||||
|
||||
// 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%")
|
||||
// % filepath.string()
|
||||
// % upload_filename.string()
|
||||
// % upload_parent_path.string()
|
||||
// % print
|
||||
// % upload_cmd;
|
||||
|
||||
// auto http = Http::post(std::move(upload_cmd));
|
||||
// http.set_post_body(filename)
|
||||
// .on_complete([&](std::string body, unsigned status) {
|
||||
// BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body;
|
||||
// progress_dialog.Update(PROGRESS_RANGE);
|
||||
|
||||
// int err_code = get_err_code_from_body(body);
|
||||
// if (err_code != 0) {
|
||||
// auto msg = format_error(body, L("Unknown error occured"), 0);
|
||||
// GUI::show_error(&progress_dialog, std::move(msg));
|
||||
// res = false;
|
||||
// } else if (print) {
|
||||
// wxString errormsg;
|
||||
// res = start_print(errormsg, upload_filepath.string());
|
||||
// if (!res) {
|
||||
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .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;
|
||||
// auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status));
|
||||
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||
// res = false;
|
||||
// })
|
||||
// .on_progress([&](Http::Progress progress, bool &cancel) {
|
||||
// if (cancel) {
|
||||
// // Upload was canceled
|
||||
// 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();
|
||||
|
||||
// disconnect();
|
||||
|
||||
// return res;
|
||||
// }
|
||||
|
||||
bool Duet::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const
|
||||
{
|
||||
// XXX: TODO
|
||||
throw "unimplemented";
|
||||
}
|
||||
|
||||
|
|
|
@ -22,11 +22,10 @@ public:
|
|||
bool test(wxString &curl_msg) const;
|
||||
wxString get_test_ok_msg () const;
|
||||
wxString get_test_failed_msg (wxString &msg) const;
|
||||
// Send gcode file to duet, filename is expected to be in UTF-8
|
||||
bool send_gcode(const std::string &filename) const;
|
||||
bool upload(PrintHostUpload upload_data) const;
|
||||
bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const;
|
||||
bool has_auto_discovery() const;
|
||||
bool can_test() const;
|
||||
virtual std::string get_host() const { return host; }
|
||||
private:
|
||||
std::string host;
|
||||
std::string password;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <curl/curl.h>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
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
|
||||
std::deque<fs::ifstream> form_files;
|
||||
std::string postfields;
|
||||
std::string error_buffer; // Used for CURLOPT_ERRORBUFFER
|
||||
size_t limit;
|
||||
bool cancel;
|
||||
|
||||
|
@ -69,13 +71,14 @@ struct Http::priv
|
|||
void http_perform();
|
||||
};
|
||||
|
||||
Http::priv::priv(const std::string &url) :
|
||||
curl(::curl_easy_init()),
|
||||
form(nullptr),
|
||||
form_end(nullptr),
|
||||
headerlist(nullptr),
|
||||
limit(0),
|
||||
cancel(false)
|
||||
Http::priv::priv(const std::string &url)
|
||||
: curl(::curl_easy_init())
|
||||
, form(nullptr)
|
||||
, form_end(nullptr)
|
||||
, headerlist(nullptr)
|
||||
, error_buffer(CURL_ERROR_SIZE + 1, '\0')
|
||||
, limit(0)
|
||||
, cancel(false)
|
||||
{
|
||||
if (curl == nullptr) {
|
||||
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_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION);
|
||||
::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer.front());
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return (boost::format("%1% (%2%)")
|
||||
return (boost::format("%1% (%2%): %3%")
|
||||
% ::curl_easy_strerror(curlcode)
|
||||
% curlcode
|
||||
% error_buffer
|
||||
).str();
|
||||
}
|
||||
|
||||
|
@ -227,9 +232,7 @@ void Http::priv::http_perform()
|
|||
::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, static_cast<void*>(this));
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
||||
#endif
|
||||
::curl_easy_setopt(curl, CURLOPT_VERBOSE, get_logging_level() >= 4);
|
||||
|
||||
if (headerlist != nullptr) {
|
||||
::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <wx/progdlg.h>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX
|
||||
#include "Http.hpp"
|
||||
|
||||
|
||||
|
@ -42,6 +43,9 @@ bool OctoPrint::test(wxString &msg) const
|
|||
})
|
||||
.on_complete([&](std::string body, unsigned) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: Got version: %1%") % body;
|
||||
|
||||
// TODO: parse body, call validate_version_text
|
||||
|
||||
})
|
||||
.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.")));
|
||||
}
|
||||
|
||||
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 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();
|
||||
const auto upload_filename = upload_data.upload_path.filename();
|
||||
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||
|
||||
wxString test_msg;
|
||||
if (! test(test_msg)) {
|
||||
auto errormsg = wxString::Format("%s: %s", errortitle, test_msg);
|
||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||
return false;
|
||||
|
||||
// TODO:
|
||||
|
||||
// auto errormsg = wxString::Format("%s: %s", errortitle, test_msg);
|
||||
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||
// return false;
|
||||
}
|
||||
|
||||
bool res = true;
|
||||
|
@ -92,36 +83,31 @@ bool OctoPrint::send_gcode(const std::string &filename) const
|
|||
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%")
|
||||
% filepath.string()
|
||||
% upload_data.source_path.string()
|
||||
% url
|
||||
% upload_filename.string()
|
||||
% upload_parent_path.string()
|
||||
% print;
|
||||
% upload_data.start_print;
|
||||
|
||||
auto http = Http::post(std::move(url));
|
||||
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_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) {
|
||||
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) {
|
||||
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));
|
||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||
error_fn(std::move(body), std::move(error), status);
|
||||
res = false;
|
||||
})
|
||||
.on_progress([&](Http::Progress progress, bool &cancel) {
|
||||
prorgess_fn(std::move(progress), cancel);
|
||||
if (cancel) {
|
||||
// Upload was canceled
|
||||
BOOST_LOG_TRIVIAL(error) << "Octoprint: Upload canceled";
|
||||
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();
|
||||
|
@ -129,11 +115,6 @@ bool OctoPrint::send_gcode(const std::string &filename) const
|
|||
return res;
|
||||
}
|
||||
|
||||
bool OctoPrint::upload(PrintHostUpload upload_data) const
|
||||
{
|
||||
throw "unimplemented";
|
||||
}
|
||||
|
||||
bool OctoPrint::has_auto_discovery() const
|
||||
{
|
||||
return true;
|
||||
|
@ -144,6 +125,12 @@ bool OctoPrint::can_test() const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool OctoPrint::validate_version_text(const std::string &version_text)
|
||||
{
|
||||
// FIXME
|
||||
return true;
|
||||
}
|
||||
|
||||
void OctoPrint::set_auth(Http &http) const
|
||||
{
|
||||
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;
|
||||
wxString get_test_ok_msg () const;
|
||||
wxString get_test_failed_msg (wxString &msg) const;
|
||||
// Send gcode file to octoprint, filename is expected to be in UTF-8
|
||||
bool send_gcode(const std::string &filename) const;
|
||||
bool upload(PrintHostUpload upload_data) const;
|
||||
bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const;
|
||||
bool has_auto_discovery() 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:
|
||||
std::string host;
|
||||
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
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
#include "OctoPrint.hpp"
|
||||
#include "Duet.hpp"
|
||||
#include "PrintHost.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <wx/app.h>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/Channel.hpp"
|
||||
#include "OctoPrint.hpp"
|
||||
#include "Duet.hpp"
|
||||
#include "../GUI/PrintHostDialogs.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
using boost::optional;
|
||||
|
||||
using Slic3r::GUI::PrintHostQueueDialog;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -18,42 +25,152 @@ PrintHost::~PrintHost() {}
|
|||
|
||||
PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
|
||||
{
|
||||
PrintHostType kind = config->option<ConfigOptionEnum<PrintHostType>>("host_type")->value;
|
||||
if (kind == htOctoPrint) {
|
||||
return new OctoPrint(config);
|
||||
} else if (kind == htDuet) {
|
||||
return new Duet(config);
|
||||
const auto opt = config->option<ConfigOptionEnum<PrintHostType>>("host_type");
|
||||
if (opt == nullptr) { return nullptr; }
|
||||
|
||||
switch (opt->value) {
|
||||
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
|
||||
{
|
||||
std::vector<PrintHostJob> jobs;
|
||||
Channel<unsigned> channel;
|
||||
// XXX: comment on how bg thread works
|
||||
|
||||
PrintHostJobQueue *q;
|
||||
|
||||
Channel<PrintHostJob> channel_jobs;
|
||||
Channel<size_t> channel_cancels;
|
||||
size_t job_id = 0;
|
||||
int prev_progress = -1;
|
||||
|
||||
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()
|
||||
: p(new priv())
|
||||
PrintHostJobQueue::PrintHostJobQueue(PrintHostQueueDialog *queue_dialog)
|
||||
: p(new priv(this))
|
||||
{
|
||||
std::shared_ptr<priv> p2 = p;
|
||||
p->bg_thread = std::thread([p2]() {
|
||||
// Wait for commands on the channel:
|
||||
auto cmd = p2->channel.pop();
|
||||
// TODO
|
||||
});
|
||||
p->queue_dialog = queue_dialog;
|
||||
}
|
||||
|
||||
PrintHostJobQueue::~PrintHostJobQueue()
|
||||
{
|
||||
// TODO: stop the thread
|
||||
// if (p && p->bg_thread.joinable()) {
|
||||
// p->bg_thread.detach();
|
||||
// }
|
||||
if (p && p->bg_thread.joinable()) {
|
||||
p->bg_exit = true;
|
||||
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 "Http.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -29,11 +31,10 @@ public:
|
|||
virtual bool test(wxString &curl_msg) const = 0;
|
||||
virtual wxString get_test_ok_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 send_gcode(const std::string &filename) const = 0; // XXX: remove in favor of upload()
|
||||
virtual bool upload(PrintHostUpload upload_data) const = 0;
|
||||
virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const = 0;
|
||||
virtual bool has_auto_discovery() const = 0;
|
||||
virtual bool can_test() const = 0;
|
||||
virtual std::string get_host() const = 0;
|
||||
|
||||
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
||||
};
|
||||
|
@ -43,6 +44,7 @@ struct PrintHostJob
|
|||
{
|
||||
PrintHostUpload upload_data;
|
||||
std::unique_ptr<PrintHost> printhost;
|
||||
bool cancelled = false;
|
||||
|
||||
PrintHostJob() {}
|
||||
PrintHostJob(const PrintHostJob&) = delete;
|
||||
|
@ -68,10 +70,12 @@ struct PrintHostJob
|
|||
};
|
||||
|
||||
|
||||
namespace GUI { class PrintHostQueueDialog; }
|
||||
|
||||
class PrintHostJobQueue
|
||||
{
|
||||
public:
|
||||
PrintHostJobQueue();
|
||||
PrintHostJobQueue(GUI::PrintHostQueueDialog *queue_dialog);
|
||||
PrintHostJobQueue(const PrintHostJobQueue &) = delete;
|
||||
PrintHostJobQueue(PrintHostJobQueue &&other) = delete;
|
||||
~PrintHostJobQueue();
|
||||
|
@ -79,6 +83,9 @@ public:
|
|||
PrintHostJobQueue& operator=(const PrintHostJobQueue &) = delete;
|
||||
PrintHostJobQueue& operator=(PrintHostJobQueue &&other) = delete;
|
||||
|
||||
void enqueue(PrintHostJob job);
|
||||
void cancel(size_t id);
|
||||
|
||||
private:
|
||||
struct priv;
|
||||
std::shared_ptr<priv> p;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue