Merge branch 'master' of https://github.com/Prusa3d/Slic3r
This commit is contained in:
commit
9463351abd
32 changed files with 3358 additions and 342 deletions
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
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef slic3r_Channel_hpp_
|
#ifndef slic3r_Channel_hpp_
|
||||||
#define slic3r_Channel_hpp_
|
#define slic3r_Channel_hpp_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
@ -13,32 +14,26 @@ namespace Slic3r {
|
||||||
|
|
||||||
template<class T> class Channel
|
template<class T> class Channel
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
using UniqueLock = std::unique_lock<std::mutex>;
|
|
||||||
using Queue = std::deque<T>;
|
|
||||||
public:
|
public:
|
||||||
class Guard
|
using UniqueLock = std::unique_lock<std::mutex>;
|
||||||
|
|
||||||
|
template<class Ptr> class Unlocker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Guard(UniqueLock lock, const Queue &queue) : m_lock(std::move(lock)), m_queue(queue) {}
|
Unlocker(UniqueLock lock) : m_lock(std::move(lock)) {}
|
||||||
Guard(const Guard &other) = delete;
|
Unlocker(const Unlocker &other) noexcept : m_lock(std::move(other.m_lock)) {} // XXX: done beacuse of MSVC 2013 not supporting init of deleter by move
|
||||||
Guard(Guard &&other) = delete;
|
Unlocker(Unlocker &&other) noexcept : m_lock(std::move(other.m_lock)) {}
|
||||||
~Guard() {}
|
Unlocker& operator=(const Unlocker &other) = delete;
|
||||||
|
Unlocker& operator=(Unlocker &&other) { m_lock = std::move(other.m_lock); }
|
||||||
|
|
||||||
// Access trampolines
|
void operator()(Ptr*) { m_lock.unlock(); }
|
||||||
size_t size() const noexcept { return m_queue.size(); }
|
|
||||||
bool empty() const noexcept { return m_queue.empty(); }
|
|
||||||
typename Queue::const_iterator begin() const noexcept { return m_queue.begin(); }
|
|
||||||
typename Queue::const_iterator end() const noexcept { return m_queue.end(); }
|
|
||||||
typename Queue::const_reference operator[](size_t i) const { return m_queue[i]; }
|
|
||||||
|
|
||||||
Guard& operator=(const Guard &other) = delete;
|
|
||||||
Guard& operator=(Guard &&other) = delete;
|
|
||||||
private:
|
private:
|
||||||
UniqueLock m_lock;
|
mutable UniqueLock m_lock; // XXX: mutable: see above
|
||||||
const Queue &m_queue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using Queue = std::deque<T>;
|
||||||
|
using LockedConstPtr = std::unique_ptr<const Queue, Unlocker<const Queue>>;
|
||||||
|
using LockedPtr = std::unique_ptr<Queue, Unlocker<Queue>>;
|
||||||
|
|
||||||
Channel() {}
|
Channel() {}
|
||||||
~Channel() {}
|
~Channel() {}
|
||||||
|
@ -56,7 +51,7 @@ public:
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
UniqueLock lock(m_mutex);
|
UniqueLock lock(m_mutex);
|
||||||
m_queue.push_back(std::forward(item));
|
m_queue.push_back(std::forward<T>(item));
|
||||||
}
|
}
|
||||||
if (! silent) { m_condition.notify_one(); }
|
if (! silent) { m_condition.notify_one(); }
|
||||||
}
|
}
|
||||||
|
@ -82,19 +77,22 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlocked observers
|
// Unlocked observers/hints
|
||||||
// Thread unsafe! Keep in mind you need to re-verify the result after acquiring lock!
|
// Thread unsafe! Keep in mind you need to re-verify the result after locking!
|
||||||
size_t size() const noexcept { return m_queue.size(); }
|
size_t size_hint() const noexcept { return m_queue.size(); }
|
||||||
bool empty() const noexcept { return m_queue.empty(); }
|
|
||||||
|
|
||||||
Guard read() const
|
LockedConstPtr lock_read() const
|
||||||
{
|
{
|
||||||
return Guard(UniqueLock(m_mutex), m_queue);
|
return LockedConstPtr(&m_queue, Unlocker<const Queue>(UniqueLock(m_mutex)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LockedPtr lock_rw()
|
||||||
|
{
|
||||||
|
return LockedPtr(&m_queue, Unlocker<Queue>(UniqueLock(m_mutex)));
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
Queue m_queue;
|
Queue m_queue;
|
||||||
std::mutex m_mutex;
|
mutable std::mutex m_mutex;
|
||||||
std::condition_variable m_condition;
|
std::condition_variable m_condition;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,6 @@ static void extract_model_from_archive(
|
||||||
const char *model_xml = strstr(scene_xml_data.data(), model_name_tag);
|
const char *model_xml = strstr(scene_xml_data.data(), model_name_tag);
|
||||||
const char *zero_tag = "<zero>";
|
const char *zero_tag = "<zero>";
|
||||||
const char *zero_xml = strstr(scene_xml_data.data(), zero_tag);
|
const char *zero_xml = strstr(scene_xml_data.data(), zero_tag);
|
||||||
float trafo[3][4] = { 0 };
|
|
||||||
Vec3d instance_rotation = Vec3d::Zero();
|
Vec3d instance_rotation = Vec3d::Zero();
|
||||||
Vec3d instance_scaling_factor = Vec3d::Ones();
|
Vec3d instance_scaling_factor = Vec3d::Ones();
|
||||||
Vec3d instance_offset = Vec3d::Zero();
|
Vec3d instance_offset = Vec3d::Zero();
|
||||||
|
@ -124,19 +123,7 @@ static void extract_model_from_archive(
|
||||||
"[%f, %f, %f]", zero, zero+1, zero+2) == 3) {
|
"[%f, %f, %f]", zero, zero+1, zero+2) == 3) {
|
||||||
instance_scaling_factor = Vec3d((double)scale[0], (double)scale[1], (double)scale[2]);
|
instance_scaling_factor = Vec3d((double)scale[0], (double)scale[1], (double)scale[2]);
|
||||||
instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]);
|
instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]);
|
||||||
Eigen::Matrix3f mat_rot, mat_scale, mat_trafo;
|
|
||||||
mat_rot = Eigen::AngleAxisf(-rotation[2], Eigen::Vector3f::UnitZ()) *
|
|
||||||
Eigen::AngleAxisf(-rotation[1], Eigen::Vector3f::UnitY()) *
|
|
||||||
Eigen::AngleAxisf(-rotation[0], Eigen::Vector3f::UnitX());
|
|
||||||
mat_scale = Eigen::Scaling(scale[0], scale[1], scale[2]);
|
|
||||||
mat_trafo = mat_rot * mat_scale;
|
|
||||||
for (size_t r = 0; r < 3; ++ r) {
|
|
||||||
for (size_t c = 0; c < 3; ++ c)
|
|
||||||
trafo[r][c] += mat_trafo(r, c);
|
|
||||||
}
|
|
||||||
instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2]));
|
instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2]));
|
||||||
// CHECK_ME -> Is the following correct ?
|
|
||||||
trafo[2][3] = position[2] / (float)instance_scaling_factor(2);
|
|
||||||
trafo_set = true;
|
trafo_set = true;
|
||||||
}
|
}
|
||||||
const char *group_tag = "<group>";
|
const char *group_tag = "<group>";
|
||||||
|
@ -189,8 +176,6 @@ static void extract_model_from_archive(
|
||||||
// All the faces have been read.
|
// All the faces have been read.
|
||||||
stl_get_size(&stl);
|
stl_get_size(&stl);
|
||||||
mesh.repair();
|
mesh.repair();
|
||||||
// Transform the model.
|
|
||||||
stl_transform(&stl, &trafo[0][0]);
|
|
||||||
if (std::abs(stl.stats.min(2)) < EPSILON)
|
if (std::abs(stl.stats.min(2)) < EPSILON)
|
||||||
stl.stats.min(2) = 0.;
|
stl.stats.min(2) = 0.;
|
||||||
// Add a mesh to a model.
|
// Add a mesh to a model.
|
||||||
|
@ -274,8 +259,6 @@ static void extract_model_from_archive(
|
||||||
memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50);
|
memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50);
|
||||||
stl_get_size(&stl);
|
stl_get_size(&stl);
|
||||||
mesh.repair();
|
mesh.repair();
|
||||||
// Transform the model.
|
|
||||||
stl_transform(&stl, &trafo[0][0]);
|
|
||||||
// Add a mesh to a model.
|
// Add a mesh to a model.
|
||||||
if (mesh.facets_count() > 0)
|
if (mesh.facets_count() > 0)
|
||||||
mesh_valid = true;
|
mesh_valid = true;
|
||||||
|
|
|
@ -612,7 +612,9 @@ double ray_mesh_intersect(const Vec3d& s,
|
||||||
const Vec3d& dir,
|
const Vec3d& dir,
|
||||||
const EigenMesh3D& m);
|
const EigenMesh3D& m);
|
||||||
|
|
||||||
PointSet normals(const PointSet& points, const EigenMesh3D& mesh);
|
PointSet normals(const PointSet& points, const EigenMesh3D& mesh,
|
||||||
|
double eps = 0.05, // min distance from edges
|
||||||
|
std::function<void()> throw_on_cancel = [](){});
|
||||||
|
|
||||||
inline Vec2d to_vec2(const Vec3d& v3) {
|
inline Vec2d to_vec2(const Vec3d& v3) {
|
||||||
return {v3(X), v3(Y)};
|
return {v3(X), v3(Y)};
|
||||||
|
@ -1049,7 +1051,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
tifcl();
|
tifcl();
|
||||||
|
|
||||||
// calculate the normals to the triangles belonging to filtered points
|
// calculate the normals to the triangles belonging to filtered points
|
||||||
auto nmls = sla::normals(filt_pts, mesh);
|
auto nmls = sla::normals(filt_pts, mesh, cfg.head_front_radius_mm, tifcl);
|
||||||
|
|
||||||
head_norm.resize(count, 3);
|
head_norm.resize(count, 3);
|
||||||
head_pos.resize(count, 3);
|
head_pos.resize(count, 3);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <cmath>
|
||||||
#include "SLA/SLASupportTree.hpp"
|
#include "SLA/SLASupportTree.hpp"
|
||||||
#include "SLA/SLABoilerPlate.hpp"
|
#include "SLA/SLABoilerPlate.hpp"
|
||||||
#include "SLA/SLASpatIndex.hpp"
|
#include "SLA/SLASpatIndex.hpp"
|
||||||
|
@ -9,15 +10,8 @@
|
||||||
#include "boost/geometry/index/rtree.hpp"
|
#include "boost/geometry/index/rtree.hpp"
|
||||||
|
|
||||||
#include <igl/ray_mesh_intersect.h>
|
#include <igl/ray_mesh_intersect.h>
|
||||||
|
|
||||||
//#if !defined(_MSC_VER) || defined(_WIN64)
|
|
||||||
#if 1
|
|
||||||
#define IGL_COMPATIBLE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef IGL_COMPATIBLE
|
|
||||||
#include <igl/point_mesh_squared_distance.h>
|
#include <igl/point_mesh_squared_distance.h>
|
||||||
#endif
|
#include <igl/remove_duplicate_vertices.h>
|
||||||
|
|
||||||
#include "SLASpatIndex.hpp"
|
#include "SLASpatIndex.hpp"
|
||||||
#include "ClipperUtils.hpp"
|
#include "ClipperUtils.hpp"
|
||||||
|
@ -84,33 +78,124 @@ size_t SpatIndex::size() const
|
||||||
return m_impl->m_store.size();
|
return m_impl->m_store.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
PointSet normals(const PointSet& points, const EigenMesh3D& mesh) {
|
bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2,
|
||||||
if(points.rows() == 0 || mesh.V.rows() == 0 || mesh.F.rows() == 0) return {};
|
double eps = 0.05)
|
||||||
#ifdef IGL_COMPATIBLE
|
{
|
||||||
|
using Line3D = Eigen::ParametrizedLine<double, 3>;
|
||||||
|
|
||||||
|
auto line = Line3D::Through(e1, e2);
|
||||||
|
double d = line.distance(p);
|
||||||
|
return std::abs(d) < eps;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Vec> double distance(const Vec& pp1, const Vec& pp2) {
|
||||||
|
auto p = pp2 - pp1;
|
||||||
|
return std::sqrt(p.transpose() * p);
|
||||||
|
}
|
||||||
|
|
||||||
|
PointSet normals(const PointSet& points, const EigenMesh3D& emesh,
|
||||||
|
double eps,
|
||||||
|
std::function<void()> throw_on_cancel) {
|
||||||
|
if(points.rows() == 0 || emesh.V.rows() == 0 || emesh.F.rows() == 0)
|
||||||
|
return {};
|
||||||
|
|
||||||
Eigen::VectorXd dists;
|
Eigen::VectorXd dists;
|
||||||
Eigen::VectorXi I;
|
Eigen::VectorXi I;
|
||||||
PointSet C;
|
PointSet C;
|
||||||
|
|
||||||
|
// We need to remove duplicate vertices and have a true index triangle
|
||||||
|
// structure
|
||||||
|
EigenMesh3D mesh;
|
||||||
|
Eigen::VectorXi SVI, SVJ;
|
||||||
|
igl::remove_duplicate_vertices(emesh.V, emesh.F, 1e-6,
|
||||||
|
mesh.V, SVI, SVJ, mesh.F);
|
||||||
|
|
||||||
igl::point_mesh_squared_distance( points, mesh.V, mesh.F, dists, I, C);
|
igl::point_mesh_squared_distance( points, mesh.V, mesh.F, dists, I, C);
|
||||||
|
|
||||||
PointSet ret(I.rows(), 3);
|
PointSet ret(I.rows(), 3);
|
||||||
for(int i = 0; i < I.rows(); i++) {
|
for(int i = 0; i < I.rows(); i++) {
|
||||||
|
throw_on_cancel();
|
||||||
auto idx = I(i);
|
auto idx = I(i);
|
||||||
auto trindex = mesh.F.row(idx);
|
auto trindex = mesh.F.row(idx);
|
||||||
|
|
||||||
auto& p1 = mesh.V.row(trindex(0));
|
const Vec3d& p1 = mesh.V.row(trindex(0));
|
||||||
auto& p2 = mesh.V.row(trindex(1));
|
const Vec3d& p2 = mesh.V.row(trindex(1));
|
||||||
auto& p3 = mesh.V.row(trindex(2));
|
const Vec3d& p3 = mesh.V.row(trindex(2));
|
||||||
|
|
||||||
Eigen::Vector3d U = p2 - p1;
|
// We should check if the point lies on an edge of the hosting triangle.
|
||||||
Eigen::Vector3d V = p3 - p1;
|
// If it does than all the other triangles using the same two points
|
||||||
ret.row(i) = U.cross(V).normalized();
|
// 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;
|
return ret;
|
||||||
#else // TODO: do something on 32 bit windows
|
|
||||||
return {};
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double ray_mesh_intersect(const Vec3d& s,
|
double ray_mesh_intersect(const Vec3d& s,
|
||||||
|
@ -223,7 +308,7 @@ Segments model_boundary(const EigenMesh3D& emesh, double offs)
|
||||||
pp.emplace_back(p);
|
pp.emplace_back(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExPolygons merged = union_ex(offset(pp, float(scale_(offs))), true);
|
ExPolygons merged = union_ex(Slic3r::offset(pp, float(scale_(offs))), true);
|
||||||
|
|
||||||
for(auto& expoly : merged) {
|
for(auto& expoly : merged) {
|
||||||
auto lines = expoly.lines();
|
auto lines = expoly.lines();
|
||||||
|
|
|
@ -65,11 +65,6 @@ PrinterTechnology BackgroundSlicingProcess::current_printer_technology() const
|
||||||
return m_print->technology();
|
return m_print->technology();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isspace(int ch)
|
|
||||||
{
|
|
||||||
return std::isspace(ch) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function may one day be merged into the Print, but historically the print was separated
|
// This function may one day be merged into the Print, but historically the print was separated
|
||||||
// from the G-code generator.
|
// from the G-code generator.
|
||||||
void BackgroundSlicingProcess::process_fff()
|
void BackgroundSlicingProcess::process_fff()
|
||||||
|
@ -88,6 +83,27 @@ void BackgroundSlicingProcess::process_fff()
|
||||||
m_print->set_status(95, "Running post-processing scripts");
|
m_print->set_status(95, "Running post-processing scripts");
|
||||||
run_post_process_scripts(export_path, m_fff_print->config());
|
run_post_process_scripts(export_path, m_fff_print->config());
|
||||||
m_print->set_status(100, "G-code file exported to " + export_path);
|
m_print->set_status(100, "G-code file exported to " + export_path);
|
||||||
|
} else if (! m_upload_job.empty()) {
|
||||||
|
// A print host upload job has been scheduled
|
||||||
|
|
||||||
|
// XXX: is fs::path::string() right?
|
||||||
|
|
||||||
|
// Generate a unique temp path to which the gcode is copied
|
||||||
|
boost::filesystem::path source_path = boost::filesystem::temp_directory_path()
|
||||||
|
/ boost::filesystem::unique_path(".printhost.%%%%-%%%%-%%%%-%%%%.gcode");
|
||||||
|
|
||||||
|
if (copy_file(m_temp_output_path, source_path.string()) != 0) {
|
||||||
|
throw std::runtime_error("Copying of the temporary G-code to the output G-code failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_print->set_status(95, "Running post-processing scripts");
|
||||||
|
run_post_process_scripts(source_path.string(), m_fff_print->config());
|
||||||
|
m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str());
|
||||||
|
|
||||||
|
m_upload_job.upload_data.source_path = std::move(source_path);
|
||||||
|
m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
|
||||||
|
|
||||||
|
GUI::wxGetApp().printhost_job_queue().enqueue(std::move(m_upload_job));
|
||||||
} else {
|
} else {
|
||||||
m_print->set_status(100, "Slicing complete");
|
m_print->set_status(100, "Slicing complete");
|
||||||
}
|
}
|
||||||
|
@ -373,13 +389,10 @@ void BackgroundSlicingProcess::schedule_upload(Slic3r::PrintHostJob upload_job)
|
||||||
if (! m_export_path.empty())
|
if (! m_export_path.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const boost::filesystem::path path = boost::filesystem::temp_directory_path()
|
|
||||||
/ boost::filesystem::unique_path(".upload.%%%%-%%%%-%%%%-%%%%.gcode");
|
|
||||||
|
|
||||||
// Guard against entering the export step before changing the export path.
|
// Guard against entering the export step before changing the export path.
|
||||||
tbb::mutex::scoped_lock lock(m_print->state_mutex());
|
tbb::mutex::scoped_lock lock(m_print->state_mutex());
|
||||||
this->invalidate_step(bspsGCodeFinalize);
|
this->invalidate_step(bspsGCodeFinalize);
|
||||||
m_export_path = path.string();
|
m_export_path = std::string();
|
||||||
m_upload_job = std::move(upload_job);
|
m_upload_job = std::move(upload_job);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -617,42 +617,71 @@ bool GLCanvas3D::Bed::_are_equal(const Pointfs& bed_1, const Pointfs& bed_2)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const double GLCanvas3D::Axes::Radius = 0.5;
|
||||||
|
const double GLCanvas3D::Axes::ArrowBaseRadius = 2.5 * GLCanvas3D::Axes::Radius;
|
||||||
|
const double GLCanvas3D::Axes::ArrowLength = 5.0;
|
||||||
|
|
||||||
GLCanvas3D::Axes::Axes()
|
GLCanvas3D::Axes::Axes()
|
||||||
: origin(Vec3d::Zero())
|
: origin(Vec3d::Zero())
|
||||||
, length(0.0f)
|
, length(Vec3d::Zero())
|
||||||
{
|
{
|
||||||
|
m_quadric = ::gluNewQuadric();
|
||||||
|
if (m_quadric != nullptr)
|
||||||
|
::gluQuadricDrawStyle(m_quadric, GLU_FILL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::Axes::render(bool depth_test) const
|
GLCanvas3D::Axes::~Axes()
|
||||||
{
|
{
|
||||||
if (depth_test)
|
if (m_quadric != nullptr)
|
||||||
::glEnable(GL_DEPTH_TEST);
|
::gluDeleteQuadric(m_quadric);
|
||||||
else
|
}
|
||||||
::glDisable(GL_DEPTH_TEST);
|
|
||||||
|
|
||||||
::glLineWidth(2.0f);
|
void GLCanvas3D::Axes::render() const
|
||||||
::glBegin(GL_LINES);
|
{
|
||||||
// draw line for x axis
|
if (m_quadric == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
::glEnable(GL_DEPTH_TEST);
|
||||||
|
::glEnable(GL_LIGHTING);
|
||||||
|
|
||||||
|
// x axis
|
||||||
::glColor3f(1.0f, 0.0f, 0.0f);
|
::glColor3f(1.0f, 0.0f, 0.0f);
|
||||||
::glVertex3dv(origin.data());
|
::glPushMatrix();
|
||||||
::glVertex3f((GLfloat)origin(0) + length, (GLfloat)origin(1), (GLfloat)origin(2));
|
::glTranslated(origin(0), origin(1), origin(2));
|
||||||
// draw line for y axis
|
::glRotated(90.0, 0.0, 1.0, 0.0);
|
||||||
::glColor3f(0.0f, 1.0f, 0.0f);
|
render_axis(length(0));
|
||||||
::glVertex3dv(origin.data());
|
::glPopMatrix();
|
||||||
::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1) + length, (GLfloat)origin(2));
|
|
||||||
::glEnd();
|
|
||||||
// draw line for Z axis
|
|
||||||
// (re-enable depth test so that axis is correctly shown when objects are behind it)
|
|
||||||
if (!depth_test)
|
|
||||||
::glEnable(GL_DEPTH_TEST);
|
|
||||||
|
|
||||||
::glBegin(GL_LINES);
|
// y axis
|
||||||
|
::glColor3f(0.0f, 1.0f, 0.0f);
|
||||||
|
::glPushMatrix();
|
||||||
|
::glTranslated(origin(0), origin(1), origin(2));
|
||||||
|
::glRotated(-90.0, 1.0, 0.0, 0.0);
|
||||||
|
render_axis(length(1));
|
||||||
|
::glPopMatrix();
|
||||||
|
|
||||||
|
// z axis
|
||||||
::glColor3f(0.0f, 0.0f, 1.0f);
|
::glColor3f(0.0f, 0.0f, 1.0f);
|
||||||
::glVertex3dv(origin.data());
|
::glPushMatrix();
|
||||||
::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2) + length);
|
::glTranslated(origin(0), origin(1), origin(2));
|
||||||
::glEnd();
|
render_axis(length(2));
|
||||||
|
::glPopMatrix();
|
||||||
|
|
||||||
|
::glDisable(GL_LIGHTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLCanvas3D::Axes::render_axis(double length) const
|
||||||
|
{
|
||||||
|
::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
|
||||||
|
::gluCylinder(m_quadric, Radius, Radius, length, 32, 1);
|
||||||
|
::gluQuadricOrientation(m_quadric, GLU_INSIDE);
|
||||||
|
::gluDisk(m_quadric, 0.0, Radius, 32, 1);
|
||||||
|
::glTranslated(0.0, 0.0, length);
|
||||||
|
::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
|
||||||
|
::gluCylinder(m_quadric, ArrowBaseRadius, 0.0, ArrowLength, 32, 1);
|
||||||
|
::gluQuadricOrientation(m_quadric, GLU_INSIDE);
|
||||||
|
::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1);
|
||||||
|
}
|
||||||
|
|
||||||
GLCanvas3D::Shader::Shader()
|
GLCanvas3D::Shader::Shader()
|
||||||
: m_shader(nullptr)
|
: m_shader(nullptr)
|
||||||
|
@ -2501,9 +2530,9 @@ void GLCanvas3D::Selection::_ensure_on_bed()
|
||||||
}
|
}
|
||||||
#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING
|
#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING
|
||||||
|
|
||||||
const float GLCanvas3D::Gizmos::OverlayTexturesScale = 1.0f;
|
const float GLCanvas3D::Gizmos::OverlayIconsScale = 1.0f;
|
||||||
const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f * OverlayTexturesScale;
|
const float GLCanvas3D::Gizmos::OverlayBorder = 5.0f;
|
||||||
const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale;
|
const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayIconsScale;
|
||||||
|
|
||||||
GLCanvas3D::Gizmos::Gizmos()
|
GLCanvas3D::Gizmos::Gizmos()
|
||||||
: m_enabled(false)
|
: m_enabled(false)
|
||||||
|
@ -2584,6 +2613,23 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent)
|
||||||
|
|
||||||
m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo));
|
m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo));
|
||||||
|
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
m_background_texture.metadata.filename = "toolbar_background.png";
|
||||||
|
m_background_texture.metadata.left = 16;
|
||||||
|
m_background_texture.metadata.top = 16;
|
||||||
|
m_background_texture.metadata.right = 16;
|
||||||
|
m_background_texture.metadata.bottom = 16;
|
||||||
|
|
||||||
|
if (!m_background_texture.metadata.filename.empty())
|
||||||
|
{
|
||||||
|
if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false))
|
||||||
|
{
|
||||||
|
_reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2606,24 +2652,22 @@ std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, con
|
||||||
|
|
||||||
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
||||||
float height = _get_total_overlay_height();
|
float height = _get_total_overlay_height();
|
||||||
float top_y = 0.5f * (cnv_h - height);
|
float top_y = 0.5f * (cnv_h - height) + OverlayBorder;
|
||||||
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||||
{
|
{
|
||||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
|
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale;
|
||||||
float half_tex_size = 0.5f * tex_size;
|
|
||||||
|
|
||||||
// we currently use circular icons for gizmo, so we check the radius
|
|
||||||
if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On))
|
if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On))
|
||||||
{
|
{
|
||||||
bool inside = (mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size;
|
bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size);
|
||||||
it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off);
|
it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off);
|
||||||
if (inside)
|
if (inside)
|
||||||
name = it->second->get_name();
|
name = it->second->get_name();
|
||||||
}
|
}
|
||||||
top_y += (tex_size + OverlayGapY);
|
top_y += (icon_size + OverlayGapY);
|
||||||
}
|
}
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
|
@ -2636,17 +2680,16 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec
|
||||||
|
|
||||||
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
||||||
float height = _get_total_overlay_height();
|
float height = _get_total_overlay_height();
|
||||||
float top_y = 0.5f * (cnv_h - height);
|
float top_y = 0.5f * (cnv_h - height) + OverlayBorder;
|
||||||
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||||
{
|
{
|
||||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
|
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale;
|
||||||
float half_tex_size = 0.5f * tex_size;
|
|
||||||
|
|
||||||
// we currently use circular icons for gizmo, so we check the radius
|
bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size);
|
||||||
if (it->second->is_activable(selection) && ((mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size))
|
if (it->second->is_activable(selection) && inside)
|
||||||
{
|
{
|
||||||
if ((it->second->get_state() == GLGizmoBase::On))
|
if ((it->second->get_state() == GLGizmoBase::On))
|
||||||
{
|
{
|
||||||
|
@ -2662,7 +2705,7 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec
|
||||||
else
|
else
|
||||||
it->second->set_state(GLGizmoBase::Off);
|
it->second->set_state(GLGizmoBase::Off);
|
||||||
|
|
||||||
top_y += (tex_size + OverlayGapY);
|
top_y += (icon_size + OverlayGapY);
|
||||||
}
|
}
|
||||||
|
|
||||||
GizmosMap::iterator it = m_gizmos.find(m_current);
|
GizmosMap::iterator it = m_gizmos.find(m_current);
|
||||||
|
@ -2734,20 +2777,18 @@ bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const
|
||||||
|
|
||||||
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
||||||
float height = _get_total_overlay_height();
|
float height = _get_total_overlay_height();
|
||||||
float top_y = 0.5f * (cnv_h - height);
|
float top_y = 0.5f * (cnv_h - height) + OverlayBorder;
|
||||||
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||||
{
|
{
|
||||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
|
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale;
|
||||||
float half_tex_size = 0.5f * tex_size;
|
|
||||||
|
|
||||||
// we currently use circular icons for gizmo, so we check the radius
|
if ((OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size))
|
||||||
if ((mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
top_y += (tex_size + OverlayGapY);
|
top_y += (icon_size + OverlayGapY);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -3020,21 +3061,102 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva
|
||||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||||
|
|
||||||
float height = _get_total_overlay_height();
|
float height = _get_total_overlay_height();
|
||||||
float top_x = (OverlayOffsetX - 0.5f * cnv_w) * inv_zoom;
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
float top_y = 0.5f * height * inv_zoom;
|
float scaled_border = OverlayBorder * inv_zoom;
|
||||||
|
|
||||||
|
float top_x = (-0.5f * cnv_w) * inv_zoom;
|
||||||
|
float top_y = (0.5f * height) * inv_zoom;
|
||||||
|
|
||||||
|
float left = top_x;
|
||||||
|
float top = top_y;
|
||||||
|
float right = left + _get_total_overlay_width() * inv_zoom;
|
||||||
|
float bottom = top - height * inv_zoom;
|
||||||
|
|
||||||
|
// renders background
|
||||||
|
unsigned int bg_tex_id = m_background_texture.texture.get_id();
|
||||||
|
float bg_tex_width = (float)m_background_texture.texture.get_width();
|
||||||
|
float bg_tex_height = (float)m_background_texture.texture.get_height();
|
||||||
|
if ((bg_tex_id != 0) && (bg_tex_width > 0) && (bg_tex_height > 0))
|
||||||
|
{
|
||||||
|
float inv_bg_tex_width = (bg_tex_width != 0.0f) ? 1.0f / bg_tex_width : 0.0f;
|
||||||
|
float inv_bg_tex_height = (bg_tex_height != 0.0f) ? 1.0f / bg_tex_height : 0.0f;
|
||||||
|
|
||||||
|
float bg_uv_left = 0.0f;
|
||||||
|
float bg_uv_right = 1.0f;
|
||||||
|
float bg_uv_top = 1.0f;
|
||||||
|
float bg_uv_bottom = 0.0f;
|
||||||
|
|
||||||
|
float bg_left = left;
|
||||||
|
float bg_right = right;
|
||||||
|
float bg_top = top;
|
||||||
|
float bg_bottom = bottom;
|
||||||
|
float bg_width = right - left;
|
||||||
|
float bg_height = top - bottom;
|
||||||
|
float bg_min_size = std::min(bg_width, bg_height);
|
||||||
|
|
||||||
|
float bg_uv_i_left = (float)m_background_texture.metadata.left * inv_bg_tex_width;
|
||||||
|
float bg_uv_i_right = 1.0f - (float)m_background_texture.metadata.right * inv_bg_tex_width;
|
||||||
|
float bg_uv_i_top = 1.0f - (float)m_background_texture.metadata.top * inv_bg_tex_height;
|
||||||
|
float bg_uv_i_bottom = (float)m_background_texture.metadata.bottom * inv_bg_tex_height;
|
||||||
|
|
||||||
|
float bg_i_left = bg_left + scaled_border;
|
||||||
|
float bg_i_right = bg_right - scaled_border;
|
||||||
|
float bg_i_top = bg_top - scaled_border;
|
||||||
|
float bg_i_bottom = bg_bottom + scaled_border;
|
||||||
|
|
||||||
|
bg_uv_left = bg_uv_i_left;
|
||||||
|
bg_i_left = bg_left;
|
||||||
|
|
||||||
|
if ((OverlayBorder > 0) && (bg_uv_top != bg_uv_i_top))
|
||||||
|
{
|
||||||
|
if (bg_uv_left != bg_uv_i_left)
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_top, bg_top, { { bg_uv_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_top }, { bg_uv_left, bg_uv_top } });
|
||||||
|
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_top, bg_top, { { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_top }, { bg_uv_i_left, bg_uv_top } });
|
||||||
|
|
||||||
|
if (bg_uv_right != bg_uv_i_right)
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_top, bg_top, { { bg_uv_i_right, bg_uv_i_top }, { bg_uv_right, bg_uv_i_top }, { bg_uv_right, bg_uv_top }, { bg_uv_i_right, bg_uv_top } });
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((OverlayBorder > 0) && (bg_uv_left != bg_uv_i_left))
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_bottom, bg_i_top, { { bg_uv_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_left, bg_uv_i_top } });
|
||||||
|
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_bottom, bg_i_top, { { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top } });
|
||||||
|
|
||||||
|
if ((OverlayBorder > 0) && (bg_uv_right != bg_uv_i_right))
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_bottom, bg_i_top, { { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top } });
|
||||||
|
|
||||||
|
if ((OverlayBorder > 0) && (bg_uv_bottom != bg_uv_i_bottom))
|
||||||
|
{
|
||||||
|
if (bg_uv_left != bg_uv_i_left)
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_bottom, bg_i_bottom, { { bg_uv_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_left, bg_uv_i_bottom } });
|
||||||
|
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_bottom, bg_i_bottom, { { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_right, bg_uv_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom } });
|
||||||
|
|
||||||
|
if (bg_uv_right != bg_uv_i_right)
|
||||||
|
GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_bottom, bg_i_bottom, { { bg_uv_i_right, bg_uv_bottom }, { bg_uv_right, bg_uv_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
top_x += OverlayBorder * inv_zoom;
|
||||||
|
top_y -= OverlayBorder * inv_zoom;
|
||||||
|
#else
|
||||||
|
float top_x = (OverlayBorder - 0.5f * cnv_w) * inv_zoom;
|
||||||
|
float top_y = (0.5f * height - OverlayBorder) * inv_zoom;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
float scaled_gap_y = OverlayGapY * inv_zoom;
|
float scaled_gap_y = OverlayGapY * inv_zoom;
|
||||||
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||||
{
|
{
|
||||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale * inv_zoom;
|
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale * inv_zoom;
|
||||||
GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + tex_size, top_y - tex_size, top_y);
|
GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + icon_size, top_y - icon_size, top_y);
|
||||||
#if ENABLE_IMGUI
|
#if ENABLE_IMGUI
|
||||||
if (it->second->get_state() == GLGizmoBase::On)
|
if (it->second->get_state() == GLGizmoBase::On)
|
||||||
it->second->render_input_window(2.0f * OverlayOffsetX + tex_size * zoom, 0.5f * cnv_h - top_y * zoom, selection);
|
it->second->render_input_window(2.0f * OverlayBorder + icon_size * zoom, 0.5f * cnv_h - top_y * zoom, selection);
|
||||||
#endif // ENABLE_IMGUI
|
#endif // ENABLE_IMGUI
|
||||||
top_y -= (tex_size + scaled_gap_y);
|
top_y -= (icon_size + scaled_gap_y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3047,19 +3169,35 @@ void GLCanvas3D::Gizmos::_render_current_gizmo(const GLCanvas3D::Selection& sele
|
||||||
|
|
||||||
float GLCanvas3D::Gizmos::_get_total_overlay_height() const
|
float GLCanvas3D::Gizmos::_get_total_overlay_height() const
|
||||||
{
|
{
|
||||||
float height = 0.0f;
|
float height = 2.0f * OverlayBorder;
|
||||||
|
|
||||||
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||||
{
|
{
|
||||||
if (it->first == SlaSupports && wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA)
|
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
height += (float)it->second->get_textures_size() * OverlayTexturesScale + OverlayGapY;
|
height += (float)it->second->get_textures_size() * OverlayIconsScale + OverlayGapY;
|
||||||
}
|
}
|
||||||
|
|
||||||
return height - OverlayGapY;
|
return height - OverlayGapY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
float GLCanvas3D::Gizmos::_get_total_overlay_width() const
|
||||||
|
{
|
||||||
|
float max_icon_width = 0.0f;
|
||||||
|
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||||
|
{
|
||||||
|
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
max_icon_width = std::max(max_icon_width, (float)it->second->get_textures_size() * OverlayIconsScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
return max_icon_width + 2.0f * OverlayBorder;
|
||||||
|
}
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
|
||||||
GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const
|
GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const
|
||||||
{
|
{
|
||||||
GizmosMap::const_iterator it = m_gizmos.find(m_current);
|
GizmosMap::const_iterator it = m_gizmos.find(m_current);
|
||||||
|
@ -3672,7 +3810,7 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape)
|
||||||
|
|
||||||
// Set the origin and size for painting of the coordinate system axes.
|
// Set the origin and size for painting of the coordinate system axes.
|
||||||
m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z);
|
m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z);
|
||||||
set_axes_length(0.3f * (float)m_bed.get_bounding_box().max_size());
|
set_bed_axes_length(0.1 * m_bed.get_bounding_box().max_size());
|
||||||
|
|
||||||
if (new_shape)
|
if (new_shape)
|
||||||
zoom_to_bed();
|
zoom_to_bed();
|
||||||
|
@ -3680,9 +3818,9 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape)
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::set_axes_length(float length)
|
void GLCanvas3D::set_bed_axes_length(double length)
|
||||||
{
|
{
|
||||||
m_axes.length = length;
|
m_axes.length = length * Vec3d::Ones();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::set_color_by(const std::string& value)
|
void GLCanvas3D::set_color_by(const std::string& value)
|
||||||
|
@ -3942,21 +4080,16 @@ void GLCanvas3D::render()
|
||||||
_render_background();
|
_render_background();
|
||||||
|
|
||||||
if (is_custom_bed) // untextured bed needs to be rendered before objects
|
if (is_custom_bed) // untextured bed needs to be rendered before objects
|
||||||
{
|
|
||||||
_render_bed(theta);
|
_render_bed(theta);
|
||||||
// disable depth testing so that axes are not covered by ground
|
|
||||||
_render_axes(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_render_objects();
|
_render_objects();
|
||||||
_render_sla_slices();
|
_render_sla_slices();
|
||||||
_render_selection();
|
_render_selection();
|
||||||
|
|
||||||
|
_render_axes();
|
||||||
|
|
||||||
if (!is_custom_bed) // textured bed needs to be rendered after objects
|
if (!is_custom_bed) // textured bed needs to be rendered after objects
|
||||||
{
|
|
||||||
_render_axes(true);
|
|
||||||
_render_bed(theta);
|
_render_bed(theta);
|
||||||
}
|
|
||||||
|
|
||||||
// we need to set the mouse's scene position here because the depth buffer
|
// we need to set the mouse's scene position here because the depth buffer
|
||||||
// could be invalidated by the following gizmo render methods
|
// could be invalidated by the following gizmo render methods
|
||||||
|
@ -5910,9 +6043,9 @@ void GLCanvas3D::_render_bed(float theta) const
|
||||||
m_bed.render(theta);
|
m_bed.render(theta);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::_render_axes(bool depth_test) const
|
void GLCanvas3D::_render_axes() const
|
||||||
{
|
{
|
||||||
m_axes.render(depth_test);
|
m_axes.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::_render_objects() const
|
void GLCanvas3D::_render_objects() const
|
||||||
|
|
|
@ -20,6 +20,8 @@ class wxTimerEvent;
|
||||||
class wxPaintEvent;
|
class wxPaintEvent;
|
||||||
class wxGLCanvas;
|
class wxGLCanvas;
|
||||||
|
|
||||||
|
class GLUquadric;
|
||||||
|
typedef class GLUquadric GLUquadricObj;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -231,12 +233,20 @@ class GLCanvas3D
|
||||||
|
|
||||||
struct Axes
|
struct Axes
|
||||||
{
|
{
|
||||||
|
static const double Radius;
|
||||||
|
static const double ArrowBaseRadius;
|
||||||
|
static const double ArrowLength;
|
||||||
Vec3d origin;
|
Vec3d origin;
|
||||||
float length;
|
Vec3d length;
|
||||||
|
GLUquadricObj* m_quadric;
|
||||||
|
|
||||||
Axes();
|
Axes();
|
||||||
|
~Axes();
|
||||||
|
|
||||||
void render(bool depth_test) const;
|
void render() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void render_axis(double length) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Shader
|
class Shader
|
||||||
|
@ -607,8 +617,8 @@ public:
|
||||||
private:
|
private:
|
||||||
class Gizmos
|
class Gizmos
|
||||||
{
|
{
|
||||||
static const float OverlayTexturesScale;
|
static const float OverlayIconsScale;
|
||||||
static const float OverlayOffsetX;
|
static const float OverlayBorder;
|
||||||
static const float OverlayGapY;
|
static const float OverlayGapY;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -628,6 +638,9 @@ private:
|
||||||
bool m_enabled;
|
bool m_enabled;
|
||||||
typedef std::map<EType, GLGizmoBase*> GizmosMap;
|
typedef std::map<EType, GLGizmoBase*> GizmosMap;
|
||||||
GizmosMap m_gizmos;
|
GizmosMap m_gizmos;
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
BackgroundTexture m_background_texture;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
EType m_current;
|
EType m_current;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -696,6 +709,9 @@ private:
|
||||||
void _render_current_gizmo(const Selection& selection) const;
|
void _render_current_gizmo(const Selection& selection) const;
|
||||||
|
|
||||||
float _get_total_overlay_height() const;
|
float _get_total_overlay_height() const;
|
||||||
|
#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
|
float _get_total_overlay_width() const;
|
||||||
|
#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE
|
||||||
GLGizmoBase* _get_current() const;
|
GLGizmoBase* _get_current() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -864,8 +880,7 @@ public:
|
||||||
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
|
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
|
||||||
// Sets m_bed.m_polygon to limit the object placement.
|
// Sets m_bed.m_polygon to limit the object placement.
|
||||||
void set_bed_shape(const Pointfs& shape);
|
void set_bed_shape(const Pointfs& shape);
|
||||||
|
void set_bed_axes_length(double length);
|
||||||
void set_axes_length(float length);
|
|
||||||
|
|
||||||
void set_clipping_plane(unsigned int id, const ClippingPlane& plane)
|
void set_clipping_plane(unsigned int id, const ClippingPlane& plane)
|
||||||
{
|
{
|
||||||
|
@ -1005,7 +1020,7 @@ private:
|
||||||
void _picking_pass() const;
|
void _picking_pass() const;
|
||||||
void _render_background() const;
|
void _render_background() const;
|
||||||
void _render_bed(float theta) const;
|
void _render_bed(float theta) const;
|
||||||
void _render_axes(bool depth_test) const;
|
void _render_axes() const;
|
||||||
void _render_objects() const;
|
void _render_objects() const;
|
||||||
void _render_selection() const;
|
void _render_selection() const;
|
||||||
void _render_warning_texture() const;
|
void _render_warning_texture() const;
|
||||||
|
|
|
@ -73,7 +73,6 @@ GUI_App::GUI_App()
|
||||||
: wxApp()
|
: wxApp()
|
||||||
#if ENABLE_IMGUI
|
#if ENABLE_IMGUI
|
||||||
, m_imgui(new ImGuiWrapper())
|
, m_imgui(new ImGuiWrapper())
|
||||||
, m_printhost_queue(new PrintHostJobQueue())
|
|
||||||
#endif // ENABLE_IMGUI
|
#endif // ENABLE_IMGUI
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -142,6 +141,8 @@ bool GUI_App::OnInit()
|
||||||
update_mode();
|
update_mode();
|
||||||
SetTopWindow(mainframe);
|
SetTopWindow(mainframe);
|
||||||
|
|
||||||
|
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
||||||
|
|
||||||
CallAfter([this]() {
|
CallAfter([this]() {
|
||||||
// temporary workaround for the correct behavior of the Scrolled sidebar panel
|
// temporary workaround for the correct behavior of the Scrolled sidebar panel
|
||||||
auto& panel = sidebar();
|
auto& panel = sidebar();
|
||||||
|
|
|
@ -92,7 +92,7 @@ class GUI_App : public wxApp
|
||||||
std::unique_ptr<ImGuiWrapper> m_imgui;
|
std::unique_ptr<ImGuiWrapper> m_imgui;
|
||||||
#endif // ENABLE_IMGUI
|
#endif // ENABLE_IMGUI
|
||||||
|
|
||||||
std::unique_ptr<PrintHostJobQueue> m_printhost_queue;
|
std::unique_ptr<PrintHostJobQueue> m_printhost_job_queue;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool OnInit() override;
|
bool OnInit() override;
|
||||||
|
@ -164,7 +164,7 @@ public:
|
||||||
ImGuiWrapper* imgui() { return m_imgui.get(); }
|
ImGuiWrapper* imgui() { return m_imgui.get(); }
|
||||||
#endif // ENABLE_IMGUI
|
#endif // ENABLE_IMGUI
|
||||||
|
|
||||||
PrintHostJobQueue& printhost_queue() { return *m_printhost_queue.get(); }
|
PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); }
|
||||||
|
|
||||||
};
|
};
|
||||||
DECLARE_APP(GUI_App)
|
DECLARE_APP(GUI_App)
|
||||||
|
|
|
@ -1460,12 +1460,9 @@ void ObjectList::update_selections()
|
||||||
select_items(sels);
|
select_items(sels);
|
||||||
|
|
||||||
if (GetSelection()) {
|
if (GetSelection()) {
|
||||||
const wxRect& sel_rc = GetItemRect(GetSelection());
|
const int sel_item_row = m_objects_model->GetRowByItem(GetSelection());
|
||||||
if (!sel_rc.IsEmpty()) {
|
ScrollLines(sel_item_row - m_selected_row);
|
||||||
const int rc_h = sel_rc.height;
|
m_selected_row = sel_item_row;
|
||||||
const int displ = GetMainWindow()->GetClientRect().GetHeight()/(2*rc_h)+1;
|
|
||||||
ScrollLines(int(sel_rc.y / rc_h - displ));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,8 @@ class ObjectList : public wxDataViewCtrl
|
||||||
bool m_parts_changed = false;
|
bool m_parts_changed = false;
|
||||||
bool m_part_settings_changed = false;
|
bool m_part_settings_changed = false;
|
||||||
|
|
||||||
|
int m_selected_row = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ObjectList(wxWindow* parent);
|
ObjectList(wxWindow* parent);
|
||||||
~ObjectList();
|
~ObjectList();
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "ProgressStatusBar.hpp"
|
#include "ProgressStatusBar.hpp"
|
||||||
#include "3DScene.hpp"
|
#include "3DScene.hpp"
|
||||||
#include "AppConfig.hpp"
|
#include "AppConfig.hpp"
|
||||||
|
#include "PrintHostDialogs.hpp"
|
||||||
#include "wxExtensions.hpp"
|
#include "wxExtensions.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
|
|
||||||
|
@ -30,7 +31,8 @@ namespace GUI {
|
||||||
MainFrame::MainFrame(const bool no_plater, const bool loaded) :
|
MainFrame::MainFrame(const bool no_plater, const bool loaded) :
|
||||||
wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE),
|
wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE),
|
||||||
m_no_plater(no_plater),
|
m_no_plater(no_plater),
|
||||||
m_loaded(loaded)
|
m_loaded(loaded),
|
||||||
|
m_printhost_queue_dlg(new PrintHostQueueDialog(this))
|
||||||
{
|
{
|
||||||
// Load the icon either from the exe, or from the ico file.
|
// Load the icon either from the exe, or from the ico file.
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
|
@ -326,7 +328,7 @@ void MainFrame::init_menubar()
|
||||||
size_t tab_offset = 0;
|
size_t tab_offset = 0;
|
||||||
if (m_plater) {
|
if (m_plater) {
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
append_menu_item(windowMenu, wxID_ANY, L("Plater Tab\tCtrl+1"), L("Show the plater"),
|
append_menu_item(windowMenu, wxID_HIGHEST + 1, L("Plater Tab\tCtrl+1"), L("Show the plater"),
|
||||||
[this](wxCommandEvent&) { select_tab(0); }, "application_view_tile.png");
|
[this](wxCommandEvent&) { select_tab(0); }, "application_view_tile.png");
|
||||||
#else
|
#else
|
||||||
append_menu_item(windowMenu, wxID_ANY, L("Select Plater Tab\tCtrl+1"), L("Show the plater"),
|
append_menu_item(windowMenu, wxID_ANY, L("Select Plater Tab\tCtrl+1"), L("Show the plater"),
|
||||||
|
@ -338,22 +340,35 @@ void MainFrame::init_menubar()
|
||||||
windowMenu->AppendSeparator();
|
windowMenu->AppendSeparator();
|
||||||
}
|
}
|
||||||
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
#if ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
append_menu_item(windowMenu, wxID_ANY, L("Print Settings Tab\tCtrl+2"), L("Show the print settings"),
|
append_menu_item(windowMenu, wxID_HIGHEST + 2, L("Print Settings Tab\tCtrl+2"), L("Show the print settings"),
|
||||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png");
|
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png");
|
||||||
append_menu_item(windowMenu, wxID_ANY, L("Filament Settings Tab\tCtrl+3"), L("Show the filament settings"),
|
append_menu_item(windowMenu, wxID_HIGHEST + 3, L("Filament Settings Tab\tCtrl+3"), L("Show the filament settings"),
|
||||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool.png");
|
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool.png");
|
||||||
append_menu_item(windowMenu, wxID_ANY, L("Printer Settings Tab\tCtrl+4"), L("Show the printer settings"),
|
append_menu_item(windowMenu, wxID_HIGHEST + 4, L("Printer Settings Tab\tCtrl+4"), L("Show the printer settings"),
|
||||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png");
|
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png");
|
||||||
if (m_plater) {
|
if (m_plater) {
|
||||||
windowMenu->AppendSeparator();
|
windowMenu->AppendSeparator();
|
||||||
wxMenuItem* item_3d = append_menu_item(windowMenu, wxID_ANY, L("3D\tCtrl+5"), L("Show the 3D editing view"),
|
wxMenuItem* item_3d = append_menu_item(windowMenu, wxID_HIGHEST + 5, L("3D\tCtrl+5"), L("Show the 3D editing view"),
|
||||||
[this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, "");
|
[this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, "");
|
||||||
wxMenuItem* item_preview = append_menu_item(windowMenu, wxID_ANY, L("Preview\tCtrl+6"), L("Show the 3D slices preview"),
|
wxMenuItem* item_preview = append_menu_item(windowMenu, wxID_HIGHEST + 6, L("Preview\tCtrl+6"), L("Show the 3D slices preview"),
|
||||||
[this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, "");
|
[this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, "");
|
||||||
|
|
||||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_3d->GetId());
|
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_3d->GetId());
|
||||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_preview->GetId());
|
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_preview->GetId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
// This is needed on Windows to fake the CTRL+# of the window menu when using the numpad
|
||||||
|
wxAcceleratorEntry entries[6];
|
||||||
|
entries[0].Set(wxACCEL_CTRL, WXK_NUMPAD1, wxID_HIGHEST + 1);
|
||||||
|
entries[1].Set(wxACCEL_CTRL, WXK_NUMPAD2, wxID_HIGHEST + 2);
|
||||||
|
entries[2].Set(wxACCEL_CTRL, WXK_NUMPAD3, wxID_HIGHEST + 3);
|
||||||
|
entries[3].Set(wxACCEL_CTRL, WXK_NUMPAD4, wxID_HIGHEST + 4);
|
||||||
|
entries[4].Set(wxACCEL_CTRL, WXK_NUMPAD5, wxID_HIGHEST + 5);
|
||||||
|
entries[5].Set(wxACCEL_CTRL, WXK_NUMPAD6, wxID_HIGHEST + 6);
|
||||||
|
wxAcceleratorTable accel(6, entries);
|
||||||
|
SetAcceleratorTable(accel);
|
||||||
|
#endif // _WIN32
|
||||||
#else
|
#else
|
||||||
append_menu_item(windowMenu, wxID_ANY, L("Select Print Settings Tab\tCtrl+2"), L("Show the print settings"),
|
append_menu_item(windowMenu, wxID_ANY, L("Select Print Settings Tab\tCtrl+2"), L("Show the print settings"),
|
||||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png");
|
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png");
|
||||||
|
@ -362,6 +377,10 @@ void MainFrame::init_menubar()
|
||||||
append_menu_item(windowMenu, wxID_ANY, L("Select Printer Settings Tab\tCtrl+4"), L("Show the printer settings"),
|
append_menu_item(windowMenu, wxID_ANY, L("Select Printer Settings Tab\tCtrl+4"), L("Show the printer settings"),
|
||||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png");
|
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png");
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
|
|
||||||
|
windowMenu->AppendSeparator();
|
||||||
|
append_menu_item(windowMenu, wxID_ANY, L("Print Host Upload Queue"), L("Display the Print Host Upload Queue window"),
|
||||||
|
[this](wxCommandEvent&) { m_printhost_queue_dlg->ShowModal(); }, "arrow_up.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
// View menu
|
// View menu
|
||||||
|
|
|
@ -21,7 +21,9 @@ class ProgressStatusBar;
|
||||||
|
|
||||||
namespace GUI
|
namespace GUI
|
||||||
{
|
{
|
||||||
|
|
||||||
class Tab;
|
class Tab;
|
||||||
|
class PrintHostQueueDialog;
|
||||||
|
|
||||||
enum QuickSlice
|
enum QuickSlice
|
||||||
{
|
{
|
||||||
|
@ -52,6 +54,8 @@ class MainFrame : public wxFrame
|
||||||
wxMenuItem* m_menu_item_repeat { nullptr };
|
wxMenuItem* m_menu_item_repeat { nullptr };
|
||||||
wxMenuItem* m_menu_item_reslice_now { nullptr };
|
wxMenuItem* m_menu_item_reslice_now { nullptr };
|
||||||
|
|
||||||
|
PrintHostQueueDialog *m_printhost_queue_dlg;
|
||||||
|
|
||||||
std::string get_base_name(const wxString full_name) const ;
|
std::string get_base_name(const wxString full_name) const ;
|
||||||
std::string get_dir_name(const wxString full_name) const ;
|
std::string get_dir_name(const wxString full_name) const ;
|
||||||
|
|
||||||
|
@ -93,6 +97,8 @@ public:
|
||||||
void select_tab(size_t tab) const;
|
void select_tab(size_t tab) const;
|
||||||
void select_view(const std::string& direction);
|
void select_view(const std::string& direction);
|
||||||
|
|
||||||
|
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }
|
||||||
|
|
||||||
Plater* m_plater { nullptr };
|
Plater* m_plater { nullptr };
|
||||||
wxNotebook* m_tabpanel { nullptr };
|
wxNotebook* m_tabpanel { nullptr };
|
||||||
wxProgressDialog* m_progress_dialog { nullptr };
|
wxProgressDialog* m_progress_dialog { nullptr };
|
||||||
|
|
|
@ -732,8 +732,7 @@ void Sidebar::show_info_sizer()
|
||||||
p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast<int>(model_object->materials_count())));
|
p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast<int>(model_object->materials_count())));
|
||||||
|
|
||||||
auto& stats = model_object->volumes.front()->mesh.stl.stats;
|
auto& stats = model_object->volumes.front()->mesh.stl.stats;
|
||||||
auto sf = model_instance->get_scaling_factor();
|
p->object_info->info_volume->SetLabel(wxString::Format("%.2f", size(0) * size(1) * size(2)));
|
||||||
p->object_info->info_volume->SetLabel(wxString::Format("%.2f", size(0) * size(1) * size(2) * sf(0) * sf(1) * sf(2)));
|
|
||||||
p->object_info->info_facets->SetLabel(wxString::Format(_(L("%d (%d shells)")), static_cast<int>(model_object->facets_count()), stats.number_of_parts));
|
p->object_info->info_facets->SetLabel(wxString::Format(_(L("%d (%d shells)")), static_cast<int>(model_object->facets_count()), stats.number_of_parts));
|
||||||
|
|
||||||
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
|
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
|
||||||
|
@ -3145,7 +3144,7 @@ void Plater::send_gcode()
|
||||||
}
|
}
|
||||||
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
|
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
|
||||||
|
|
||||||
Slic3r::PrintHostSendDialog dlg(default_output_file);
|
PrintHostSendDialog dlg(default_output_file);
|
||||||
if (dlg.ShowModal() == wxID_OK) {
|
if (dlg.ShowModal() == wxID_OK) {
|
||||||
upload_job.upload_data.upload_path = dlg.filename();
|
upload_job.upload_data.upload_path = dlg.filename();
|
||||||
upload_job.upload_data.start_print = dlg.start_print();
|
upload_job.upload_data.start_print = dlg.start_print();
|
||||||
|
|
|
@ -1,20 +1,28 @@
|
||||||
#include "PrintHostDialogs.hpp"
|
#include "PrintHostDialogs.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <wx/frame.h>
|
#include <wx/frame.h>
|
||||||
#include <wx/event.h>
|
|
||||||
#include <wx/progdlg.h>
|
#include <wx/progdlg.h>
|
||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
#include <wx/stattext.h>
|
#include <wx/stattext.h>
|
||||||
#include <wx/textctrl.h>
|
#include <wx/textctrl.h>
|
||||||
#include <wx/checkbox.h>
|
#include <wx/checkbox.h>
|
||||||
|
#include <wx/button.h>
|
||||||
|
#include <wx/dataview.h>
|
||||||
|
#include <wx/wupdlock.h>
|
||||||
|
#include <wx/debug.h>
|
||||||
|
|
||||||
#include "slic3r/GUI/GUI.hpp"
|
#include "GUI.hpp"
|
||||||
#include "slic3r/GUI/MsgDialog.hpp"
|
#include "MsgDialog.hpp"
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "I18N.hpp"
|
||||||
|
#include "../Utils/PrintHost.hpp"
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
namespace GUI {
|
||||||
|
|
||||||
|
|
||||||
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path)
|
PrintHostSendDialog::PrintHostSendDialog(const fs::path &path)
|
||||||
: MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE)
|
: MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE)
|
||||||
|
@ -45,5 +53,95 @@ fs::path PrintHostSendDialog::filename() const
|
||||||
|
|
||||||
bool PrintHostSendDialog::start_print() const
|
bool PrintHostSendDialog::start_print() const
|
||||||
{
|
{
|
||||||
return box_print->GetValue(); }
|
return box_print->GetValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||||
|
wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
||||||
|
|
||||||
|
PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id)
|
||||||
|
: wxEvent(winid, eventType)
|
||||||
|
, job_id(job_id)
|
||||||
|
{}
|
||||||
|
|
||||||
|
PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id, int progress)
|
||||||
|
: wxEvent(winid, eventType)
|
||||||
|
, job_id(job_id)
|
||||||
|
, progress(progress)
|
||||||
|
{}
|
||||||
|
|
||||||
|
PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id, wxString error)
|
||||||
|
: wxEvent(winid, eventType)
|
||||||
|
, job_id(job_id)
|
||||||
|
, error(std::move(error))
|
||||||
|
{}
|
||||||
|
|
||||||
|
wxEvent *PrintHostQueueDialog::Event::Clone() const
|
||||||
|
{
|
||||||
|
return new Event(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
||||||
|
: wxDialog(parent, wxID_ANY, _(L("Print host upload queue")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||||
|
, on_progress_evt(this, EVT_PRINTHOST_PROGRESS, &PrintHostQueueDialog::on_progress, this)
|
||||||
|
, on_error_evt(this, EVT_PRINTHOST_ERROR, &PrintHostQueueDialog::on_error, this)
|
||||||
|
{
|
||||||
|
enum { HEIGHT = 800, WIDTH = 400, SPACING = 5 };
|
||||||
|
|
||||||
|
SetMinSize(wxSize(HEIGHT, WIDTH));
|
||||||
|
|
||||||
|
auto *topsizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
|
job_list = new wxDataViewListCtrl(this, wxID_ANY);
|
||||||
|
job_list->AppendTextColumn("ID", wxDATAVIEW_CELL_INERT);
|
||||||
|
job_list->AppendProgressColumn("Progress", wxDATAVIEW_CELL_INERT);
|
||||||
|
job_list->AppendTextColumn("Status", wxDATAVIEW_CELL_INERT);
|
||||||
|
job_list->AppendTextColumn("Host", wxDATAVIEW_CELL_INERT);
|
||||||
|
job_list->AppendTextColumn("Filename", wxDATAVIEW_CELL_INERT);
|
||||||
|
|
||||||
|
auto *btnsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
auto *btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected")));
|
||||||
|
auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close")));
|
||||||
|
btnsizer->Add(btn_cancel, 0, wxRIGHT, SPACING);
|
||||||
|
btnsizer->AddStretchSpacer();
|
||||||
|
btnsizer->Add(btn_close);
|
||||||
|
|
||||||
|
topsizer->Add(job_list, 1, wxEXPAND | wxBOTTOM, SPACING);
|
||||||
|
topsizer->Add(btnsizer, 0, wxEXPAND);
|
||||||
|
SetSizer(topsizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostQueueDialog::append_job(const PrintHostJob &job)
|
||||||
|
{
|
||||||
|
wxCHECK_RET(!job.empty(), "PrintHostQueueDialog: Attempt to append an empty job");
|
||||||
|
|
||||||
|
wxVector<wxVariant> fields;
|
||||||
|
fields.push_back(wxVariant(wxString::Format("%d", job_list->GetItemCount() + 1)));
|
||||||
|
fields.push_back(wxVariant(0));
|
||||||
|
fields.push_back(wxVariant(_(L("Enqueued"))));
|
||||||
|
fields.push_back(wxVariant(job.printhost->get_host()));
|
||||||
|
fields.push_back(wxVariant(job.upload_data.upload_path.string()));
|
||||||
|
job_list->AppendItem(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostQueueDialog::on_progress(Event &evt)
|
||||||
|
{
|
||||||
|
wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list");
|
||||||
|
|
||||||
|
const wxVariant status(evt.progress < 100 ? _(L("Uploading")) : _(L("Complete")));
|
||||||
|
|
||||||
|
job_list->SetValue(wxVariant(evt.progress), evt.job_id, 1);
|
||||||
|
job_list->SetValue(status, evt.job_id, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostQueueDialog::on_error(Event &evt)
|
||||||
|
{
|
||||||
|
wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list");
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
|
@ -2,24 +2,27 @@
|
||||||
#define slic3r_PrintHostSendDialog_hpp_
|
#define slic3r_PrintHostSendDialog_hpp_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
#include <wx/frame.h>
|
|
||||||
#include <wx/event.h>
|
#include <wx/event.h>
|
||||||
#include <wx/progdlg.h>
|
#include <wx/dialog.h>
|
||||||
#include <wx/sizer.h>
|
|
||||||
#include <wx/stattext.h>
|
|
||||||
#include <wx/textctrl.h>
|
|
||||||
#include <wx/checkbox.h>
|
|
||||||
|
|
||||||
#include "slic3r/GUI/GUI.hpp"
|
#include "GUI.hpp"
|
||||||
#include "slic3r/GUI/MsgDialog.hpp"
|
#include "GUI_Utils.hpp"
|
||||||
|
#include "MsgDialog.hpp"
|
||||||
|
#include "../Utils/PrintHost.hpp"
|
||||||
|
|
||||||
|
class wxTextCtrl;
|
||||||
|
class wxCheckBox;
|
||||||
|
class wxDataViewListCtrl;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
struct PrintHostJob;
|
||||||
|
|
||||||
|
namespace GUI {
|
||||||
|
|
||||||
|
|
||||||
class PrintHostSendDialog : public GUI::MsgDialog
|
class PrintHostSendDialog : public GUI::MsgDialog
|
||||||
{
|
{
|
||||||
|
@ -38,12 +41,38 @@ private:
|
||||||
class PrintHostQueueDialog : public wxDialog
|
class PrintHostQueueDialog : public wxDialog
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PrintHostQueueDialog();
|
class Event : public wxEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
size_t job_id;
|
||||||
|
int progress = 0; // in percent
|
||||||
|
wxString error;
|
||||||
|
|
||||||
|
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:
|
private:
|
||||||
|
wxDataViewListCtrl *job_list;
|
||||||
|
// Note: EventGuard prevents delivery of progress evts to a freed PrintHostQueueDialog
|
||||||
|
EventGuard on_progress_evt;
|
||||||
|
EventGuard on_error_evt;
|
||||||
|
|
||||||
|
void on_progress(Event&);
|
||||||
|
void on_error(Event&);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
wxDECLARE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||||
|
wxDECLARE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event);
|
||||||
|
|
||||||
}
|
|
||||||
|
}}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -953,6 +953,44 @@ void PrusaObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType&
|
||||||
type = itUndef;
|
type = itUndef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int PrusaObjectDataViewModel::GetRowByItem(const wxDataViewItem& item) const
|
||||||
|
{
|
||||||
|
if (m_objects.empty())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int row_num = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < m_objects.size(); i++)
|
||||||
|
{
|
||||||
|
row_num++;
|
||||||
|
if (item == wxDataViewItem(m_objects[i]))
|
||||||
|
return row_num;
|
||||||
|
|
||||||
|
for (int j = 0; j < m_objects[i]->GetChildCount(); j++)
|
||||||
|
{
|
||||||
|
row_num++;
|
||||||
|
PrusaObjectDataViewModelNode* cur_node = m_objects[i]->GetNthChild(j);
|
||||||
|
if (item == wxDataViewItem(cur_node))
|
||||||
|
return row_num;
|
||||||
|
|
||||||
|
if (cur_node->m_type == itVolume && cur_node->GetChildCount() == 1)
|
||||||
|
row_num++;
|
||||||
|
if (cur_node->m_type == itInstanceRoot)
|
||||||
|
{
|
||||||
|
row_num++;
|
||||||
|
for (int t = 0; t < cur_node->GetChildCount(); t++)
|
||||||
|
{
|
||||||
|
row_num++;
|
||||||
|
if (item == wxDataViewItem(cur_node->GetNthChild(t)))
|
||||||
|
return row_num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
wxString PrusaObjectDataViewModel::GetName(const wxDataViewItem &item) const
|
wxString PrusaObjectDataViewModel::GetName(const wxDataViewItem &item) const
|
||||||
{
|
{
|
||||||
PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
|
PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
|
||||||
|
|
|
@ -463,6 +463,7 @@ public:
|
||||||
int GetVolumeIdByItem(const wxDataViewItem& item) const;
|
int GetVolumeIdByItem(const wxDataViewItem& item) const;
|
||||||
int GetInstanceIdByItem(const wxDataViewItem& item) const;
|
int GetInstanceIdByItem(const wxDataViewItem& item) const;
|
||||||
void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx);
|
void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx);
|
||||||
|
int GetRowByItem(const wxDataViewItem& item) const;
|
||||||
bool IsEmpty() { return m_objects.empty(); }
|
bool IsEmpty() { return m_objects.empty(); }
|
||||||
|
|
||||||
// helper method for wxLog
|
// helper method for wxLog
|
||||||
|
@ -525,8 +526,14 @@ class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer
|
||||||
#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
#endif //ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PrusaBitmapTextRenderer(wxDataViewCellMode mode = wxDATAVIEW_CELL_EDITABLE,
|
PrusaBitmapTextRenderer(wxDataViewCellMode mode =
|
||||||
int align = wxDVR_DEFAULT_ALIGNMENT
|
#ifdef __WXOSX__
|
||||||
|
wxDATAVIEW_CELL_INERT
|
||||||
|
#else
|
||||||
|
wxDATAVIEW_CELL_EDITABLE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
,int align = wxDVR_DEFAULT_ALIGNMENT
|
||||||
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
#if ENABLE_NONCUSTOM_DATA_VIEW_RENDERING
|
||||||
);
|
);
|
||||||
#else
|
#else
|
||||||
|
@ -542,7 +549,14 @@ public:
|
||||||
virtual bool Render(wxRect cell, wxDC *dc, int state);
|
virtual bool Render(wxRect cell, wxDC *dc, int state);
|
||||||
virtual wxSize GetSize() const;
|
virtual wxSize GetSize() const;
|
||||||
|
|
||||||
bool HasEditorCtrl() const override { return true; }
|
bool HasEditorCtrl() const override
|
||||||
|
{
|
||||||
|
#ifdef __WXOSX__
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
wxWindow* CreateEditorCtrl(wxWindow* parent,
|
wxWindow* CreateEditorCtrl(wxWindow* parent,
|
||||||
wxRect labelRect,
|
wxRect labelRect,
|
||||||
const wxVariant& value) override;
|
const wxVariant& value) override;
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
#include "slic3r/GUI/GUI.hpp"
|
#include "slic3r/GUI/GUI.hpp"
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
#include "slic3r/GUI/MsgDialog.hpp"
|
#include "slic3r/GUI/MsgDialog.hpp"
|
||||||
#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX
|
|
||||||
#include "Http.hpp"
|
#include "Http.hpp"
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
|
@ -55,89 +54,90 @@ wxString Duet::get_test_failed_msg (wxString &msg) const
|
||||||
return wxString::Format("%s: %s", _(L("Could not connect to Duet")), msg);
|
return wxString::Format("%s: %s", _(L("Could not connect to Duet")), msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Duet::send_gcode(const std::string &filename) const
|
// bool Duet::send_gcode(const std::string &filename) const
|
||||||
{
|
// {
|
||||||
enum { PROGRESS_RANGE = 1000 };
|
// enum { PROGRESS_RANGE = 1000 };
|
||||||
|
|
||||||
const auto errortitle = _(L("Error while uploading to the Duet"));
|
// const auto errortitle = _(L("Error while uploading to the Duet"));
|
||||||
fs::path filepath(filename);
|
// fs::path filepath(filename);
|
||||||
|
|
||||||
PrintHostSendDialog send_dialog(filepath.filename());
|
// GUI::PrintHostSendDialog send_dialog(filepath.filename());
|
||||||
if (send_dialog.ShowModal() != wxID_OK) { return false; }
|
// if (send_dialog.ShowModal() != wxID_OK) { return false; }
|
||||||
|
|
||||||
const bool print = send_dialog.start_print();
|
// const bool print = send_dialog.start_print();
|
||||||
const auto upload_filepath = send_dialog.filename();
|
// const auto upload_filepath = send_dialog.filename();
|
||||||
const auto upload_filename = upload_filepath.filename();
|
// const auto upload_filename = upload_filepath.filename();
|
||||||
const auto upload_parent_path = upload_filepath.parent_path();
|
// const auto upload_parent_path = upload_filepath.parent_path();
|
||||||
|
|
||||||
wxProgressDialog progress_dialog(
|
// wxProgressDialog progress_dialog(
|
||||||
_(L("Duet upload")),
|
// _(L("Duet upload")),
|
||||||
_(L("Sending G-code file to Duet...")),
|
// _(L("Sending G-code file to Duet...")),
|
||||||
PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
|
// PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
|
||||||
progress_dialog.Pulse();
|
// progress_dialog.Pulse();
|
||||||
|
|
||||||
wxString connect_msg;
|
// wxString connect_msg;
|
||||||
if (!connect(connect_msg)) {
|
// if (!connect(connect_msg)) {
|
||||||
auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg);
|
// auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg);
|
||||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
bool res = true;
|
// bool res = true;
|
||||||
|
|
||||||
auto upload_cmd = get_upload_url(upload_filepath.string());
|
// auto upload_cmd = get_upload_url(upload_filepath.string());
|
||||||
BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%")
|
// BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%")
|
||||||
% filepath.string()
|
// % filepath.string()
|
||||||
% upload_filename.string()
|
// % upload_filename.string()
|
||||||
% upload_parent_path.string()
|
// % upload_parent_path.string()
|
||||||
% print
|
// % print
|
||||||
% upload_cmd;
|
// % upload_cmd;
|
||||||
|
|
||||||
auto http = Http::post(std::move(upload_cmd));
|
// auto http = Http::post(std::move(upload_cmd));
|
||||||
http.set_post_body(filename)
|
// http.set_post_body(filename)
|
||||||
.on_complete([&](std::string body, unsigned status) {
|
// .on_complete([&](std::string body, unsigned status) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body;
|
// BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body;
|
||||||
progress_dialog.Update(PROGRESS_RANGE);
|
// progress_dialog.Update(PROGRESS_RANGE);
|
||||||
|
|
||||||
int err_code = get_err_code_from_body(body);
|
// int err_code = get_err_code_from_body(body);
|
||||||
if (err_code != 0) {
|
// if (err_code != 0) {
|
||||||
auto msg = format_error(body, L("Unknown error occured"), 0);
|
// auto msg = format_error(body, L("Unknown error occured"), 0);
|
||||||
GUI::show_error(&progress_dialog, std::move(msg));
|
// GUI::show_error(&progress_dialog, std::move(msg));
|
||||||
res = false;
|
// res = false;
|
||||||
} else if (print) {
|
// } else if (print) {
|
||||||
wxString errormsg;
|
// wxString errormsg;
|
||||||
res = start_print(errormsg, upload_filepath.string());
|
// res = start_print(errormsg, upload_filepath.string());
|
||||||
if (!res) {
|
// if (!res) {
|
||||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
// .on_error([&](std::string body, std::string error, unsigned status) {
|
||||||
BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
|
// BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
|
||||||
auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status));
|
// auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status));
|
||||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||||
res = false;
|
// res = false;
|
||||||
})
|
// })
|
||||||
.on_progress([&](Http::Progress progress, bool &cancel) {
|
// .on_progress([&](Http::Progress progress, bool &cancel) {
|
||||||
if (cancel) {
|
// if (cancel) {
|
||||||
// Upload was canceled
|
// // Upload was canceled
|
||||||
res = false;
|
// res = false;
|
||||||
} else if (progress.ultotal > 0) {
|
// } else if (progress.ultotal > 0) {
|
||||||
int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal;
|
// int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal;
|
||||||
cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing
|
// cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing
|
||||||
} else {
|
// } else {
|
||||||
cancel = !progress_dialog.Pulse();
|
// cancel = !progress_dialog.Pulse();
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
.perform_sync();
|
// .perform_sync();
|
||||||
|
|
||||||
disconnect();
|
// disconnect();
|
||||||
|
|
||||||
return res;
|
// return res;
|
||||||
}
|
// }
|
||||||
|
|
||||||
bool Duet::upload(PrintHostUpload upload_data) const
|
bool Duet::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const
|
||||||
{
|
{
|
||||||
|
// XXX: TODO
|
||||||
throw "unimplemented";
|
throw "unimplemented";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,10 @@ public:
|
||||||
bool test(wxString &curl_msg) const;
|
bool test(wxString &curl_msg) const;
|
||||||
wxString get_test_ok_msg () const;
|
wxString get_test_ok_msg () const;
|
||||||
wxString get_test_failed_msg (wxString &msg) const;
|
wxString get_test_failed_msg (wxString &msg) const;
|
||||||
// Send gcode file to duet, filename is expected to be in UTF-8
|
bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const;
|
||||||
bool send_gcode(const std::string &filename) const;
|
|
||||||
bool upload(PrintHostUpload upload_data) const;
|
|
||||||
bool has_auto_discovery() const;
|
bool has_auto_discovery() const;
|
||||||
bool can_test() const;
|
bool can_test() const;
|
||||||
|
virtual std::string get_host() const { return host; }
|
||||||
private:
|
private:
|
||||||
std::string host;
|
std::string host;
|
||||||
std::string password;
|
std::string password;
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
|
|
||||||
typedef std::shared_ptr<Http> Ptr;
|
typedef std::shared_ptr<Http> Ptr;
|
||||||
typedef std::function<void(std::string /* body */, unsigned /* http_status */)> CompleteFn;
|
typedef std::function<void(std::string /* body */, unsigned /* http_status */)> CompleteFn;
|
||||||
|
|
||||||
// A HTTP request may fail at various stages of completeness (URL parsing, DNS lookup, TCP connection, ...).
|
// A HTTP request may fail at various stages of completeness (URL parsing, DNS lookup, TCP connection, ...).
|
||||||
// If the HTTP request could not be made or failed before completion, the `error` arg contains a description
|
// If the HTTP request could not be made or failed before completion, the `error` arg contains a description
|
||||||
// of the error and `http_status` is zero.
|
// of the error and `http_status` is zero.
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
#include <wx/progdlg.h>
|
||||||
|
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX
|
|
||||||
#include "Http.hpp"
|
#include "Http.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,32 +60,19 @@ wxString OctoPrint::get_test_failed_msg (wxString &msg) const
|
||||||
_(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required.")));
|
_(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OctoPrint::send_gcode(const std::string &filename) const
|
bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const
|
||||||
{
|
{
|
||||||
enum { PROGRESS_RANGE = 1000 };
|
const auto upload_filename = upload_data.upload_path.filename();
|
||||||
|
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||||
const auto errortitle = _(L("Error while uploading to the OctoPrint server"));
|
|
||||||
fs::path filepath(filename);
|
|
||||||
|
|
||||||
PrintHostSendDialog send_dialog(filepath.filename());
|
|
||||||
if (send_dialog.ShowModal() != wxID_OK) { return false; }
|
|
||||||
|
|
||||||
const bool print = send_dialog.start_print();
|
|
||||||
const auto upload_filepath = send_dialog.filename();
|
|
||||||
const auto upload_filename = upload_filepath.filename();
|
|
||||||
const auto upload_parent_path = upload_filepath.parent_path();
|
|
||||||
|
|
||||||
wxProgressDialog progress_dialog(
|
|
||||||
_(L("OctoPrint upload")),
|
|
||||||
_(L("Sending G-code file to the OctoPrint server...")),
|
|
||||||
PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
|
|
||||||
progress_dialog.Pulse();
|
|
||||||
|
|
||||||
wxString test_msg;
|
wxString test_msg;
|
||||||
if (!test(test_msg)) {
|
if (! test(test_msg)) {
|
||||||
auto errormsg = wxString::Format("%s: %s", errortitle, test_msg);
|
|
||||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
// TODO:
|
||||||
return false;
|
|
||||||
|
// auto errormsg = wxString::Format("%s: %s", errortitle, test_msg);
|
||||||
|
// GUI::show_error(&progress_dialog, std::move(errormsg));
|
||||||
|
// return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool res = true;
|
bool res = true;
|
||||||
|
@ -92,36 +80,31 @@ bool OctoPrint::send_gcode(const std::string &filename) const
|
||||||
auto url = make_url("api/files/local");
|
auto url = make_url("api/files/local");
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%")
|
BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%")
|
||||||
% filepath.string()
|
% upload_data.source_path.string()
|
||||||
% url
|
% url
|
||||||
% upload_filename.string()
|
% upload_filename.string()
|
||||||
% upload_parent_path.string()
|
% upload_parent_path.string()
|
||||||
% print;
|
% upload_data.start_print;
|
||||||
|
|
||||||
auto http = Http::post(std::move(url));
|
auto http = Http::post(std::move(url));
|
||||||
set_auth(http);
|
set_auth(http);
|
||||||
http.form_add("print", print ? "true" : "false")
|
http.form_add("print", upload_data.start_print ? "true" : "false")
|
||||||
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
||||||
.form_add_file("file", filename, upload_filename.string())
|
.form_add_file("file", upload_data.source_path.string(), upload_filename.string())
|
||||||
.on_complete([&](std::string body, unsigned status) {
|
.on_complete([&](std::string body, unsigned status) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body;
|
BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body;
|
||||||
progress_dialog.Update(PROGRESS_RANGE);
|
|
||||||
})
|
})
|
||||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||||
BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
|
BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body;
|
||||||
auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status));
|
error_fn(std::move(body), std::move(error), status);
|
||||||
GUI::show_error(&progress_dialog, std::move(errormsg));
|
|
||||||
res = false;
|
res = false;
|
||||||
})
|
})
|
||||||
.on_progress([&](Http::Progress progress, bool &cancel) {
|
.on_progress([&](Http::Progress progress, bool &cancel) {
|
||||||
|
prorgess_fn(std::move(progress), cancel);
|
||||||
if (cancel) {
|
if (cancel) {
|
||||||
// Upload was canceled
|
// Upload was canceled
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Octoprint: Upload canceled";
|
||||||
res = false;
|
res = false;
|
||||||
} else if (progress.ultotal > 0) {
|
|
||||||
int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal;
|
|
||||||
cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing
|
|
||||||
} else {
|
|
||||||
cancel = !progress_dialog.Pulse();
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.perform_sync();
|
.perform_sync();
|
||||||
|
@ -129,11 +112,6 @@ bool OctoPrint::send_gcode(const std::string &filename) const
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OctoPrint::upload(PrintHostUpload upload_data) const
|
|
||||||
{
|
|
||||||
throw "unimplemented";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OctoPrint::has_auto_discovery() const
|
bool OctoPrint::has_auto_discovery() const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -22,11 +22,10 @@ public:
|
||||||
bool test(wxString &curl_msg) const;
|
bool test(wxString &curl_msg) const;
|
||||||
wxString get_test_ok_msg () const;
|
wxString get_test_ok_msg () const;
|
||||||
wxString get_test_failed_msg (wxString &msg) const;
|
wxString get_test_failed_msg (wxString &msg) const;
|
||||||
// Send gcode file to octoprint, filename is expected to be in UTF-8
|
bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const;
|
||||||
bool send_gcode(const std::string &filename) const;
|
|
||||||
bool upload(PrintHostUpload upload_data) const;
|
|
||||||
bool has_auto_discovery() const;
|
bool has_auto_discovery() const;
|
||||||
bool can_test() const;
|
bool can_test() const;
|
||||||
|
virtual std::string get_host() const { return host; }
|
||||||
private:
|
private:
|
||||||
std::string host;
|
std::string host;
|
||||||
std::string apikey;
|
std::string apikey;
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
#include "OctoPrint.hpp"
|
#include "PrintHost.hpp"
|
||||||
#include "Duet.hpp"
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
|
#include <wx/app.h>
|
||||||
|
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
#include "libslic3r/Channel.hpp"
|
#include "libslic3r/Channel.hpp"
|
||||||
|
#include "OctoPrint.hpp"
|
||||||
|
#include "Duet.hpp"
|
||||||
|
#include "../GUI/PrintHostDialogs.hpp"
|
||||||
|
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
using boost::optional;
|
using boost::optional;
|
||||||
|
using Slic3r::GUI::PrintHostQueueDialog;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -30,30 +36,130 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
|
||||||
|
|
||||||
struct PrintHostJobQueue::priv
|
struct PrintHostJobQueue::priv
|
||||||
{
|
{
|
||||||
std::vector<PrintHostJob> jobs;
|
// XXX: comment on how bg thread works
|
||||||
Channel<unsigned> channel;
|
|
||||||
|
PrintHostJobQueue *q;
|
||||||
|
|
||||||
|
Channel<PrintHostJob> channel_jobs;
|
||||||
|
Channel<size_t> channel_cancels;
|
||||||
|
size_t job_id = 0;
|
||||||
|
int prev_progress = -1;
|
||||||
|
|
||||||
std::thread bg_thread;
|
std::thread bg_thread;
|
||||||
optional<PrintHostJob> bg_job;
|
bool bg_exit = false;
|
||||||
|
|
||||||
|
PrintHostQueueDialog *queue_dialog;
|
||||||
|
|
||||||
|
priv(PrintHostJobQueue *q) : q(q) {}
|
||||||
|
|
||||||
|
void start_bg_thread();
|
||||||
|
void bg_thread_main();
|
||||||
|
void progress_fn(Http::Progress progress, bool &cancel);
|
||||||
|
void error_fn(std::string body, std::string error, unsigned http_status);
|
||||||
|
void perform_job(PrintHostJob the_job);
|
||||||
};
|
};
|
||||||
|
|
||||||
PrintHostJobQueue::PrintHostJobQueue()
|
PrintHostJobQueue::PrintHostJobQueue(PrintHostQueueDialog *queue_dialog)
|
||||||
: p(new priv())
|
: p(new priv(this))
|
||||||
{
|
{
|
||||||
std::shared_ptr<priv> p2 = p;
|
p->queue_dialog = queue_dialog;
|
||||||
p->bg_thread = std::thread([p2]() {
|
|
||||||
// Wait for commands on the channel:
|
|
||||||
auto cmd = p2->channel.pop();
|
|
||||||
// TODO
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintHostJobQueue::~PrintHostJobQueue()
|
PrintHostJobQueue::~PrintHostJobQueue()
|
||||||
{
|
{
|
||||||
// TODO: stop the thread
|
if (p && p->bg_thread.joinable()) {
|
||||||
// if (p && p->bg_thread.joinable()) {
|
p->bg_exit = true;
|
||||||
// p->bg_thread.detach();
|
p->channel_jobs.push(PrintHostJob()); // Push an empty job to wake up bg_thread in case it's sleeping
|
||||||
// }
|
p->bg_thread.detach(); // Let the background thread go, it should exit on its own
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::priv::start_bg_thread()
|
||||||
|
{
|
||||||
|
if (bg_thread.joinable()) { return; }
|
||||||
|
|
||||||
|
std::shared_ptr<priv> p2 = q->p;
|
||||||
|
bg_thread = std::thread([p2]() {
|
||||||
|
p2->bg_thread_main();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::priv::bg_thread_main()
|
||||||
|
{
|
||||||
|
// bg thread entry point
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Pick up jobs from the job channel:
|
||||||
|
while (! bg_exit) {
|
||||||
|
auto job = channel_jobs.pop(); // Sleeps in a cond var if there are no jobs
|
||||||
|
if (! job.cancelled) {
|
||||||
|
perform_job(std::move(job));
|
||||||
|
}
|
||||||
|
job_id++;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
wxTheApp->OnUnhandledException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel)
|
||||||
|
{
|
||||||
|
if (bg_exit) {
|
||||||
|
cancel = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channel_cancels.size_hint() > 0) {
|
||||||
|
// Lock both queues
|
||||||
|
auto cancels = channel_cancels.lock_rw();
|
||||||
|
auto jobs = channel_jobs.lock_rw();
|
||||||
|
|
||||||
|
for (size_t cancel_id : *cancels) {
|
||||||
|
if (cancel_id == job_id) {
|
||||||
|
cancel = true;
|
||||||
|
} else if (cancel_id > job_id) {
|
||||||
|
jobs->at(cancel_id - job_id).cancelled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancels->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
int gui_progress = progress.ultotal > 0 ? 100*progress.ulnow / progress.ultotal : 0;
|
||||||
|
if (gui_progress != prev_progress) {
|
||||||
|
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, gui_progress);
|
||||||
|
wxQueueEvent(queue_dialog, evt);
|
||||||
|
prev_progress = gui_progress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::priv::error_fn(std::string body, std::string error, unsigned http_status)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job)
|
||||||
|
{
|
||||||
|
if (bg_exit || the_job.empty()) { return; }
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
fs::remove(gcode_path); // XXX: error handling
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHostJobQueue::enqueue(PrintHostJob job)
|
||||||
|
{
|
||||||
|
p->start_bg_thread();
|
||||||
|
p->queue_dialog->append_job(job);
|
||||||
|
p->channel_jobs.push(std::move(job));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
|
|
||||||
|
#include "Http.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -29,11 +31,10 @@ public:
|
||||||
virtual bool test(wxString &curl_msg) const = 0;
|
virtual bool test(wxString &curl_msg) const = 0;
|
||||||
virtual wxString get_test_ok_msg () const = 0;
|
virtual wxString get_test_ok_msg () const = 0;
|
||||||
virtual wxString get_test_failed_msg (wxString &msg) const = 0;
|
virtual wxString get_test_failed_msg (wxString &msg) const = 0;
|
||||||
// Send gcode file to print host, filename is expected to be in UTF-8
|
virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const = 0;
|
||||||
virtual bool send_gcode(const std::string &filename) const = 0; // XXX: remove in favor of upload()
|
|
||||||
virtual bool upload(PrintHostUpload upload_data) const = 0;
|
|
||||||
virtual bool has_auto_discovery() const = 0;
|
virtual bool has_auto_discovery() const = 0;
|
||||||
virtual bool can_test() const = 0;
|
virtual bool can_test() const = 0;
|
||||||
|
virtual std::string get_host() const = 0;
|
||||||
|
|
||||||
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
||||||
};
|
};
|
||||||
|
@ -43,6 +44,7 @@ struct PrintHostJob
|
||||||
{
|
{
|
||||||
PrintHostUpload upload_data;
|
PrintHostUpload upload_data;
|
||||||
std::unique_ptr<PrintHost> printhost;
|
std::unique_ptr<PrintHost> printhost;
|
||||||
|
bool cancelled = false;
|
||||||
|
|
||||||
PrintHostJob() {}
|
PrintHostJob() {}
|
||||||
PrintHostJob(const PrintHostJob&) = delete;
|
PrintHostJob(const PrintHostJob&) = delete;
|
||||||
|
@ -68,10 +70,12 @@ struct PrintHostJob
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace GUI { class PrintHostQueueDialog; }
|
||||||
|
|
||||||
class PrintHostJobQueue
|
class PrintHostJobQueue
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PrintHostJobQueue();
|
PrintHostJobQueue(GUI::PrintHostQueueDialog *queue_dialog);
|
||||||
PrintHostJobQueue(const PrintHostJobQueue &) = delete;
|
PrintHostJobQueue(const PrintHostJobQueue &) = delete;
|
||||||
PrintHostJobQueue(PrintHostJobQueue &&other) = delete;
|
PrintHostJobQueue(PrintHostJobQueue &&other) = delete;
|
||||||
~PrintHostJobQueue();
|
~PrintHostJobQueue();
|
||||||
|
@ -79,6 +83,9 @@ public:
|
||||||
PrintHostJobQueue& operator=(const PrintHostJobQueue &) = delete;
|
PrintHostJobQueue& operator=(const PrintHostJobQueue &) = delete;
|
||||||
PrintHostJobQueue& operator=(PrintHostJobQueue &&other) = delete;
|
PrintHostJobQueue& operator=(PrintHostJobQueue &&other) = delete;
|
||||||
|
|
||||||
|
void enqueue(PrintHostJob job);
|
||||||
|
void cancel(size_t id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct priv;
|
struct priv;
|
||||||
std::shared_ptr<priv> p;
|
std::shared_ptr<priv> p;
|
||||||
|
|
Loading…
Reference in a new issue