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

This commit is contained in:
supermerill 2019-01-29 17:55:24 +01:00
commit a76bb026df
60 changed files with 2054 additions and 612 deletions

View File

@ -85,6 +85,12 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
if(NOT WIN32)
# Add DEBUG flags for the debug builds in a way the Visual Studio adds these flags.
add_compile_options("$<$<CONFIG:DEBUG>:-DDEBUG>")
add_compile_options("$<$<CONFIG:DEBUG>:-D_DEBUG>")
endif()
# To be able to link libslic3r with the Perl XS module.
# Once we get rid of Perl and libslic3r is linked statically, we can get rid of -fPIC
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
@ -169,6 +175,11 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STRE
endif ()
endif()
if (APPLE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=partial-availability -Werror=unguarded-availability -Werror=unguarded-availability-new")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=partial-availability -Werror=unguarded-availability -Werror=unguarded-availability-new")
endif ()
# Where all the bundled libraries reside?
set(LIBDIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
set(LIBDIR_BIN ${CMAKE_CURRENT_BINARY_DIR}/src)

View File

@ -1,8 +1,21 @@
if (MSVC_VERSION EQUAL 1800)
set(DEP_VS_VER "12")
set(DEP_BOOST_TOOLSET "msvc-12.0")
elseif (MSVC_VERSION EQUAL 1900)
set(DEP_VS_VER "14")
set(DEP_BOOST_TOOLSET "msvc-14.0")
elseif (MSVC_VERSION GREATER 1900)
set(DEP_VS_VER "15")
set(DEP_BOOST_TOOLSET "msvc-14.1")
else ()
message(FATAL_ERROR "Unsupported MSVC version")
endif ()
if (${DEPS_BITS} EQUAL 32)
set(DEP_MSVC_GEN "Visual Studio 12")
set(DEP_MSVC_GEN "Visual Studio ${DEP_VS_VER}")
else ()
set(DEP_MSVC_GEN "Visual Studio 12 Win64")
set(DEP_MSVC_GEN "Visual Studio ${DEP_VS_VER} Win64")
endif ()
@ -29,7 +42,7 @@ ExternalProject_Add(dep_boost
--with-regex
"--prefix=${DESTDIR}/usr/local"
"address-model=${DEPS_BITS}"
toolset=msvc-12.0
"toolset=${DEP_BOOST_TOOLSET}"
link=static
variant=release
threading=multi
@ -204,7 +217,7 @@ ExternalProject_Add(dep_libcurl
URL_HASH SHA256=cc245bf9a1a42a45df491501d97d5593392a03f7b4f07b952793518d97666115
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND ""
BUILD_COMMAND cd winbuild && nmake /f Makefile.vc mode=static VC=12 GEN_PDB=yes DEBUG=no "MACHINE=${DEP_LIBCURL_TARGET}"
BUILD_COMMAND cd winbuild && nmake /f Makefile.vc mode=static "VC=${DEP_VS_VER}" GEN_PDB=yes DEBUG=no "MACHINE=${DEP_LIBCURL_TARGET}"
INSTALL_COMMAND cd builds\\libcurl-*-release-*-winssl
&& "${CMAKE_COMMAND}" -E copy_directory include "${DESTDIR}\\usr\\local\\include"
&& "${CMAKE_COMMAND}" -E copy_directory lib "${DESTDIR}\\usr\\local\\lib"
@ -214,7 +227,7 @@ if (${DEP_DEBUG})
ExternalProject_Add_Step(dep_libcurl build_debug
DEPENDEES build
DEPENDERS install
COMMAND cd winbuild && nmake /f Makefile.vc mode=static VC=12 GEN_PDB=yes DEBUG=yes "MACHINE=${DEP_LIBCURL_TARGET}"
COMMAND cd winbuild && nmake /f Makefile.vc mode=static "VC=${DEP_VS_VER}" GEN_PDB=yes DEBUG=yes "MACHINE=${DEP_LIBCURL_TARGET}"
WORKING_DIRECTORY "${SOURCE_DIR}"
)
ExternalProject_Add_Step(dep_libcurl install_debug

Binary file not shown.

View File

@ -45,7 +45,9 @@ extern "C" {
#define chdir _chdir
#define isatty _isatty
#define lseek _lseek
#if _MSC_VER < 1900
#define snprintf _snprintf
#endif
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#define stat _stat

View File

@ -71,6 +71,11 @@ if(TBB_FOUND)
target_compile_definitions(libnest2d INTERFACE -DTBB_USE_CAPTURED_EXCEPTION=0)
target_link_libraries(libnest2d INTERFACE tbb)
# The following breaks compilation on Visual Studio in Debug mode.
#find_package(Threads REQUIRED)
#target_link_libraries(libnest2d INTERFACE ${TBB_LIBRARIES} ${CMAKE_DL_LIBS}
# Threads::Threads
# )
else()
find_package(OpenMP QUIET)
@ -88,7 +93,7 @@ endif()
add_subdirectory(${SRC_DIR}/libnest2d/backends/${LIBNEST2D_GEOMETRIES})
add_subdirectory(${SRC_DIR}/libnest2d/optimizers/${LIBNEST2D_OPTIMIZER})
#target_sources(libnest2d INTERFACE ${LIBNEST2D_SRCFILES})
target_sources(libnest2d INTERFACE ${LIBNEST2D_SRCFILES})
target_include_directories(libnest2d INTERFACE ${SRC_DIR})
if(NOT LIBNEST2D_HEADER_ONLY)

View File

@ -62,9 +62,9 @@ if(NOT Boost_INCLUDE_DIRS_FOUND)
endif()
target_include_directories(ClipperBackend INTERFACE ${Boost_INCLUDE_DIRS} )
#target_sources(ClipperBackend INTERFACE
# ${CMAKE_CURRENT_SOURCE_DIR}/geometries.hpp
# ${SRC_DIR}/libnest2d/utils/boost_alg.hpp )
target_sources(ClipperBackend INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/geometries.hpp
${SRC_DIR}/libnest2d/utils/boost_alg.hpp )
target_compile_definitions(ClipperBackend INTERFACE LIBNEST2D_BACKEND_CLIPPER)

View File

@ -113,6 +113,7 @@ template<> struct CountourType<PolygonImpl> {
template<> struct ShapeTag<PolygonImpl> { using Type = PolygonTag; };
template<> struct ShapeTag<PathImpl> { using Type = PathTag; };
template<> struct ShapeTag<PointImpl> { using Type = PointTag; };
template<> struct ShapeTag<TMultiShape<PolygonImpl>> {
using Type = MultiPolygonTag;

View File

@ -69,12 +69,14 @@ struct PointPair {
RawPoint p2;
};
struct PointTag {};
struct PolygonTag {};
struct PathTag {};
struct MultiPolygonTag {};
struct BoxTag {};
struct CircleTag {};
/// Meta-functions to derive the tags
template<class Shape> struct ShapeTag { using Type = typename Shape::Tag; };
template<class S> using Tag = typename ShapeTag<remove_cvref_t<S>>::Type;
@ -131,7 +133,7 @@ public:
_Circle(const RawPoint& center, double r): center_(center), radius_(r) {}
inline const RawPoint& center() const BP2D_NOEXCEPT { return center_; }
inline const void center(const RawPoint& c) { center_ = c; }
inline void center(const RawPoint& c) { center_ = c; }
inline double radius() const BP2D_NOEXCEPT { return radius_; }
inline void radius(double r) { radius_ = r; }
@ -518,21 +520,19 @@ inline bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/)
return false;
}
template<class RawShape>
inline bool isInside(const TPoint<RawShape>& /*point*/,
const RawShape& /*shape*/)
{
static_assert(always_false<RawShape>::value,
"shapelike::isInside(point, shape) unimplemented!");
template<class TGuest, class THost>
inline bool isInside(const TGuest&, const THost&,
const PointTag&, const PolygonTag&) {
static_assert(always_false<THost>::value,
"shapelike::isInside(point, path) unimplemented!");
return false;
}
template<class RawShape>
inline bool isInside(const RawShape& /*shape*/,
const RawShape& /*shape*/)
{
static_assert(always_false<RawShape>::value,
"shapelike::isInside(shape, shape) unimplemented!");
template<class TGuest, class THost>
inline bool isInside(const TGuest&, const THost&,
const PolygonTag&, const PolygonTag&) {
static_assert(always_false<THost>::value,
"shapelike::isInside(shape, shape) unimplemented!");
return false;
}
@ -651,7 +651,7 @@ template<class RawPath> inline bool isConvex(const RawPath& sh, const PathTag&)
template<class RawShape>
inline typename TContour<RawShape>::iterator
begin(RawShape& sh, const PolygonTag& t)
begin(RawShape& sh, const PolygonTag&)
{
return begin(contour(sh), PathTag());
}
@ -818,16 +818,16 @@ inline auto convexHull(const RawShape& sh)
return convexHull(sh, Tag<RawShape>());
}
template<class RawShape>
inline bool isInside(const TPoint<RawShape>& point,
const _Circle<TPoint<RawShape>>& circ)
template<class TP, class TC>
inline bool isInside(const TP& point, const TC& circ,
const PointTag&, const CircleTag&)
{
return pointlike::distance(point, circ.center()) < circ.radius();
}
template<class RawShape>
inline bool isInside(const TPoint<RawShape>& point,
const _Box<TPoint<RawShape>>& box)
template<class TP, class TB>
inline bool isInside(const TP& point, const TB& box,
const PointTag&, const BoxTag&)
{
auto px = getX(point);
auto py = getY(point);
@ -839,27 +839,27 @@ inline bool isInside(const TPoint<RawShape>& point,
return px > minx && px < maxx && py > miny && py < maxy;
}
template<class RawShape>
inline bool isInside(const RawShape& sh,
const _Circle<TPoint<RawShape>>& circ)
template<class RawShape, class TC>
inline bool isInside(const RawShape& sh, const TC& circ,
const PolygonTag&, const CircleTag&)
{
return std::all_of(cbegin(sh), cend(sh),
[&circ](const TPoint<RawShape>& p){
return isInside<RawShape>(p, circ);
return std::all_of(cbegin(sh), cend(sh), [&circ](const TPoint<RawShape>& p)
{
return isInside(p, circ, PointTag(), CircleTag());
});
}
template<class RawShape>
inline bool isInside(const _Box<TPoint<RawShape>>& box,
const _Circle<TPoint<RawShape>>& circ)
template<class TB, class TC>
inline bool isInside(const TB& box, const TC& circ,
const BoxTag&, const CircleTag&)
{
return isInside<RawShape>(box.minCorner(), circ) &&
isInside<RawShape>(box.maxCorner(), circ);
return isInside(box.minCorner(), circ, BoxTag(), CircleTag()) &&
isInside(box.maxCorner(), circ, BoxTag(), CircleTag());
}
template<class RawShape>
inline bool isInside(const _Box<TPoint<RawShape>>& ibb,
const _Box<TPoint<RawShape>>& box)
template<class TBGuest, class TBHost>
inline bool isInside(const TBGuest& ibb, const TBHost& box,
const BoxTag&, const BoxTag&)
{
auto iminX = getX(ibb.minCorner());
auto imaxX = getX(ibb.maxCorner());
@ -874,6 +874,18 @@ inline bool isInside(const _Box<TPoint<RawShape>>& ibb,
return iminX > minX && imaxX < maxX && iminY > minY && imaxY < maxY;
}
template<class RawShape, class TB>
inline bool isInside(const RawShape& poly, const TB& box,
const PolygonTag&, const BoxTag&)
{
return isInside(boundingBox(poly), box, BoxTag(), BoxTag());
}
template<class TGuest, class THost>
inline bool isInside(const TGuest& guest, const THost& host) {
return isInside(guest, host, Tag<TGuest>(), Tag<THost>());
}
template<class RawShape> // Potential O(1) implementation may exist
inline TPoint<RawShape>& vertex(RawShape& sh, unsigned long idx,
const PolygonTag&)

View File

@ -251,6 +251,460 @@ inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
return {rsh, top_nfp};
}
template<class RawShape>
NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
const RawShape& cother)
{
// Algorithms are from the original algorithm proposed in paper:
// https://eprints.soton.ac.uk/36850/1/CORMSIS-05-05.pdf
// /////////////////////////////////////////////////////////////////////////
// Algorithm 1: Obtaining the minkowski sum
// /////////////////////////////////////////////////////////////////////////
// I guess this is not a full minkowski sum of the two input polygons by
// definition. This yields a subset that is compatible with the next 2
// algorithms.
using Result = NfpResult<RawShape>;
using Vertex = TPoint<RawShape>;
using Coord = TCoord<Vertex>;
using Edge = _Segment<Vertex>;
namespace sl = shapelike;
using std::signbit;
using std::sort;
using std::vector;
using std::ref;
using std::reference_wrapper;
// TODO The original algorithms expects the stationary polygon in
// counter clockwise and the orbiter in clockwise order.
// So for preventing any further complication, I will make the input
// the way it should be, than make my way around the orientations.
// Reverse the stationary contour to counter clockwise
auto stcont = sl::contour(cstationary);
{
std::reverse(sl::begin(stcont), sl::end(stcont));
stcont.pop_back();
auto it = std::min_element(sl::begin(stcont), sl::end(stcont),
[](const Vertex& v1, const Vertex& v2) {
return getY(v1) < getY(v2);
});
std::rotate(sl::begin(stcont), it, sl::end(stcont));
sl::addVertex(stcont, sl::front(stcont));
}
RawShape stationary;
sl::contour(stationary) = stcont;
// Reverse the orbiter contour to counter clockwise
auto orbcont = sl::contour(cother);
{
std::reverse(orbcont.begin(), orbcont.end());
// Step 1: Make the orbiter reverse oriented
orbcont.pop_back();
auto it = std::min_element(orbcont.begin(), orbcont.end(),
[](const Vertex& v1, const Vertex& v2) {
return getY(v1) < getY(v2);
});
std::rotate(orbcont.begin(), it, orbcont.end());
orbcont.emplace_back(orbcont.front());
for(auto &v : orbcont) v = -v;
}
// Copy the orbiter (contour only), we will have to work on it
RawShape orbiter;
sl::contour(orbiter) = orbcont;
// An edge with additional data for marking it
struct MarkedEdge {
Edge e; Radians turn_angle = 0; bool is_turning_point = false;
MarkedEdge() = default;
MarkedEdge(const Edge& ed, Radians ta, bool tp):
e(ed), turn_angle(ta), is_turning_point(tp) {}
// debug
std::string label;
};
// Container for marked edges
using EdgeList = vector<MarkedEdge>;
EdgeList A, B;
// This is how an edge list is created from the polygons
auto fillEdgeList = [](EdgeList& L, const RawShape& ppoly, int dir) {
auto& poly = sl::contour(ppoly);
L.reserve(sl::contourVertexCount(poly));
if(dir > 0) {
auto it = poly.begin();
auto nextit = std::next(it);
double turn_angle = 0;
bool is_turn_point = false;
while(nextit != poly.end()) {
L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point);
it++; nextit++;
}
} else {
auto it = sl::rbegin(poly);
auto nextit = std::next(it);
double turn_angle = 0;
bool is_turn_point = false;
while(nextit != sl::rend(poly)) {
L.emplace_back(Edge(*it, *nextit), turn_angle, is_turn_point);
it++; nextit++;
}
}
auto getTurnAngle = [](const Edge& e1, const Edge& e2) {
auto phi = e1.angleToXaxis();
auto phi_prev = e2.angleToXaxis();
auto turn_angle = phi-phi_prev;
if(turn_angle > Pi) turn_angle -= TwoPi;
if(turn_angle < -Pi) turn_angle += TwoPi;
return turn_angle;
};
auto eit = L.begin();
auto enext = std::next(eit);
eit->turn_angle = getTurnAngle(L.front().e, L.back().e);
while(enext != L.end()) {
enext->turn_angle = getTurnAngle( enext->e, eit->e);
eit->is_turning_point =
signbit(enext->turn_angle) != signbit(eit->turn_angle);
++eit; ++enext;
}
L.back().is_turning_point = signbit(L.back().turn_angle) !=
signbit(L.front().turn_angle);
};
// Step 2: Fill the edgelists
fillEdgeList(A, stationary, 1);
fillEdgeList(B, orbiter, 1);
int i = 1;
for(MarkedEdge& me : A) {
std::cout << "a" << i << ":\n\t"
<< getX(me.e.first()) << " " << getY(me.e.first()) << "\n\t"
<< getX(me.e.second()) << " " << getY(me.e.second()) << "\n\t"
<< "Turning point: " << (me.is_turning_point ? "yes" : "no")
<< std::endl;
me.label = "a"; me.label += std::to_string(i);
i++;
}
i = 1;
for(MarkedEdge& me : B) {
std::cout << "b" << i << ":\n\t"
<< getX(me.e.first()) << " " << getY(me.e.first()) << "\n\t"
<< getX(me.e.second()) << " " << getY(me.e.second()) << "\n\t"
<< "Turning point: " << (me.is_turning_point ? "yes" : "no")
<< std::endl;
me.label = "b"; me.label += std::to_string(i);
i++;
}
// A reference to a marked edge that also knows its container
struct MarkedEdgeRef {
reference_wrapper<MarkedEdge> eref;
reference_wrapper<vector<MarkedEdgeRef>> container;
Coord dir = 1; // Direction modifier
inline Radians angleX() const { return eref.get().e.angleToXaxis(); }
inline const Edge& edge() const { return eref.get().e; }
inline Edge& edge() { return eref.get().e; }
inline bool isTurningPoint() const {
return eref.get().is_turning_point;
}
inline bool isFrom(const vector<MarkedEdgeRef>& cont ) {
return &(container.get()) == &cont;
}
inline bool eq(const MarkedEdgeRef& mr) {
return &(eref.get()) == &(mr.eref.get());
}
MarkedEdgeRef(reference_wrapper<MarkedEdge> er,
reference_wrapper<vector<MarkedEdgeRef>> ec):
eref(er), container(ec), dir(1) {}
MarkedEdgeRef(reference_wrapper<MarkedEdge> er,
reference_wrapper<vector<MarkedEdgeRef>> ec,
Coord d):
eref(er), container(ec), dir(d) {}
};
using EdgeRefList = vector<MarkedEdgeRef>;
// Comparing two marked edges
auto sortfn = [](const MarkedEdgeRef& e1, const MarkedEdgeRef& e2) {
return e1.angleX() < e2.angleX();
};
EdgeRefList Aref, Bref; // We create containers for the references
Aref.reserve(A.size()); Bref.reserve(B.size());
// Fill reference container for the stationary polygon
std::for_each(A.begin(), A.end(), [&Aref](MarkedEdge& me) {
Aref.emplace_back( ref(me), ref(Aref) );
});
// Fill reference container for the orbiting polygon
std::for_each(B.begin(), B.end(), [&Bref](MarkedEdge& me) {
Bref.emplace_back( ref(me), ref(Bref) );
});
auto mink = [sortfn] // the Mink(Q, R, direction) sub-procedure
(const EdgeRefList& Q, const EdgeRefList& R, bool positive)
{
// Step 1 "merge sort_list(Q) and sort_list(R) to form merge_list(Q,R)"
// Sort the containers of edge references and merge them.
// Q could be sorted only once and be reused here but we would still
// need to merge it with sorted(R).
EdgeRefList merged;
EdgeRefList S, seq;
merged.reserve(Q.size() + R.size());
merged.insert(merged.end(), R.begin(), R.end());
std::stable_sort(merged.begin(), merged.end(), sortfn);
merged.insert(merged.end(), Q.begin(), Q.end());
std::stable_sort(merged.begin(), merged.end(), sortfn);
// Step 2 "set i = 1, k = 1, direction = 1, s1 = q1"
// we don't use i, instead, q is an iterator into Q. k would be an index
// into the merged sequence but we use "it" as an iterator for that
// here we obtain references for the containers for later comparisons
const auto& Rcont = R.begin()->container.get();
const auto& Qcont = Q.begin()->container.get();
// Set the initial direction
Coord dir = 1;
// roughly i = 1 (so q = Q.begin()) and s1 = q1 so S[0] = q;
if(positive) {
auto q = Q.begin();
S.emplace_back(*q);
// Roughly step 3
std::cout << "merged size: " << merged.size() << std::endl;
auto mit = merged.begin();
for(bool finish = false; !finish && q != Q.end();) {
++q; // "Set i = i + 1"
while(!finish && mit != merged.end()) {
if(mit->isFrom(Rcont)) {
auto s = *mit;
s.dir = dir;
S.emplace_back(s);
}
if(mit->eq(*q)) {
S.emplace_back(*q);
if(mit->isTurningPoint()) dir = -dir;
if(q == Q.begin()) finish = true;
break;
}
mit += dir;
// __nfp::advance(mit, merged, dir > 0);
}
}
} else {
auto q = Q.rbegin();
S.emplace_back(*q);
// Roughly step 3
std::cout << "merged size: " << merged.size() << std::endl;
auto mit = merged.begin();
for(bool finish = false; !finish && q != Q.rend();) {
++q; // "Set i = i + 1"
while(!finish && mit != merged.end()) {
if(mit->isFrom(Rcont)) {
auto s = *mit;
s.dir = dir;
S.emplace_back(s);
}
if(mit->eq(*q)) {
S.emplace_back(*q);
S.back().dir = -1;
if(mit->isTurningPoint()) dir = -dir;
if(q == Q.rbegin()) finish = true;
break;
}
mit += dir;
// __nfp::advance(mit, merged, dir > 0);
}
}
}
// Step 4:
// "Let starting edge r1 be in position si in sequence"
// whaaat? I guess this means the following:
auto it = S.begin();
while(!it->eq(*R.begin())) ++it;
// "Set j = 1, next = 2, direction = 1, seq1 = si"
// we don't use j, seq is expanded dynamically.
dir = 1;
auto next = std::next(R.begin()); seq.emplace_back(*it);
// Step 5:
// "If all si edges have been allocated to seqj" should mean that
// we loop until seq has equal size with S
auto send = it; //it == S.begin() ? it : std::prev(it);
while(it != S.end()) {
++it; if(it == S.end()) it = S.begin();
if(it == send) break;
if(it->isFrom(Qcont)) {
seq.emplace_back(*it); // "If si is from Q, j = j + 1, seqj = si"
// "If si is a turning point in Q,
// direction = - direction, next = next + direction"
if(it->isTurningPoint()) {
dir = -dir;
next += dir;
// __nfp::advance(next, R, dir > 0);
}
}
if(it->eq(*next) /*&& dir == next->dir*/) { // "If si = direction.rnext"
// "j = j + 1, seqj = si, next = next + direction"
seq.emplace_back(*it);
next += dir;
// __nfp::advance(next, R, dir > 0);
}
}
return seq;
};
std::vector<EdgeRefList> seqlist;
seqlist.reserve(Bref.size());
EdgeRefList Bslope = Bref; // copy Bref, we will make a slope diagram
// make the slope diagram of B
std::sort(Bslope.begin(), Bslope.end(), sortfn);
auto slopeit = Bslope.begin(); // search for the first turning point
while(!slopeit->isTurningPoint() && slopeit != Bslope.end()) slopeit++;
if(slopeit == Bslope.end()) {
// no turning point means convex polygon.
seqlist.emplace_back(mink(Aref, Bref, true));
} else {
int dir = 1;
auto firstturn = Bref.begin();
while(!firstturn->eq(*slopeit)) ++firstturn;
assert(firstturn != Bref.end());
EdgeRefList bgroup; bgroup.reserve(Bref.size());
bgroup.emplace_back(*slopeit);
auto b_it = std::next(firstturn);
while(b_it != firstturn) {
if(b_it == Bref.end()) b_it = Bref.begin();
while(!slopeit->eq(*b_it)) {
__nfp::advance(slopeit, Bslope, dir > 0);
}
if(!slopeit->isTurningPoint()) {
bgroup.emplace_back(*slopeit);
} else {
if(!bgroup.empty()) {
if(dir > 0) bgroup.emplace_back(*slopeit);
for(auto& me : bgroup) {
std::cout << me.eref.get().label << ", ";
}
std::cout << std::endl;
seqlist.emplace_back(mink(Aref, bgroup, dir == 1 ? true : false));
bgroup.clear();
if(dir < 0) bgroup.emplace_back(*slopeit);
} else {
bgroup.emplace_back(*slopeit);
}
dir *= -1;
}
++b_it;
}
}
// while(it != Bref.end()) // This is step 3 and step 4 in one loop
// if(it->isTurningPoint()) {
// R = {R.last, it++};
// auto seq = mink(Q, R, orientation);
// // TODO step 6 (should be 5 shouldn't it?): linking edges from A
// // I don't get this step
// seqlist.insert(seqlist.end(), seq.begin(), seq.end());
// orientation = !orientation;
// } else ++it;
// if(seqlist.empty()) seqlist = mink(Q, {Bref.begin(), Bref.end()}, true);
// /////////////////////////////////////////////////////////////////////////
// Algorithm 2: breaking Minkowski sums into track line trips
// /////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////
// Algorithm 3: finding the boundary of the NFP from track line trips
// /////////////////////////////////////////////////////////////////////////
for(auto& seq : seqlist) {
std::cout << "seqlist size: " << seq.size() << std::endl;
for(auto& s : seq) {
std::cout << (s.dir > 0 ? "" : "-") << s.eref.get().label << ", ";
}
std::cout << std::endl;
}
auto& seq = seqlist.front();
RawShape rsh;
Vertex top_nfp;
std::vector<Edge> edgelist; edgelist.reserve(seq.size());
for(auto& s : seq) {
edgelist.emplace_back(s.eref.get().e);
}
__nfp::buildPolygon(edgelist, rsh, top_nfp);
return Result(rsh, top_nfp);
}
// Specializable NFP implementation class. Specialize it if you have a faster
// or better NFP implementation
template<class RawShape, NfpLevel nfptype>

View File

@ -482,17 +482,40 @@ public:
template<class RawShape>
inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const {
return sl::isInside<RawShape>(boundingBox(), box);
return sl::isInside(boundingBox(), box);
}
template<class RawShape> inline bool
_Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const {
return sl::isInside<RawShape>(transformedShape(), circ);
return sl::isInside(transformedShape(), circ);
}
template<class RawShape> using _ItemRef = std::reference_wrapper<_Item<RawShape>>;
template<class RawShape> using _ItemGroup = std::vector<_ItemRef<RawShape>>;
template<class I> using _ItemRef = std::reference_wrapper<I>;
template<class I> using _ItemGroup = std::vector<_ItemRef<I>>;
/**
* \brief A list of packed item vectors. Each vector represents a bin.
*/
template<class RawShape>
using _PackGroup = std::vector<std::vector<_ItemRef<RawShape>>>;
/**
* \brief A list of packed (index, item) pair vectors. Each vector represents a
* bin.
*
* The index is points to the position of the item in the original input
* sequence. This way the caller can use the items as a transformation data
* carrier and transform the original objects manually.
*/
template<class RawShape>
using _IndexedPackGroup = std::vector<
std::vector<
std::pair<
unsigned,
_ItemRef<RawShape>
>
>
>;
template<class Iterator>
struct ConstItemRange {
@ -524,8 +547,10 @@ class PlacementStrategyLike {
PlacementStrategy impl_;
public:
using RawShape = typename PlacementStrategy::ShapeType;
/// The item type that the placer works with.
using Item = typename PlacementStrategy::Item;
using Item = _Item<RawShape>;
/// The placer's config type. Should be a simple struct but can be anything.
using Config = typename PlacementStrategy::Config;
@ -544,8 +569,7 @@ public:
*/
using PackResult = typename PlacementStrategy::PackResult;
using ItemRef = _ItemRef<Item>;
using ItemGroup = _ItemGroup<Item>;
using ItemGroup = _ItemGroup<RawShape>;
using DefaultIterator = typename ItemGroup::const_iterator;
/**
@ -619,6 +643,15 @@ public:
return impl_.pack(item, remaining);
}
/**
* This method makes possible to "preload" some items into the placer. It
* will not move these items but will consider them as already packed.
*/
inline void preload(const ItemGroup& packeditems)
{
impl_.preload(packeditems);
}
/// Unpack the last element (remove it from the list of packed items).
inline void unpackLast() { impl_.unpackLast(); }
@ -649,11 +682,11 @@ template<class SelectionStrategy>
class SelectionStrategyLike {
SelectionStrategy impl_;
public:
using Item = typename SelectionStrategy::Item;
using RawShape = typename SelectionStrategy::ShapeType;
using Item = _Item<RawShape>;
using PackGroup = _PackGroup<RawShape>;
using Config = typename SelectionStrategy::Config;
using ItemRef = std::reference_wrapper<Item>;
using ItemGroup = std::vector<ItemRef>;
/**
* @brief Provide a different configuration for the selection strategy.
@ -703,60 +736,29 @@ public:
std::forward<PConfig>(config));
}
/**
* \brief Get the number of bins opened by the selection algorithm.
*
* Initially it is zero and after the call to packItems it will return
* the number of bins opened by the packing procedure.
*
* \return The number of bins opened.
*/
inline size_t binCount() const { return impl_.binCount(); }
/**
* @brief Get the items for a particular bin.
* @param binIndex The index of the requested bin.
* @return Returns a list of all items packed into the requested bin.
*/
inline ItemGroup itemsForBin(size_t binIndex) {
return impl_.itemsForBin(binIndex);
inline const PackGroup& getResult() const {
return impl_.getResult();
}
/// Same as itemsForBin but for a const context.
inline const ItemGroup itemsForBin(size_t binIndex) const {
return impl_.itemsForBin(binIndex);
}
/**
* @brief Loading a group of already packed bins. It is best to use a result
* from a previous packing. The algorithm will consider this input as if the
* objects are already packed and not move them. If any of these items are
* outside the bin, it is up to the placer algorithm what will happen.
* Packing additional items can fail for the bottom-left and nfp placers.
* @param pckgrp A packgroup which is a vector of item vectors. Each item
* vector corresponds to a packed bin.
*/
inline void preload(const PackGroup& pckgrp) { impl_.preload(pckgrp); }
void clear() { impl_.clear(); }
};
/**
* \brief A list of packed item vectors. Each vector represents a bin.
*/
template<class RawShape>
using _PackGroup = std::vector<
std::vector<
std::reference_wrapper<_Item<RawShape>>
>
>;
/**
* \brief A list of packed (index, item) pair vectors. Each vector represents a
* bin.
*
* The index is points to the position of the item in the original input
* sequence. This way the caller can use the items as a transformation data
* carrier and transform the original objects manually.
*/
template<class RawShape>
using _IndexedPackGroup = std::vector<
std::vector<
std::pair<
unsigned,
std::reference_wrapper<_Item<RawShape>>
>
>
>;
/**
* The Arranger is the front-end class for the libnest2d library. It takes the
* input items and outputs the items with the proper transformations to be
@ -868,17 +870,29 @@ public:
}
/// Set a predicate to tell when to abort nesting.
inline Nester& stopCondition(StopCondition fn) {
inline Nester& stopCondition(StopCondition fn)
{
selector_.stopCondition(fn); return *this;
}
inline PackGroup lastResult() {
PackGroup ret;
for(size_t i = 0; i < selector_.binCount(); i++) {
auto items = selector_.itemsForBin(i);
ret.push_back(items);
inline const PackGroup& lastResult() const
{
return selector_.getResult();
}
inline void preload(const PackGroup& pgrp)
{
selector_.preload(pgrp);
}
inline void preload(const IndexedPackGroup& ipgrp)
{
PackGroup pgrp; pgrp.reserve(ipgrp.size());
for(auto& ig : ipgrp) {
pgrp.emplace_back(); pgrp.back().reserve(ig.size());
for(auto& r : ig) pgrp.back().emplace_back(r.second);
}
return ret;
preload(pgrp);
}
private:
@ -892,7 +906,7 @@ private:
// have to exist for the lifetime of this call.
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
>
inline PackGroup _execute(TIterator from, TIterator to, bool = false)
inline const PackGroup& _execute(TIterator from, TIterator to, bool = false)
{
__execute(from, to);
return lastResult();
@ -902,7 +916,7 @@ private:
class IT = remove_cvref_t<typename TIterator::value_type>,
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
>
inline PackGroup _execute(TIterator from, TIterator to, int = false)
inline const PackGroup& _execute(TIterator from, TIterator to, int = false)
{
item_cache_ = {from, to};
@ -946,10 +960,12 @@ private:
TSel& selector)
{
IndexedPackGroup pg;
pg.reserve(selector.binCount());
pg.reserve(selector.getResult().size());
for(size_t i = 0; i < selector.binCount(); i++) {
auto items = selector.itemsForBin(i);
const PackGroup& pckgrp = selector.getResult();
for(size_t i = 0; i < pckgrp.size(); i++) {
auto items = pckgrp[i];
pg.push_back({});
pg[i].reserve(items.size());

View File

@ -48,12 +48,12 @@ else()
target_link_libraries(NloptOptimizer INTERFACE Nlopt::Nlopt)
endif()
#target_sources( NloptOptimizer INTERFACE
#${CMAKE_CURRENT_SOURCE_DIR}/simplex.hpp
#${CMAKE_CURRENT_SOURCE_DIR}/subplex.hpp
#${CMAKE_CURRENT_SOURCE_DIR}/genetic.hpp
#${CMAKE_CURRENT_SOURCE_DIR}/nlopt_boilerplate.hpp
#)
target_sources( NloptOptimizer INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/simplex.hpp
${CMAKE_CURRENT_SOURCE_DIR}/subplex.hpp
${CMAKE_CURRENT_SOURCE_DIR}/genetic.hpp
${CMAKE_CURRENT_SOURCE_DIR}/nlopt_boilerplate.hpp
)
target_compile_definitions(NloptOptimizer INTERFACE LIBNEST2D_OPTIMIZER_NLOPT)

View File

@ -130,7 +130,7 @@ namespace placers {
template<class RawShape>
struct NfpPConfig {
using ItemGroup = _ItemGroup<_Item<RawShape>>;
using ItemGroup = _ItemGroup<RawShape>;
enum class Alignment {
CENTER,
@ -138,6 +138,8 @@ struct NfpPConfig {
BOTTOM_RIGHT,
TOP_LEFT,
TOP_RIGHT,
DONT_ALIGN //!> Warning: parts may end up outside the bin with the
//! default object function.
};
/// Which angles to try out for better results.
@ -545,8 +547,8 @@ public:
_NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default;
#ifndef BP2D_COMPILER_MSVC12 // MSVC2013 does not support default move ctors
_NofitPolyPlacer(_NofitPolyPlacer&&) /*BP2D_NOEXCEPT*/ = default;
_NofitPolyPlacer& operator=(_NofitPolyPlacer&&) /*BP2D_NOEXCEPT*/ = default;
_NofitPolyPlacer(_NofitPolyPlacer&&) = default;
_NofitPolyPlacer& operator=(_NofitPolyPlacer&&) = default;
#endif
static inline double overfit(const Box& bb, const RawShape& bin) {
@ -905,26 +907,44 @@ private:
// This is the kernel part of the object function that is
// customizable by the library client
auto _objfunc = config_.object_function?
config_.object_function :
[norm, bin, binbb, pbb](const Item& item)
{
auto ibb = item.boundingBox();
auto fullbb = boundingBox(pbb, ibb);
std::function<double(const Item&)> _objfunc;
if(config_.object_function) _objfunc = config_.object_function;
else {
double score = pl::distance(ibb.center(), binbb.center());
score /= norm;
// Inside check has to be strict if no alignment was enabled
std::function<double(const Box&)> ins_check;
if(config_.alignment == Config::Alignment::DONT_ALIGN)
ins_check = [&binbb, norm](const Box& fullbb) {
double ret = 0;
if(!sl::isInside(fullbb, binbb))
ret += norm;
return ret;
};
else
ins_check = [&bin](const Box& fullbb) {
double miss = overfit(fullbb, bin);
miss = miss > 0? miss : 0;
return std::pow(miss, 2);
};
double miss = overfit(fullbb, bin);
miss = miss > 0? miss : 0;
score += std::pow(miss, 2);
_objfunc = [norm, binbb, pbb, ins_check](const Item& item)
{
auto ibb = item.boundingBox();
auto fullbb = boundingBox(pbb, ibb);
return score;
};
double score = pl::distance(ibb.center(),
binbb.center());
score /= norm;
score += ins_check(fullbb);
return score;
};
}
// Our object function for placement
auto rawobjfunc =
[_objfunc, iv, startpos] (Vertex v, Item& itm)
auto rawobjfunc = [_objfunc, iv, startpos]
(Vertex v, Item& itm)
{
auto d = v - iv;
d += startpos;
@ -938,9 +958,10 @@ private:
ecache[opt.nfpidx].coords(opt.hidx, opt.relpos);
};
auto boundaryCheck =
[&merged_pile, &getNfpPoint, &item, &bin, &iv, &startpos]
(const Optimum& o)
auto alignment = config_.alignment;
auto boundaryCheck = [alignment, &merged_pile, &getNfpPoint,
&item, &bin, &iv, &startpos] (const Optimum& o)
{
auto v = getNfpPoint(o);
auto d = v - iv;
@ -951,7 +972,12 @@ private:
auto chull = sl::convexHull(merged_pile);
merged_pile.pop_back();
return overfit(chull, bin);
double miss = 0;
if(alignment == Config::Alignment::DONT_ALIGN)
miss = sl::isInside(chull, bin) ? -1.0 : 1.0;
else miss = overfit(chull, bin);
return miss;
};
Optimum optimum(0, 0);
@ -1101,7 +1127,9 @@ private:
}
inline void finalAlign(_Circle<TPoint<RawShape>> cbin) {
if(items_.empty()) return;
if(items_.empty() ||
config_.alignment == Config::Alignment::DONT_ALIGN) return;
nfp::Shapes<RawShape> m;
m.reserve(items_.size());
for(Item& item : items_) m.emplace_back(item.transformedShape());
@ -1113,7 +1141,9 @@ private:
}
inline void finalAlign(Box bbin) {
if(items_.empty()) return;
if(items_.empty() ||
config_.alignment == Config::Alignment::DONT_ALIGN) return;
nfp::Shapes<RawShape> m;
m.reserve(items_.size());
for(Item& item : items_) m.emplace_back(item.transformedShape());
@ -1147,6 +1177,7 @@ private:
cb = bbin.maxCorner();
break;
}
default: ; // DONT_ALIGN
}
auto d = cb - ci;
@ -1184,6 +1215,7 @@ private:
cb = bbin.maxCorner();
break;
}
default:;
}
auto d = cb - ci;

View File

@ -12,6 +12,7 @@ class PlacerBoilerplate {
mutable bool farea_valid_ = false;
mutable double farea_ = 0.0;
public:
using ShapeType = RawShape;
using Item = _Item<RawShape>;
using Vertex = TPoint<RawShape>;
using Segment = _Segment<Vertex>;
@ -19,7 +20,7 @@ public:
using Coord = TCoord<Vertex>;
using Unit = Coord;
using Config = Cfg;
using ItemGroup = _ItemGroup<Item>;
using ItemGroup = _ItemGroup<RawShape>;
using DefaultIter = typename ItemGroup::const_iterator;
class PackResult {
@ -59,8 +60,7 @@ public:
}
template<class Range = ConstItemRange<DefaultIter>>
bool pack(Item& item,
const Range& rem = Range()) {
bool pack(Item& item, const Range& rem = Range()) {
auto&& r = static_cast<Subclass*>(this)->trypack(item, rem);
if(r) {
items_.push_back(*(r.item_ptr_));
@ -69,6 +69,11 @@ public:
return r;
}
void preload(const ItemGroup& packeditems) {
items_.insert(items_.end(), packeditems.begin(), packeditems.end());
farea_valid_ = false;
}
void accept(PackResult& r) {
if(r) {
r.item_ptr_->translation(r.move_);
@ -117,6 +122,7 @@ using Base::bin_; \
using Base::items_; \
using Base::config_; \
public: \
using typename Base::ShapeType; \
using typename Base::Item; \
using typename Base::ItemGroup; \
using typename Base::BinType; \

View File

@ -33,7 +33,7 @@ class _DJDHeuristic: public SelectionBoilerplate<RawShape> {
public:
using typename Base::Item;
using typename Base::ItemRef;
using ItemRef = std::reference_wrapper<Item>;
/**
* @brief The Config for DJD heuristic.
@ -126,6 +126,8 @@ public:
store_.clear();
store_.reserve(last-first);
// TODO: support preloading
packed_bins_.clear();
std::copy(first, last, std::back_inserter(store_));

View File

@ -34,6 +34,10 @@ public:
store_.clear();
auto total = last-first;
store_.reserve(total);
// TODO: support preloading
packed_bins_.clear();
packed_bins_.emplace_back();
auto makeProgress = [this, &total](

View File

@ -36,11 +36,19 @@ public:
store_.clear();
store_.reserve(last-first);
packed_bins_.clear();
std::vector<Placer> placers;
placers.reserve(last-first);
// If the packed_items array is not empty we have to create as many
// placers as there are elements in packed bins and preload each item
// into the appropriate placer
for(ItemGroup& ig : packed_bins_) {
placers.emplace_back(bin);
placers.back().configure(pconfig);
placers.back().preload(ig);
}
std::copy(first, last, std::back_inserter(store_));
auto sortfunc = [](Item& i1, Item& i2) {

View File

@ -9,27 +9,23 @@ namespace libnest2d { namespace selections {
template<class RawShape>
class SelectionBoilerplate {
public:
using ShapeType = RawShape;
using Item = _Item<RawShape>;
using ItemRef = std::reference_wrapper<Item>;
using ItemGroup = std::vector<ItemRef>;
using PackGroup = std::vector<ItemGroup>;
using ItemGroup = _ItemGroup<RawShape>;
using PackGroup = _PackGroup<RawShape>;
size_t binCount() const { return packed_bins_.size(); }
ItemGroup itemsForBin(size_t binIndex) {
assert(binIndex < packed_bins_.size());
return packed_bins_[binIndex];
}
inline const ItemGroup itemsForBin(size_t binIndex) const {
assert(binIndex < packed_bins_.size());
return packed_bins_[binIndex];
inline const PackGroup& getResult() const {
return packed_bins_;
}
inline void progressIndicator(ProgressFunction fn) { progress_ = fn; }
inline void stopCondition(StopCondition cond) { stopcond_ = cond; }
inline void preload(const PackGroup& pckgrp) { packed_bins_ = pckgrp; }
inline void clear() { packed_bins_.clear(); }
protected:
PackGroup packed_bins_;

View File

@ -356,13 +356,15 @@ inline double area(const PolygonImpl& shape, const PolygonTag&)
#endif
template<>
inline bool isInside(const PointImpl& point, const PolygonImpl& shape)
inline bool isInside(const PointImpl& point, const PolygonImpl& shape,
const PointTag&, const PolygonTag&)
{
return boost::geometry::within(point, shape);
}
template<>
inline bool isInside(const PolygonImpl& sh1, const PolygonImpl& sh2)
inline bool isInside(const PolygonImpl& sh1, const PolygonImpl& sh2,
const PolygonTag&, const PolygonTag&)
{
return boost::geometry::within(sh1, sh2);
}

View File

@ -32,6 +32,7 @@ public:
}
this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1));
}
void reset() { this->defined = false; this->min = PointClass::Zero(); this->max = PointClass::Zero(); }
void merge(const PointClass &point);
void merge(const std::vector<PointClass> &points);
void merge(const BoundingBoxBase<PointClass> &bb);

View File

@ -860,7 +860,7 @@ void GCode::_do_export(Print &print, FILE *file)
if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming)) {
// Set initial extruder only after custom start G-code.
// Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
_write(file, this->set_extruder(initial_extruder_id));
_write(file, this->set_extruder(initial_extruder_id, 0.));
}
// Do all objects for each layer.
@ -1537,15 +1537,13 @@ void GCode::process_layer(
}
} // for objects
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size());
for (unsigned int extruder_id : layer_tools.extruders)
{
gcode += (layer_tools.has_wipe_tower && m_wipe_tower) ?
m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) :
this->set_extruder(extruder_id);
this->set_extruder(extruder_id, print_z);
// let analyzer tag generator aware of a role type change
if (m_enable_analyzer && layer_tools.has_wipe_tower && m_wipe_tower)
@ -2692,7 +2690,7 @@ std::string GCode::retract(bool toolchange)
return gcode;
}
std::string GCode::set_extruder(unsigned int extruder_id)
std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
{
if (!m_writer.need_toolchange(extruder_id))
return "";
@ -2726,6 +2724,8 @@ std::string GCode::set_extruder(unsigned int extruder_id)
DynamicConfig config;
config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id()));
config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id));
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
gcode += placeholder_parser_process("toolchange_gcode", m_config.toolchange_gcode.value, extruder_id, &config);
check_add_eol(gcode);
}

View File

@ -257,12 +257,12 @@ protected:
bool needs_retraction(const Polyline &travel, ExtrusionRole role = erNone);
std::string retract(bool toolchange = false);
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
std::string set_extruder(unsigned int extruder_id);
std::string set_extruder(unsigned int extruder_id, double print_z);
/* Origin of print coordinates expressed in unscaled G-code coordinates.
This affects the input arguments supplied to the extrude*() and travel_to()
methods. */
Vec2d m_origin;
Vec2d m_origin;
FullPrintConfig m_config;
GCodeWriter m_writer;
PlaceholderParser m_placeholder_parser;

View File

@ -207,8 +207,7 @@ static bool sort_pointfs(const Vec3d& a, const Vec3d& b)
}
// This implementation is based on Andrew's monotone chain 2D convex hull algorithm
Polygon
convex_hull(Points points)
Polygon convex_hull(Points points)
{
assert(points.size() >= 3);
// sort input points
@ -922,16 +921,16 @@ Vec3d extract_euler_angles(const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>&
Vec3d angles2 = Vec3d::Zero();
if (is_approx(std::abs(rotation_matrix(2, 0)), 1.0))
{
angles1(0) = 0.0;
if (rotation_matrix(2, 0) > 0.0) // == +1.0
angles1(2) = 0.0;
if (rotation_matrix(2, 0) < 0.0) // == -1.0
{
angles1(1) = 0.5 * (double)PI;
angles1(2) = angles1(0) + ::atan2(rotation_matrix(0, 1), rotation_matrix(0, 2));
angles1(0) = angles1(2) + ::atan2(rotation_matrix(0, 1), rotation_matrix(0, 2));
}
else // == -1.0
else // == 1.0
{
angles1(1) = 0.5 * (double)PI;
angles1(2) = -angles1(0) - ::atan2(rotation_matrix(0, 1), rotation_matrix(0, 2));
angles1(1) = - 0.5 * (double)PI;
angles1(0) = - angles1(2) + ::atan2(- rotation_matrix(0, 1), - rotation_matrix(0, 2));
}
angles2 = angles1;
}

View File

@ -207,7 +207,7 @@ public:
void set_rotation(const Vec3d& rotation);
void set_rotation(Axis axis, double rotation);
Vec3d get_scaling_factor() const { return m_scaling_factor; }
const Vec3d& get_scaling_factor() const { return m_scaling_factor; }
double get_scaling_factor(Axis axis) const { return m_scaling_factor(axis); }
void set_scaling_factor(const Vec3d& scaling_factor);

View File

@ -53,6 +53,11 @@
#define HAS_INTRINSIC_128_TYPE
#endif
#if defined(_MSC_VER) && defined(_WIN64)
#include <intrin.h>
#pragma intrinsic(_mul128)
#endif
//------------------------------------------------------------------------------
// Int128 class (enables safe math on signed 64bit integers)
// eg Int128 val1((int64_t)9223372036854775807); //ie 2^63 -1

View File

@ -489,6 +489,9 @@ void Model::convert_multipart_object(unsigned int max_extruders)
{
new_v->name = o->name;
new_v->config.set_deserialize("extruder", get_auto_extruder_id_as_string(max_extruders));
#if ENABLE_VOLUMES_CENTERING_FIXES
new_v->translate(-o->origin_translation);
#endif // ENABLE_VOLUMES_CENTERING_FIXES
}
}
@ -573,6 +576,8 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
this->origin_translation = rhs.origin_translation;
m_bounding_box = rhs.m_bounding_box;
m_bounding_box_valid = rhs.m_bounding_box_valid;
m_raw_mesh_bounding_box = rhs.m_raw_mesh_bounding_box;
m_raw_mesh_bounding_box_valid = rhs.m_raw_mesh_bounding_box_valid;
this->clear_volumes();
this->volumes.reserve(rhs.volumes.size());
@ -604,6 +609,8 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
this->origin_translation = std::move(rhs.origin_translation);
m_bounding_box = std::move(rhs.m_bounding_box);
m_bounding_box_valid = std::move(rhs.m_bounding_box_valid);
m_raw_mesh_bounding_box = rhs.m_raw_mesh_bounding_box;
m_raw_mesh_bounding_box_valid = rhs.m_raw_mesh_bounding_box_valid;
this->clear_volumes();
this->volumes = std::move(rhs.volumes);
@ -783,19 +790,11 @@ void ModelObject::clear_instances()
const BoundingBoxf3& ModelObject::bounding_box() const
{
if (! m_bounding_box_valid) {
BoundingBoxf3 raw_bbox;
for (const ModelVolume *v : this->volumes)
if (v->is_model_part())
{
TriangleMesh m = v->mesh;
m.transform(v->get_matrix());
raw_bbox.merge(m.bounding_box());
}
BoundingBoxf3 bb;
for (const ModelInstance *i : this->instances)
bb.merge(i->transform_bounding_box(raw_bbox));
m_bounding_box = bb;
m_bounding_box_valid = true;
BoundingBoxf3 raw_bbox = this->raw_mesh_bounding_box();
m_bounding_box.reset();
for (const ModelInstance *i : this->instances)
m_bounding_box.merge(i->transform_bounding_box(raw_bbox));
}
return m_bounding_box;
}
@ -842,6 +841,26 @@ TriangleMesh ModelObject::full_raw_mesh() const
return mesh;
}
BoundingBoxf3 ModelObject::raw_mesh_bounding_box() const
{
if (! m_raw_mesh_bounding_box_valid) {
m_raw_mesh_bounding_box_valid = true;
m_raw_mesh_bounding_box.reset();
for (const ModelVolume *v : this->volumes)
if (v->is_model_part())
m_raw_mesh_bounding_box.merge(v->mesh.transformed_bounding_box(v->get_matrix()));
}
return m_raw_mesh_bounding_box;
}
BoundingBoxf3 ModelObject::full_raw_mesh_bounding_box() const
{
BoundingBoxf3 bb;
for (const ModelVolume *v : this->volumes)
bb.merge(v->mesh.transformed_bounding_box(v->get_matrix()));
return bb;
}
// A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
// This bounding box is only used for the actual slicing.
BoundingBoxf3 ModelObject::raw_bounding_box() const
@ -896,19 +915,59 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
return bb;
}
#if ENABLE_VOLUMES_CENTERING_FIXES
BoundingBoxf3 ModelObject::full_raw_mesh_bounding_box() const
// Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane.
// This method is cheap in that it does not make any unnecessary copy of the volume meshes.
// This method is used by the auto arrange function.
Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance)
{
BoundingBoxf3 bb;
Points pts;
for (const ModelVolume *v : this->volumes)
{
TriangleMesh vol_mesh(v->mesh);
vol_mesh.transform(v->get_matrix());
bb.merge(vol_mesh.bounding_box());
if (v->is_model_part()) {
const stl_file &stl = v->mesh.stl;
Transform3d trafo = trafo_instance * v->get_matrix();
if (stl.v_shared == nullptr) {
// Using the STL faces.
for (unsigned int i = 0; i < stl.stats.number_of_facets; ++ i) {
const stl_facet &facet = stl.facet_start[i];
for (size_t j = 0; j < 3; ++ j) {
Vec3d p = trafo * facet.vertex[j].cast<double>();
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
}
}
} else {
// Using the shared vertices should be a bit quicker than using the STL faces.
for (int i = 0; i < stl.stats.shared_vertices; ++ i) {
Vec3d p = trafo * stl.v_shared[i].cast<double>();
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
}
}
}
std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) < b(0) || (a(0) == b(0) && a(1) < b(1)); });
pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) == b(0) && a(1) == b(1); }), pts.end());
Polygon hull;
int n = (int)pts.size();
if (n >= 3) {
int k = 0;
hull.points.resize(2 * n);
// Build lower hull
for (int i = 0; i < n; ++ i) {
while (k >= 2 && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
-- k;
hull[k ++] = pts[i];
}
// Build upper hull
for (int i = n-2, t = k+1; i >= 0; i--) {
while (k >= t && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
-- k;
hull[k ++] = pts[i];
}
hull.points.resize(k);
assert(hull.points.front() == hull.points.back());
hull.points.pop_back();
}
return bb;
return hull;
}
#endif // ENABLE_VOLUMES_CENTERING_FIXES
void ModelObject::center_around_origin()
{
@ -1097,7 +1156,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
// Perform cut
TriangleMeshSlicer tms(&volume->mesh);
tms.cut(z, &upper_mesh, &lower_mesh);
tms.cut(float(z), &upper_mesh, &lower_mesh);
// Reset volume transformation except for offset
const Vec3d offset = volume->get_offset();

View File

@ -205,7 +205,7 @@ public:
// This bounding box is approximate and not snug.
// This bounding box is being cached.
const BoundingBoxf3& bounding_box() const;
void invalidate_bounding_box() { m_bounding_box_valid = false; }
void invalidate_bounding_box() { m_bounding_box_valid = false; m_raw_mesh_bounding_box_valid = false; }
// A mesh containing all transformed instances of this object.
TriangleMesh mesh() const;
@ -219,10 +219,16 @@ public:
BoundingBoxf3 raw_bounding_box() const;
// A snug bounding box around the transformed non-modifier object volumes.
BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const;
#if ENABLE_VOLUMES_CENTERING_FIXES
// Bounding box of non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
// A snug bounding box of non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
BoundingBoxf3 raw_mesh_bounding_box() const;
// A snug bounding box of non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
BoundingBoxf3 full_raw_mesh_bounding_box() const;
#endif // ENABLE_VOLUMES_CENTERING_FIXES
// Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane.
// This method is cheap in that it does not make any unnecessary copy of the volume meshes.
// This method is used by the auto arrange function.
Polygon convex_hull_2d(const Transform3d &trafo_instance);
void center_around_origin();
void ensure_on_bed();
void translate_instances(const Vec3d& vector);
@ -261,7 +267,8 @@ protected:
void set_model(Model *model) { m_model = model; }
private:
ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false) {}
ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()),
m_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {}
~ModelObject();
/* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
@ -280,6 +287,8 @@ private:
// Bounding box, cached.
mutable BoundingBoxf3 m_bounding_box;
mutable bool m_bounding_box_valid;
mutable BoundingBoxf3 m_raw_mesh_bounding_box;
mutable bool m_raw_mesh_bounding_box_valid;
};
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
@ -464,7 +473,7 @@ public:
void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); }
void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); }
Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
const Vec3d& get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); }
void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); }

View File

@ -358,6 +358,29 @@ public:
m_rtree.clear();
return m_pck.executeIndexed(std::forward<Args>(args)...);
}
inline void preload(const PackGroup& pg) {
m_pconf.alignment = PConfig::Alignment::DONT_ALIGN;
m_pconf.object_function = nullptr; // drop the special objectfunction
m_pck.preload(pg);
// Build the rtree for queries to work
for(const ItemGroup& grp : pg)
for(unsigned idx = 0; idx < grp.size(); ++idx) {
Item& itm = grp[idx];
m_rtree.insert({itm.boundingBox(), idx});
}
m_pck.configure(m_pconf);
}
bool is_colliding(const Item& item) {
if(m_rtree.empty()) return false;
std::vector<SpatElement> result;
m_rtree.query(bgi::intersects(item.boundingBox()),
std::back_inserter(result));
return !result.empty();
}
};
// Arranger specialization for a Box shaped bin.
@ -365,8 +388,8 @@ template<> class AutoArranger<Box>: public _ArrBase<Box> {
public:
AutoArranger(const Box& bin, Distance dist,
std::function<void(unsigned)> progressind,
std::function<bool(void)> stopcond):
std::function<void(unsigned)> progressind = [](unsigned){},
std::function<bool(void)> stopcond = [](){return false;}):
_ArrBase<Box>(bin, dist, progressind, stopcond)
{
@ -411,8 +434,8 @@ template<> class AutoArranger<lnCircle>: public _ArrBase<lnCircle> {
public:
AutoArranger(const lnCircle& bin, Distance dist,
std::function<void(unsigned)> progressind,
std::function<bool(void)> stopcond):
std::function<void(unsigned)> progressind = [](unsigned){},
std::function<bool(void)> stopcond = [](){return false;}):
_ArrBase<lnCircle>(bin, dist, progressind, stopcond) {
// As with the box, only the inside check is different.
@ -456,8 +479,8 @@ public:
template<> class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
public:
AutoArranger(const PolygonImpl& bin, Distance dist,
std::function<void(unsigned)> progressind,
std::function<bool(void)> stopcond):
std::function<void(unsigned)> progressind = [](unsigned){},
std::function<bool(void)> stopcond = [](){return false;}):
_ArrBase<PolygonImpl>(bin, dist, progressind, stopcond)
{
m_pconf.object_function = [this, &bin] (const Item &item) {
@ -530,24 +553,47 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
for(ModelObject* objptr : model.objects) {
if(objptr) {
TriangleMesh rmesh = objptr->raw_mesh();
ModelInstance * finst = objptr->instances.front();
// Object instances should carry the same scaling and
// x, y rotation that is why we use the first instance.
// The next line will apply only the full mirroring and scaling
rmesh.transform(finst->get_matrix(true, true, false, false));
rmesh.rotate_x(float(finst->get_rotation()(X)));
rmesh.rotate_y(float(finst->get_rotation()(Y)));
// TODO export the exact 2D projection. Cannot do it as libnest2d
// does not support concave shapes (yet).
auto p = rmesh.convex_hull();
ClipperLib::Path clpath;
//WIP Vojtech's optimization of the calculation of the convex hull is not working correctly yet.
#if 1
{
TriangleMesh rmesh = objptr->raw_mesh();
p.make_clockwise();
p.append(p.first_point());
auto clpath = Slic3rMultiPoint_to_ClipperPath(p);
ModelInstance * finst = objptr->instances.front();
// Object instances should carry the same scaling and
// x, y rotation that is why we use the first instance.
// The next line will apply only the full mirroring and scaling
rmesh.transform(finst->get_matrix(true, true, false, false));
rmesh.rotate_x(float(finst->get_rotation()(X)));
rmesh.rotate_y(float(finst->get_rotation()(Y)));
// TODO export the exact 2D projection. Cannot do it as libnest2d
// does not support concave shapes (yet).
auto p = rmesh.convex_hull();
p.make_clockwise();
p.append(p.first_point());
clpath = Slic3rMultiPoint_to_ClipperPath(p);
}
#else
// Object instances should carry the same scaling and
// x, y rotation that is why we use the first instance.
{
ModelInstance *finst = objptr->instances.front();
Vec3d rotation = finst->get_rotation();
rotation.z() = 0.;
Transform3d trafo_instance = Geometry::assemble_transform(Vec3d::Zero(), rotation, finst->get_scaling_factor(), finst->get_mirror());
Polygon p = objptr->convex_hull_2d(trafo_instance);
assert(! p.points.empty());
p.reverse();
assert(! p.is_counter_clockwise());
p.append(p.first_point());
clpath = Slic3rMultiPoint_to_ClipperPath(p);
}
#endif
for(ModelInstance* objinst : objptr->instances) {
if(objinst) {
@ -791,5 +837,174 @@ bool arrange(Model &model, // The model with the geometries
return ret && result.size() == 1;
}
void find_new_position(const Model &model,
ModelInstancePtrs toadd,
coord_t min_obj_distance,
const Polyline &bed)
{
// Get the 2D projected shapes with their 3D model instance pointers
auto shapemap = arr::projectModelFromTop(model);
// Copy the references for the shapes only as the arranger expects a
// sequence of objects convertible to Item or ClipperPolygon
PackGroup preshapes; preshapes.emplace_back();
ItemGroup shapes;
preshapes.front().reserve(shapemap.size());
std::vector<ModelInstance*> shapes_ptr; shapes_ptr.reserve(toadd.size());
IndexedPackGroup result;
// If there is no hint about the shape, we will try to guess
BedShapeHint bedhint = bedShape(bed);
BoundingBox bbb(bed);
auto binbb = Box({
static_cast<libnest2d::Coord>(bbb.min(0)),
static_cast<libnest2d::Coord>(bbb.min(1))
},
{
static_cast<libnest2d::Coord>(bbb.max(0)),
static_cast<libnest2d::Coord>(bbb.max(1))
});
for(auto it = shapemap.begin(); it != shapemap.end(); ++it) {
if(std::find(toadd.begin(), toadd.end(), it->first) == toadd.end()) {
if(it->second.isInside(binbb)) // just ignore items which are outside
preshapes.front().emplace_back(std::ref(it->second));
}
else {
shapes_ptr.emplace_back(it->first);
shapes.emplace_back(std::ref(it->second));
}
}
auto try_first_to_center = [&shapes, &shapes_ptr, &binbb]
(std::function<bool(const Item&)> is_colliding,
std::function<void(Item&)> preload)
{
// Try to put the first item to the center, as the arranger will not
// do this for us.
auto shptrit = shapes_ptr.begin();
for(auto shit = shapes.begin(); shit != shapes.end(); ++shit, ++shptrit)
{
// Try to place items to the center
Item& itm = *shit;
auto ibb = itm.boundingBox();
auto d = binbb.center() - ibb.center();
itm.translate(d);
if(!is_colliding(itm)) {
preload(itm);
auto offset = itm.translation();
Radians rot = itm.rotation();
ModelInstance *minst = *shptrit;
Vec3d foffset(offset.X*SCALING_FACTOR,
offset.Y*SCALING_FACTOR,
minst->get_offset()(Z));
// write the transformation data into the model instance
minst->set_rotation(Z, rot);
minst->set_offset(foffset);
shit = shapes.erase(shit);
shptrit = shapes_ptr.erase(shptrit);
break;
}
}
};
switch(bedhint.type) {
case BedShapeType::BOX: {
// Create the arranger for the box shaped bed
AutoArranger<Box> arrange(binbb, min_obj_distance);
if(!preshapes.front().empty()) { // If there is something on the plate
arrange.preload(preshapes);
try_first_to_center(
[&arrange](const Item& itm) {return arrange.is_colliding(itm);},
[&arrange](Item& itm) { arrange.preload({{itm}}); }
);
}
// Arrange and return the items with their respective indices within the
// input sequence.
result = arrange(shapes.begin(), shapes.end());
break;
}
case BedShapeType::CIRCLE: {
auto c = bedhint.shape.circ;
auto cc = to_lnCircle(c);
// Create the arranger for the box shaped bed
AutoArranger<lnCircle> arrange(cc, min_obj_distance);
if(!preshapes.front().empty()) { // If there is something on the plate
arrange.preload(preshapes);
try_first_to_center(
[&arrange](const Item& itm) {return arrange.is_colliding(itm);},
[&arrange](Item& itm) { arrange.preload({{itm}}); }
);
}
// Arrange and return the items with their respective indices within the
// input sequence.
result = arrange(shapes.begin(), shapes.end());
break;
}
case BedShapeType::IRREGULAR:
case BedShapeType::WHO_KNOWS: {
using P = libnest2d::PolygonImpl;
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
P irrbed = sl::create<PolygonImpl>(std::move(ctour));
AutoArranger<P> arrange(irrbed, min_obj_distance);
if(!preshapes.front().empty()) { // If there is something on the plate
arrange.preload(preshapes);
try_first_to_center(
[&arrange](const Item& itm) {return arrange.is_colliding(itm);},
[&arrange](Item& itm) { arrange.preload({{itm}}); }
);
}
// Arrange and return the items with their respective indices within the
// input sequence.
result = arrange(shapes.begin(), shapes.end());
break;
}
};
// Now we go through the result which will contain the fixed and the moving
// polygons as well. We will have to search for our item.
const auto STRIDE_PADDING = 1.2;
Coord stride = Coord(STRIDE_PADDING*binbb.width()*SCALING_FACTOR);
Coord batch_offset = 0;
for(auto& group : result) {
for(auto& r : group) if(r.first < shapes.size()) {
Item& resultitem = r.second;
unsigned idx = r.first;
auto offset = resultitem.translation();
Radians rot = resultitem.rotation();
ModelInstance *minst = shapes_ptr[idx];
Vec3d foffset(offset.X*SCALING_FACTOR + batch_offset,
offset.Y*SCALING_FACTOR,
minst->get_offset()(Z));
// write the transformation data into the model instance
minst->set_rotation(Z, rot);
minst->set_offset(foffset);
}
batch_offset += stride;
}
}
}
}

View File

@ -73,7 +73,13 @@ bool arrange(Model &model, coord_t min_obj_distance,
std::function<void(unsigned)> progressind,
std::function<bool(void)> stopcondition);
}
/// This will find a suitable position for a new object instance and leave the
/// old items untouched.
void find_new_position(const Model& model,
ModelInstancePtrs instances_to_add,
coord_t min_obj_distance,
const Slic3r::Polyline& bed);
}
} // arr
} // Slic3r
#endif // MODELARRANGE_HPP

View File

@ -2060,7 +2060,7 @@ DynamicConfig PrintStatistics::config() const
config.set_key_value("print_time", new ConfigOptionString(normal_print_time));
config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time));
config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time));
config.set_key_value("used_filament", new ConfigOptionFloat (this->total_used_filament));
config.set_key_value("used_filament", new ConfigOptionFloat (this->total_used_filament / 1000.));
config.set_key_value("extruded_volume", new ConfigOptionFloat (this->total_extruded_volume));
config.set_key_value("total_cost", new ConfigOptionFloat (this->total_cost));
config.set_key_value("total_weight", new ConfigOptionFloat (this->total_weight));
@ -2076,7 +2076,7 @@ DynamicConfig PrintStatistics::placeholders()
"print_time", "normal_print_time", "silent_print_time",
"used_filament", "extruded_volume", "total_cost", "total_weight",
"total_wipe_tower_cost", "total_wipe_tower_filament"})
config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}"));
config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}"));
return config;
}

View File

@ -5,8 +5,8 @@
#include <functional>
#include <numeric>
#include "ExPolygon.hpp"
#include "TriangleMesh.hpp"
#include <libslic3r/ExPolygon.hpp>
#include <libslic3r/TriangleMesh.hpp>
namespace Slic3r {
namespace sla {
@ -53,7 +53,7 @@ struct Contour3D {
void merge(const Contour3D& ctr) {
auto s3 = coord_t(points.size());
auto s = coord_t(indices.size());
auto s = indices.size();
points.insert(points.end(), ctr.points.begin(), ctr.points.end());
indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end());
@ -62,6 +62,17 @@ struct Contour3D {
auto& idx = indices[n]; x(idx) += s3; y(idx) += s3; z(idx) += s3;
}
}
// Write the index triangle structure to OBJ file for debugging purposes.
void to_obj(std::ostream& stream) {
for(auto& p : points) {
stream << "v " << p.transpose() << "\n";
}
for(auto& f : indices) {
stream << "f " << (f + Vec3i(1, 1, 1)).transpose() << "\n";
}
}
};
//using PointSet = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::DontAlign>; //Eigen::MatrixXd;

View File

@ -9,8 +9,8 @@
#include "SLASpatIndex.hpp"
#include "SLABasePool.hpp"
#include "ClipperUtils.hpp"
#include "Model.hpp"
#include <libslic3r/ClipperUtils.hpp>
#include <libslic3r/Model.hpp>
#include <boost/log/trivial.hpp>
@ -164,7 +164,13 @@ Contour3D sphere(double rho, Portion portion = make_portion(0.0, 2.0*PI),
return ret;
}
Contour3D cylinder(double r, double h, size_t ssteps) {
// Down facing cylinder in Z direction with arguments:
// r: radius
// h: Height
// ssteps: how many edges will create the base circle
// sp: starting point
Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d sp = {0,0,0})
{
Contour3D ret;
auto steps = int(ssteps);
@ -173,9 +179,10 @@ Contour3D cylinder(double r, double h, size_t ssteps) {
points.reserve(2*ssteps);
double a = 2*PI/steps;
Vec3d jp = {0, 0, 0};
Vec3d endp = {0, 0, h};
Vec3d jp = sp;
Vec3d endp = {sp(X), sp(Y), sp(Z) + h};
// Upper circle points
for(int i = 0; i < steps; ++i) {
double phi = i*a;
double ex = endp(X) + r*std::cos(phi);
@ -183,6 +190,7 @@ Contour3D cylinder(double r, double h, size_t ssteps) {
points.emplace_back(ex, ey, endp(Z));
}
// Lower circle points
for(int i = 0; i < steps; ++i) {
double phi = i*a;
double x = jp(X) + r*std::cos(phi);
@ -190,6 +198,7 @@ Contour3D cylinder(double r, double h, size_t ssteps) {
points.emplace_back(x, y, jp(Z));
}
// Now create long triangles connecting upper and lower circles
indices.reserve(2*ssteps);
auto offs = steps;
for(int i = 0; i < steps - 1; ++i) {
@ -197,10 +206,26 @@ Contour3D cylinder(double r, double h, size_t ssteps) {
indices.emplace_back(i, offs + i + 1, i + 1);
}
// Last triangle connecting the first and last vertices
auto last = steps - 1;
indices.emplace_back(0, last, offs);
indices.emplace_back(last, offs + last, offs);
// According to the slicing algorithms, we need to aid them with generating
// a watertight body. So we create a triangle fan for the upper and lower
// ending of the cylinder to close the geometry.
points.emplace_back(jp); size_t ci = points.size() - 1;
for(int i = 0; i < steps - 1; ++i)
indices.emplace_back(i + offs + 1, i + offs, ci);
indices.emplace_back(offs, steps + offs - 1, ci);
points.emplace_back(endp); ci = points.size() - 1;
for(int i = 0; i < steps - 1; ++i)
indices.emplace_back(ci, i, i + 1);
indices.emplace_back(steps - 1, 0, ci);
return ret;
}
@ -352,36 +377,15 @@ struct Pillar {
r(radius), steps(st), endpoint(endp), starts_from_head(false)
{
assert(steps > 0);
int steps_1 = int(steps - 1);
auto& points = mesh.points;
auto& indices = mesh.indices;
points.reserve(2*steps);
double a = 2*PI/steps;
double h = jp(Z) - endp(Z);
assert(h > 0); // Endpoint is below the starting point
for(size_t i = 0; i < steps; ++i) {
double phi = i*a;
double x = jp(X) + r*std::cos(phi);
double y = jp(Y) + r*std::sin(phi);
points.emplace_back(x, y, jp(Z));
}
for(size_t i = 0; i < steps; ++i) {
double phi = i*a;
double ex = endp(X) + r*std::cos(phi);
double ey = endp(Y) + r*std::sin(phi);
points.emplace_back(ex, ey, endp(Z));
}
indices.reserve(2*steps);
int offs = int(steps);
for(int i = 0; i < steps_1 ; ++i) {
indices.emplace_back(i, i + offs, offs + i + 1);
indices.emplace_back(i, offs + i + 1, i + 1);
}
indices.emplace_back(0, steps_1, offs);
indices.emplace_back(steps_1, offs + steps_1, offs);
// We just create a bridge geometry with the pillar parameters and
// move the data.
Contour3D body = cylinder(radius, h, st, endp);
mesh.points.swap(body.points);
mesh.indices.swap(body.indices);
}
Pillar(const Junction& junc, const Vec3d& endp):
@ -1387,7 +1391,8 @@ bool SLASupportTree::generate(const PointSet &points,
}
double d = distance(jp, jn);
if(jn(Z) <= gndlvl || d > max_len) break;
if(jn(Z) <= (gndlvl + 2*cfg.head_width_mm) || d > max_len)
break;
double chkd = ray_mesh_intersect(jp, dirv(jp, jn), emesh);
if(chkd >= d) nearest_id = ne.second;

View File

@ -440,10 +440,6 @@ std::vector<float> SLAPrint::calculate_heights(const BoundingBoxf3& bb3d,
auto flh = float(layer_height);
auto gnd = float(bb3d.min(Z));
// The first layer (the one before the initial height) is added only
// if there is no pad and no elevation value
if(minZ >= gnd) heights.emplace_back(minZ);
for(float h = minZ + initial_layer_height; h < maxZ; h += flh)
if(h >= gnd) heights.emplace_back(h);
@ -510,6 +506,9 @@ void SLAPrint::process()
po.m_supportdata.reset(new SLAPrintObject::SupportData());
po.m_supportdata->emesh = sla::to_eigenmesh(po.transformed_mesh());
// If supports are disabled, we can skip the model scan.
if(!po.m_config.supports_enable.getBool()) return;
BOOST_LOG_TRIVIAL(debug) << "Support point count "
<< mo.sla_support_points.size();
@ -693,7 +692,6 @@ void SLAPrint::process()
// model_slice method. Only difference is that here it works with
// scaled coordinates
po.m_level_ids.clear();
if(sminZ >= smodelgnd) po.m_level_ids.emplace_back(sminZ);
for(LevelID h = sminZ + sih; h < smaxZ; h += slh)
if(h >= smodelgnd) po.m_level_ids.emplace_back(h);

View File

@ -525,67 +525,22 @@ BoundingBoxf3 TriangleMesh::bounding_box() const
return bb;
}
BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& t) const
BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) const
{
bool has_shared = (stl.v_shared != nullptr);
if (!has_shared)
stl_generate_shared_vertices(const_cast<stl_file*>(&stl));
unsigned int vertices_count = (stl.stats.shared_vertices > 0) ? (unsigned int)stl.stats.shared_vertices : 3 * (unsigned int)stl.stats.number_of_facets;
if (vertices_count == 0)
return BoundingBoxf3();
Eigen::MatrixXd src_vertices(3, vertices_count);
if (stl.stats.shared_vertices > 0)
{
assert(stl.v_shared != nullptr);
stl_vertex* vertex_ptr = stl.v_shared;
for (int i = 0; i < stl.stats.shared_vertices; ++i)
{
src_vertices(0, i) = (double)(*vertex_ptr)(0);
src_vertices(1, i) = (double)(*vertex_ptr)(1);
src_vertices(2, i) = (double)(*vertex_ptr)(2);
vertex_ptr += 1;
BoundingBoxf3 bbox;
if (stl.v_shared == nullptr) {
// Using the STL faces.
for (int i = 0; i < this->facets_count(); ++ i) {
const stl_facet &facet = this->stl.facet_start[i];
for (size_t j = 0; j < 3; ++ j)
bbox.merge(trafo * facet.vertex[j].cast<double>());
}
} else {
// Using the shared vertices should be a bit quicker than using the STL faces.
for (int i = 0; i < stl.stats.shared_vertices; ++ i)
bbox.merge(trafo * this->stl.v_shared[i].cast<double>());
}
else
{
stl_facet* facet_ptr = stl.facet_start;
unsigned int v_id = 0;
while (facet_ptr < stl.facet_start + stl.stats.number_of_facets)
{
for (int i = 0; i < 3; ++i)
{
src_vertices(0, v_id) = (double)facet_ptr->vertex[i](0);
src_vertices(1, v_id) = (double)facet_ptr->vertex[i](1);
src_vertices(2, v_id) = (double)facet_ptr->vertex[i](2);
++v_id;
}
facet_ptr += 1;
}
}
if (!has_shared && (stl.stats.shared_vertices > 0))
stl_invalidate_shared_vertices(const_cast<stl_file*>(&stl));
Eigen::MatrixXd dst_vertices(3, vertices_count);
dst_vertices = t * src_vertices.colwise().homogeneous();
Vec3d v_min(dst_vertices(0, 0), dst_vertices(1, 0), dst_vertices(2, 0));
Vec3d v_max = v_min;
for (int i = 1; i < vertices_count; ++i)
{
for (int j = 0; j < 3; ++j)
{
v_min(j) = std::min(v_min(j), dst_vertices(j, i));
v_max(j) = std::max(v_max(j), dst_vertices(j, i));
}
}
return BoundingBoxf3(v_min, v_max);
return bbox;
}
TriangleMesh TriangleMesh::convex_hull_3d() const
@ -2010,4 +1965,5 @@ TriangleMesh make_sphere(double rho, double fa) {
TriangleMesh mesh(vertices, facets);
return mesh;
}
}

View File

@ -60,7 +60,7 @@ public:
Polygon convex_hull();
BoundingBoxf3 bounding_box() const;
// Returns the bbox of this TriangleMesh transformed by the given transformation
BoundingBoxf3 transformed_bounding_box(const Transform3d& t) const;
BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const;
// Returns the convex hull of this TriangleMesh
TriangleMesh convex_hull_3d() const;
void reset_repair_stats();

View File

@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.6)
include(PrecompiledHeader)
add_library(libslic3r_gui STATIC
set(SLIC3R_GUI_SOURCES
pchheader.cpp
pchheader.hpp
GUI/AboutDialog.cpp
@ -127,6 +127,12 @@ add_library(libslic3r_gui STATIC
Utils/HexFile.hpp
)
if (APPLE)
list(APPEND SLIC3R_GUI_SOURCES Utils/RetinaHelperImpl.mm)
endif ()
add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
target_link_libraries(libslic3r_gui libslic3r avrdude imgui)
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)

View File

@ -59,6 +59,11 @@ void AppConfig::set_defaults()
if (get("use_legacy_opengl").empty())
set("use_legacy_opengl", "0");
#if __APPLE__
if (get("use_retina_opengl").empty())
set("use_retina_opengl", "1");
#endif
if (get("remember_output_path").empty())
set("remember_output_path", "1");

View File

@ -437,12 +437,10 @@ void BackgroundSlicingProcess::prepare_upload()
if (m_print == m_fff_print) {
m_print->set_status(95, "Running post-processing scripts");
run_post_process_scripts(source_path.string(), m_fff_print->config());
if (copy_file(m_temp_output_path, source_path.string()) != 0) {
throw std::runtime_error("Copying of the temporary G-code to the output G-code failed");
}
run_post_process_scripts(source_path.string(), m_fff_print->config());
m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
} else {
m_sla_print->export_raster<SLAZipFmt>(source_path.string());

View File

@ -143,9 +143,11 @@ void Field::get_value_by_opt_type(wxString& str)
break;
}
double val;
// Replace the first occurence of comma in decimal number.
str.Replace(",", ".", false);
if(!str.ToCDouble(&val))
{
show_error(m_parent, _(L("Input value contains incorrect symbol(s).\nUse, please, only digits")));
show_error(m_parent, _(L("Invalid numeric input.")));
set_value(double_to_string(val), true);
}
if (m_opt.min > val || val > m_opt.max)
@ -163,10 +165,12 @@ void Field::get_value_by_opt_type(wxString& str)
if (m_opt.type == coFloatOrPercent && !str.IsEmpty() && str.Last() != '%')
{
double val;
// Replace the first occurence of comma in decimal number.
str.Replace(",", ".", false);
if (!str.ToCDouble(&val))
{
show_error(m_parent, _(L("Input value contains incorrect symbol(s).\nUse, please, only digits")));
set_value(double_to_string(val), true);
show_error(m_parent, _(L("Invalid numeric input.")));
set_value(double_to_string(val), true);
}
else if (m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max ||
m_opt.sidetext.rfind("mm ") != std::string::npos && val > 1)
@ -334,6 +338,7 @@ void TextCtrl::propagate_value()
boost::any& TextCtrl::get_value()
{
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
// modifies ret_string!
get_value_by_opt_type(ret_str);
return m_value;
@ -760,11 +765,13 @@ boost::any& Choice::get_value()
else if (m_opt.gui_type == "f_enum_open") {
const int ret_enum = static_cast<wxComboBox*>(window)->GetSelection();
if (ret_enum < 0 || m_opt.enum_values.empty() || m_opt.type == coStrings)
// modifies ret_string!
get_value_by_opt_type(ret_str);
else
m_value = atof(m_opt.enum_values[ret_enum].c_str());
}
else
// modifies ret_string!
get_value_by_opt_type(ret_str);
return m_value;

View File

@ -115,7 +115,7 @@ public:
/// subclasses should overload with a specific version
/// Postcondition: Method does not fire the on_change event.
virtual void set_value(const boost::any& value, bool change_event) = 0;
/// Gets a boost::any representing this control.
/// subclasses should overload with a specific version
virtual boost::any& get_value() = 0;
@ -128,6 +128,8 @@ public:
virtual wxString get_tooltip_text(const wxString& default_string);
void field_changed() { on_change_field(); }
// set icon to "UndoToSystemValue" button according to an inheritance of preset
// void set_nonsys_btn_icon(const wxBitmap& icon);

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,9 @@
#define slic3r_GLCanvas3D_hpp_
#include <stddef.h>
#include <memory>
#include "libslic3r/Technologies.hpp"
#include "3DScene.hpp"
#include "GLToolbar.hpp"
#include "Event.hpp"
@ -20,6 +22,9 @@ class wxTimerEvent;
class wxPaintEvent;
class wxGLCanvas;
// Support for Retina OpenGL on Mac OS
#define ENABLE_RETINA_GL __APPLE__
class GLUquadric;
typedef class GLUquadric GLUquadricObj;
@ -36,6 +41,10 @@ namespace GUI {
class GLGizmoBase;
#if ENABLE_RETINA_GL
class RetinaHelper;
#endif
class GeometryBuffer
{
std::vector<float> m_vertices;
@ -55,16 +64,20 @@ class Size
{
int m_width;
int m_height;
float m_scale_factor;
public:
Size();
Size(int width, int height);
Size(int width, int height, float scale_factor = 1.0);
int get_width() const;
void set_width(int width);
int get_height() const;
void set_height(int height);
int get_scale_factor() const;
void set_scale_factor(int height);
};
class Rect
@ -209,6 +222,8 @@ class GLCanvas3D
mutable GLBed m_model;
#endif // ENABLE_PRINT_BED_MODELS
mutable float m_scale_factor;
public:
Bed();
@ -224,9 +239,9 @@ class GLCanvas3D
Point point_projection(const Point& point) const;
#if ENABLE_PRINT_BED_MODELS
void render(float theta, bool useVBOs) const;
void render(float theta, bool useVBOs, float scale_factor) const;
#else
void render(float theta) const;
void render(float theta, float scale_factor) const;
#endif // ENABLE_PRINT_BED_MODELS
private:
@ -297,6 +312,9 @@ class GLCanvas3D
};
private:
static const float THICKNESS_BAR_WIDTH;
static const float THICKNESS_RESET_BUTTON_HEIGHT;
bool m_use_legacy_opengl;
bool m_enabled;
Shader m_shader;
@ -380,6 +398,9 @@ class GLCanvas3D
void _render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const;
void _render_profile(const Rect& bar_rect) const;
void update_slicing_parameters();
static float thickness_bar_width(const GLCanvas3D &canvas);
static float reset_button_height(const GLCanvas3D &canvas);
};
struct Mouse
@ -536,6 +557,8 @@ public:
mutable GLArrow m_arrow;
mutable GLCurvedArrow m_curved_arrow;
mutable float m_scale_factor;
public:
Selection();
#if ENABLE_RENDER_SELECTION_CENTER
@ -617,7 +640,7 @@ public:
void erase();
void render() const;
void render(float scale_factor = 1.0) const;
#if ENABLE_RENDER_SELECTION_CENTER
void render_center() const;
#endif // ENABLE_RENDER_SELECTION_CENTER
@ -647,7 +670,15 @@ public:
void _render_sidebar_rotation_hint(Axis axis) const;
void _render_sidebar_scale_hint(Axis axis) const;
void _render_sidebar_size_hint(Axis axis, double length) const;
void _synchronize_unselected_instances(bool including_z = false);
enum SyncRotationType {
// Do not synchronize rotation. Either not rotating at all, or rotating by world Z axis.
SYNC_ROTATION_NONE = 0,
// Synchronize fully. Used from "place on bed" feature.
SYNC_ROTATION_FULL = 1,
// Synchronize after rotation by an axis not parallel with Z.
SYNC_ROTATION_GENERAL = 2,
};
void _synchronize_unselected_instances(SyncRotationType sync_rotation_type);
void _synchronize_unselected_volumes();
void _ensure_on_bed();
};
@ -680,10 +711,6 @@ public:
private:
class Gizmos
{
static const float OverlayIconsScale;
static const float OverlayBorder;
static const float OverlayGapY;
public:
enum EType : unsigned char
{
@ -704,6 +731,10 @@ private:
BackgroundTexture m_background_texture;
EType m_current;
float m_overlay_icons_scale;
float m_overlay_border;
float m_overlay_gap_y;
public:
Gizmos();
~Gizmos();
@ -713,6 +744,8 @@ private:
bool is_enabled() const;
void set_enabled(bool enable);
void set_overlay_scale(float scale);
std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection);
void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection);
void update_on_off_state(const Selection& selection);
@ -802,7 +835,7 @@ private:
public:
WarningTexture();
bool generate(const std::string& msg);
bool generate(const std::string& msg, const GLCanvas3D& canvas);
void render(const GLCanvas3D& canvas) const;
};
@ -832,6 +865,9 @@ private:
wxGLCanvas* m_canvas;
wxGLContext* m_context;
#if ENABLE_RETINA_GL
std::unique_ptr<RetinaHelper> m_retina_helper;
#endif
bool m_in_render;
LegendTexture m_legend_texture;
WarningTexture m_warning_texture;
@ -1030,6 +1066,8 @@ public:
void handle_sidebar_focus_event(const std::string& opt_key, bool focus_on);
void update_ui_from_settings();
private:
bool _is_shown_on_screen() const;
#if !ENABLE_REWORKED_BED_SHAPE_CHANGE

View File

@ -1428,6 +1428,7 @@ void GLGizmoFlatten::on_start_dragging(const GLCanvas3D::Selection& selection)
{
if (m_hover_id != -1)
{
assert(m_planes_valid);
m_normal = m_planes[m_hover_id].normal;
m_starting_center = selection.get_bounding_box().center();
}
@ -1446,6 +1447,8 @@ void GLGizmoFlatten::on_render(const GLCanvas3D::Selection& selection) const
::glPushMatrix();
::glMultMatrixd(m.data());
::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z());
if (this->is_plane_update_necessary())
const_cast<GLGizmoFlatten*>(this)->update_planes();
for (int i = 0; i < (int)m_planes.size(); ++i)
{
if (i == m_hover_id)
@ -1478,6 +1481,8 @@ void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selectio
::glPushMatrix();
::glMultMatrixd(m.data());
::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z());
if (this->is_plane_update_necessary())
const_cast<GLGizmoFlatten*>(this)->update_planes();
for (int i = 0; i < (int)m_planes.size(); ++i)
{
::glColor3f(1.0f, 1.0f, picking_color_component(i));
@ -1497,11 +1502,11 @@ void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selectio
void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
{
m_starting_center = Vec3d::Zero();
bool object_changed = m_model_object != model_object;
if (m_model_object != model_object) {
m_planes.clear();
m_planes_valid = false;
}
m_model_object = model_object;
if (model_object && (object_changed || is_plane_update_necessary()))
update_planes();
}
void GLGizmoFlatten::update_planes()
@ -1701,6 +1706,8 @@ void GLGizmoFlatten::update_planes()
}
m_first_instance_scale = m_model_object->instances.front()->get_scaling_factor();
m_first_instance_mirror = m_model_object->instances.front()->get_mirror();
m_planes_valid = true;
}
@ -1709,7 +1716,7 @@ bool GLGizmoFlatten::is_plane_update_necessary() const
if (m_state != On || !m_model_object || m_model_object->instances.empty())
return false;
if (m_model_object->volumes.size() != m_volumes_matrices.size())
if (! m_planes_valid || m_model_object->volumes.size() != m_volumes_matrices.size())
return true;
// We want to recalculate when the scale changes - some planes could (dis)appear.

View File

@ -408,6 +408,7 @@ private:
Vec3d m_first_instance_mirror;
std::vector<PlaneData> m_planes;
bool m_planes_valid = false;
mutable Vec3d m_starting_center;
const ModelObject* m_model_object = nullptr;
std::vector<const Transform3d*> instances_matrices;

View File

@ -468,12 +468,12 @@ float GLToolbar::get_width_horizontal() const
float GLToolbar::get_width_vertical() const
{
return 2.0f * m_layout.border + m_icons_texture.metadata.icon_size * m_layout.icons_scale;
return 2.0f * m_layout.border * m_layout.icons_scale + m_icons_texture.metadata.icon_size * m_layout.icons_scale;
}
float GLToolbar::get_height_horizontal() const
{
return 2.0f * m_layout.border + m_icons_texture.metadata.icon_size * m_layout.icons_scale;
return 2.0f * m_layout.border * m_layout.icons_scale + m_icons_texture.metadata.icon_size * m_layout.icons_scale;
}
float GLToolbar::get_height_vertical() const
@ -483,33 +483,36 @@ float GLToolbar::get_height_vertical() const
float GLToolbar::get_main_size() const
{
float size = 2.0f * m_layout.border;
float size = 2.0f * m_layout.border * m_layout.icons_scale;
for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i)
{
if (m_items[i]->is_separator())
size += m_layout.separator_size;
size += m_layout.separator_size * m_layout.icons_scale;
else
size += (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale;
}
if (m_items.size() > 1)
size += ((float)m_items.size() - 1.0f) * m_layout.gap_size;
size += ((float)m_items.size() - 1.0f) * m_layout.gap_size * m_layout.icons_scale;
return size;
}
std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent)
{
// NB: mouse_pos is already scaled appropriately
float zoom = parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float factor = m_layout.icons_scale * inv_zoom;
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom;
float scaled_separator_size = m_layout.separator_size * inv_zoom;
float scaled_gap_size = m_layout.gap_size * inv_zoom;
float scaled_border = m_layout.border * inv_zoom;
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
@ -591,16 +594,19 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC
std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent)
{
// NB: mouse_pos is already scaled appropriately
float zoom = parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float factor = m_layout.icons_scale * inv_zoom;
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom;
float scaled_separator_size = m_layout.separator_size * inv_zoom;
float scaled_gap_size = m_layout.gap_size * inv_zoom;
float scaled_border = m_layout.border * inv_zoom;
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
@ -682,16 +688,19 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan
int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const
{
// NB: mouse_pos is already scaled appropriately
float zoom = parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float factor = m_layout.icons_scale * inv_zoom;
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom;
float scaled_separator_size = m_layout.separator_size * inv_zoom;
float scaled_gap_size = m_layout.gap_size * inv_zoom;
float scaled_border = m_layout.border * inv_zoom;
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
@ -724,16 +733,19 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3
int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const
{
// NB: mouse_pos is already scaled appropriately
float zoom = parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float factor = m_layout.icons_scale * inv_zoom;
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom;
float scaled_separator_size = m_layout.separator_size * inv_zoom;
float scaled_gap_size = m_layout.gap_size * inv_zoom;
float scaled_border = m_layout.border * inv_zoom;
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
@ -774,11 +786,12 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
float zoom = parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float factor = inv_zoom * m_layout.icons_scale;
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom;
float scaled_separator_size = m_layout.separator_size * inv_zoom;
float scaled_gap_size = m_layout.gap_size * inv_zoom;
float scaled_border = m_layout.border * inv_zoom;
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
float scaled_width = get_width() * inv_zoom;
float scaled_height = get_height() * inv_zoom;
@ -899,11 +912,12 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const
float zoom = parent.get_camera_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float factor = inv_zoom * m_layout.icons_scale;
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom;
float scaled_separator_size = m_layout.separator_size * inv_zoom;
float scaled_gap_size = m_layout.gap_size * inv_zoom;
float scaled_border = m_layout.border * inv_zoom;
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * factor;
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
float scaled_width = get_width() * inv_zoom;
float scaled_height = get_height() * inv_zoom;

View File

@ -270,16 +270,11 @@ void GUI_App::set_label_clr_sys(const wxColour& clr) {
void GUI_App::recreate_GUI()
{
std::cerr << "recreate_GUI" << std::endl;
// to make sure nobody accesses data from the soon-to-be-destroyed widgets:
tabs_list.clear();
plater_ = nullptr;
clear_tabs_list();
if (plater_) {
// before creating a new plater let's delete old one
plater_->Destroy();
plater_ = nullptr;
}
MainFrame* topwindow = dynamic_cast<MainFrame*>(GetTopWindow());
MainFrame* topwindow = mainframe;
mainframe = new MainFrame();
sidebar().obj_list()->init_objects(); // propagate model objects to object list
@ -691,15 +686,6 @@ void GUI_App::load_current_presets()
}
}
void GUI_App::clear_tabs_list()
{
for (auto tab : tabs_list) {
tab->Destroy();
tab = nullptr;
}
tabs_list.clear();
}
#ifdef __APPLE__
// wxWidgets override to get an event on open files.
void GUI_App::MacOpenFiles(const wxArrayString &fileNames)

View File

@ -134,7 +134,6 @@ public:
bool check_unsaved_changes();
bool checked_tab(Tab* tab);
void load_current_presets();
void clear_tabs_list();
#ifdef __APPLE__
// wxWidgets override to get an event on open files.

View File

@ -21,8 +21,6 @@ namespace GUI
wxDEFINE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
typedef std::map<std::string, std::vector<std::string>> FreqSettingsBundle;
// pt_FFF
FreqSettingsBundle FREQ_SETTINGS_BUNDLE_FFF =
{
@ -587,9 +585,9 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
// Context Menu
std::vector<std::string> get_options(const bool is_part, const bool is_sla)
std::vector<std::string> ObjectList::get_options(const bool is_part)
{
if (is_sla) {
if (wxGetApp().plater()->printer_technology() == ptSLA) {
SLAPrintObjectConfig full_sla_config;
auto options = full_sla_config.keys();
options.erase(find(options.begin(), options.end(), "layer_height"));
@ -605,13 +603,8 @@ std::vector<std::string> get_options(const bool is_part, const bool is_sla)
}
return options;
}
std::vector<std::string> get_options(const bool is_part)
{
return get_options(is_part, wxGetApp().plater()->printer_technology() == ptSLA);
}
const std::vector<std::string>& get_options_for_bundle(const wxString& bundle_name)
const std::vector<std::string>& ObjectList::get_options_for_bundle(const wxString& bundle_name)
{
const FreqSettingsBundle& bundle = wxGetApp().plater()->printer_technology() == ptSLA ?
FREQ_SETTINGS_BUNDLE_SLA : FREQ_SETTINGS_BUNDLE_FFF;
@ -621,14 +614,25 @@ const std::vector<std::string>& get_options_for_bundle(const wxString& bundle_na
if (bundle_name == _(it.first))
return it.second;
}
return std::vector<std::string> {};
#if 0
// if "Quick menu" is selected
FreqSettingsBundle& bundle_quick = wxGetApp().plater()->printer_technology() == ptSLA ?
m_freq_settings_sla: m_freq_settings_fff;
for (auto& it : bundle_quick)
{
if ( bundle_name == wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)) )
return it.second;
}
#endif
static std::vector<std::string> empty;
return empty;
}
// category -> vector ( option ; label )
typedef std::map< std::string, std::vector< std::pair<std::string, std::string> > > settings_menu_hierarchy;
void get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part, const bool is_sla)
void ObjectList::get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part)
{
auto options = get_options(is_part, is_sla);
auto options = get_options(is_part);
auto extruders_cnt = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA ? 1 :
wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
@ -653,11 +657,6 @@ void get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part
}
}
void get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part)
{
get_options_menu(settings_menu, is_part, wxGetApp().plater()->printer_technology() == ptSLA);
}
void ObjectList::get_settings_choice(const wxString& category_name)
{
wxArrayString names;
@ -691,10 +690,59 @@ void ObjectList::get_settings_choice(const wxString& category_name)
if (wxGetSelectedChoices(selections, _(L("Select showing settings")), category_name, names) == -1)
return;
const int selection_cnt = selections.size();
#if 0
if (selection_cnt > 0)
{
// Add selected items to the "Quick menu"
FreqSettingsBundle& freq_settings = wxGetApp().plater()->printer_technology() == ptSLA ?
m_freq_settings_sla : m_freq_settings_fff;
bool changed_existing = false;
std::vector<std::string> tmp_freq_cat = {};
for (auto& cat : freq_settings)
{
if (_(cat.first) == category_name)
{
std::vector<std::string>& freq_settings_category = cat.second;
freq_settings_category.clear();
freq_settings_category.reserve(selection_cnt);
for (auto sel : selections)
freq_settings_category.push_back((*settings_list)[sel].first);
changed_existing = true;
break;
}
}
if (!changed_existing)
{
// Create new "Quick menu" item
for (auto& cat : settings_menu)
{
if (_(cat.first) == category_name)
{
freq_settings[cat.first] = std::vector<std::string> {};
std::vector<std::string>& freq_settings_category = freq_settings.find(cat.first)->second;
freq_settings_category.reserve(selection_cnt);
for (auto sel : selections)
freq_settings_category.push_back((*settings_list)[sel].first);
break;
}
}
}
}
#endif
std::vector <std::string> selected_options;
selected_options.reserve(selection_cnt);
for (auto sel : selections)
selected_options.push_back((*settings_list)[sel].first);
const DynamicPrintConfig& from_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
for (auto& setting : (*settings_list))
{
auto& opt_key = setting.first;
@ -703,8 +751,17 @@ void ObjectList::get_settings_choice(const wxString& category_name)
m_config->erase(opt_key);
if (find(opt_keys.begin(), opt_keys.end(), opt_key) == opt_keys.end() &&
find(selected_options.begin(), selected_options.end(), opt_key) != selected_options.end())
m_config->set_key_value(opt_key, m_default_config->option(opt_key)->clone());
find(selected_options.begin(), selected_options.end(), opt_key) != selected_options.end()) {
const ConfigOption* option = from_config.option(opt_key);
if (!option) {
// if current option doesn't exist in prints.get_edited_preset(),
// get it from m_default_config
if (m_default_config) delete m_default_config;
m_default_config = DynamicPrintConfig::new_from_defaults_keys(get_options(false));
option = m_default_config->option(opt_key);
}
m_config->set_key_value(opt_key, option->clone());
}
}
@ -718,10 +775,20 @@ void ObjectList::get_freq_settings_choice(const wxString& bundle_name)
auto opt_keys = m_config->keys();
const DynamicPrintConfig& from_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
for (auto& opt_key : options)
{
if ( find(opt_keys.begin(), opt_keys.end(), opt_key) == opt_keys.end() )
m_config->set_key_value(opt_key, m_default_config->option(opt_key)->clone());
if (find(opt_keys.begin(), opt_keys.end(), opt_key) == opt_keys.end()) {
const ConfigOption* option = from_config.option(opt_key);
if (!option) {
// if current option doesn't exist in prints.get_edited_preset(),
// get it from m_default_config
if (m_default_config) delete m_default_config;
m_default_config = DynamicPrintConfig::new_from_defaults_keys(get_options(false));
option = m_default_config->option(opt_key);
}
m_config->set_key_value(opt_key, option->clone());
}
}
// Add settings item for object
@ -835,7 +902,20 @@ wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_)
if (settings_id != wxNOT_FOUND)
menu->Destroy(settings_id);
}
#if 0
for (auto& it : m_freq_settings_fff)
{
settings_id = menu->FindItem(wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)));
if (settings_id != wxNOT_FOUND)
menu->Destroy(settings_id);
}
for (auto& it : m_freq_settings_sla)
{
settings_id = menu->FindItem(wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)));
if (settings_id != wxNOT_FOUND)
menu->Destroy(settings_id);
}
#endif
menu->DestroySeparators(); // delete old separators
const auto sel_vol = get_selected_model_volume();
@ -937,6 +1017,7 @@ wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu)
void ObjectList::create_freq_settings_popupmenu(wxMenu *menu)
{
// Add default settings bundles
const FreqSettingsBundle& bundle = wxGetApp().plater()->printer_technology() == ptFFF ?
FREQ_SETTINGS_BUNDLE_FFF : FREQ_SETTINGS_BUNDLE_SLA;
@ -951,6 +1032,20 @@ void ObjectList::create_freq_settings_popupmenu(wxMenu *menu)
[menu, this](wxCommandEvent& event) { get_freq_settings_choice(menu->GetLabel(event.GetId())); },
CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu);
}
#if 0
// Add "Quick" settings bundles
const FreqSettingsBundle& bundle_quick = wxGetApp().plater()->printer_technology() == ptFFF ?
m_freq_settings_fff : m_freq_settings_sla;
for (auto& it : bundle_quick) {
if (it.first.empty() || it.first == "Extruders" && extruders_cnt == 1)
continue;
append_menu_item(menu, wxID_ANY, wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)), "",
[menu, this](wxCommandEvent& event) { get_freq_settings_choice(menu->GetLabel(event.GetId())); },
CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu);
}
#endif
}
void ObjectList::update_opt_keys(t_config_option_keys& opt_keys)
@ -1276,15 +1371,12 @@ bool ObjectList::is_splittable()
if (!get_volume_by_item(item, volume) || !volume)
return false;
if (volume->is_splittable() != -1) // if is_splittable value is already known
return volume->is_splittable() == 0 ? false : true;
TriangleMeshPtrs meshptrs = volume->mesh.split();
bool splittable = meshptrs.size() > 1;
for (TriangleMesh* m : meshptrs) { delete m; }
volume->set_splittable(splittable ? 1 : 0);
return splittable;
int splittable = volume->is_splittable();
if (splittable == -1) {
splittable = (int)volume->mesh.has_multiple_patches();
volume->set_splittable(splittable);
}
return splittable != 0;
}
bool ObjectList::selected_instances_of_same_object()
@ -1381,9 +1473,6 @@ void ObjectList::part_selection_changed()
m_config = &(*m_objects)[obj_idx_]->config;
}
}
if (m_default_config) delete m_default_config;
m_default_config = DynamicPrintConfig::new_from_defaults_keys(get_options(is_part));
}
}

View File

@ -25,6 +25,11 @@ class ModelVolume;
// FIXME: broken build on mac os because of this is missing:
typedef std::vector<std::string> t_config_option_keys;
typedef std::map<std::string, std::vector<std::string>> FreqSettingsBundle;
// category -> vector ( option ; label )
typedef std::map< std::string, std::vector< std::pair<std::string, std::string> > > settings_menu_hierarchy;
namespace GUI {
wxDECLARE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
@ -129,6 +134,11 @@ class ObjectList : public wxDataViewCtrl
int m_selected_row = 0;
#if 0
FreqSettingsBundle m_freq_settings_fff;
FreqSettingsBundle m_freq_settings_sla;
#endif
public:
ObjectList(wxWindow* parent);
~ObjectList();
@ -264,6 +274,10 @@ private:
void ItemValueChanged(wxDataViewEvent &event);
void OnEditingDone(wxDataViewEvent &event);
std::vector<std::string> get_options(const bool is_part);
const std::vector<std::string>& get_options_for_bundle(const wxString& bundle_name);
void get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part);
};

View File

@ -39,19 +39,13 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
change_rotation_value(new_value);
else if (param == "scale")
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
{
change_scale_value(new_value);
update_settings_value(wxGetApp().plater()->canvas3D()->get_selection());
}
#else
change_scale_value(new_value);
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
else if (param == "size")
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
{
change_size_value(new_value);
update_settings_value(wxGetApp().plater()->canvas3D()->get_selection());
}
#else
change_size_value(new_value);
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
@ -267,7 +261,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele
bool changed_box = false;
if (!m_cache.instance.matches_object(obj_idx))
{
m_cache.instance.set(obj_idx, instance_idx, (*wxGetApp().model_objects())[obj_idx]->raw_mesh().bounding_box().size());
m_cache.instance.set(obj_idx, instance_idx, (*wxGetApp().model_objects())[obj_idx]->raw_mesh_bounding_box().size());
changed_box = true;
}
if (changed_box || !m_cache.instance.matches_instance(instance_idx) || !m_cache.scale.isApprox(100.0 * m_new_scale))
@ -278,7 +272,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele
m_new_size = Vec3d::Zero();
#else
if ((0 <= obj_idx) && (obj_idx < (int)wxGetApp().model_objects()->size()))
m_new_size = volume->get_instance_transformation().get_matrix(true, true) * (*wxGetApp().model_objects())[obj_idx]->raw_mesh().bounding_box().size();
m_new_size = volume->get_instance_transformation().get_matrix(true, true) * (*wxGetApp().model_objects())[obj_idx]->raw_mesh_bounding_box().size();
else
// this should never happen
m_new_size = Vec3d::Zero();
@ -548,18 +542,16 @@ void ObjectManipulation::change_size_value(const Vec3d& size)
{
const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
Vec3d ref_size = m_cache.size;
#else
Vec3d ref_size = m_cache_size;
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
if (selection.is_single_full_instance())
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
if (selection.is_single_volume() || selection.is_single_modifier())
{
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
ref_size = volume->bounding_box.size();
}
else if (selection.is_single_full_instance())
ref_size = m_cache.instance.box_size;
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
Vec3d scale = 100.0 * Vec3d(size(0) / ref_size(0), size(1) / ref_size(1), size(2) / ref_size(2));
Vec3d scaling_factor = scale;
@ -590,6 +582,12 @@ void ObjectManipulation::change_size_value(const Vec3d& size)
m_cache.size = size;
#else
if (selection.is_single_full_instance())
{
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
ref_size = volume->bounding_box.size();
}
change_scale_value(100.0 * Vec3d(size(0) / ref_size(0), size(1) / ref_size(1), size(2) / ref_size(2)));
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
}

View File

@ -109,6 +109,7 @@ public:
virtual ~Preview();
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
GLCanvas3D* get_canvas3d() { return m_canvas; }
void set_view_toolbar(GLToolbar* toolbar);

View File

@ -2,6 +2,8 @@
#include <cstdio>
#include <vector>
#include <cmath>
#include <stdexcept>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
@ -24,6 +26,7 @@ namespace GUI {
ImGuiWrapper::ImGuiWrapper()
: m_font_texture(0)
, m_style_scaling(1.0)
, m_mouse_buttons(0)
, m_disabled(false)
{
@ -39,18 +42,9 @@ bool ImGuiWrapper::init()
{
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf").c_str(), 18.0f);
if (font == nullptr) {
font = io.Fonts->AddFontDefault();
if (font == nullptr)
return false;
}
else {
m_fonts.insert(FontsMap::value_type("Noto Sans Regular 18", font));
}
init_default_font(m_style_scaling);
io.IniFilename = nullptr;
ImGui::GetIO().IniFilename = nullptr;
return true;
}
@ -62,6 +56,15 @@ void ImGuiWrapper::set_display_size(float w, float h)
io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
}
void ImGuiWrapper::set_style_scaling(float scaling)
{
if (!std::isnan(scaling) && !std::isinf(scaling) && scaling != m_style_scaling) {
ImGui::GetStyle().ScaleAllSizes(scaling / m_style_scaling);
init_default_font(scaling);
m_style_scaling = scaling;
}
}
bool ImGuiWrapper::update_mouse_data(wxMouseEvent& evt)
{
ImGuiIO& io = ImGui::GetIO();
@ -93,6 +96,7 @@ void ImGuiWrapper::render()
void ImGuiWrapper::set_next_window_pos(float x, float y, int flag)
{
ImGui::SetNextWindowPos(ImVec2(x, y), (ImGuiCond)flag);
ImGui::SetNextWindowSize(ImVec2(0.0, 0.0));
}
void ImGuiWrapper::set_next_window_bg_alpha(float alpha)
@ -198,6 +202,23 @@ bool ImGuiWrapper::want_any_input() const
return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput;
}
void ImGuiWrapper::init_default_font(float scaling)
{
static const float font_size = 18.0f;
destroy_fonts_texture();
ImGuiIO& io = ImGui::GetIO();
io.Fonts->Clear();
ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf").c_str(), font_size * scaling);
if (font == nullptr) {
font = io.Fonts->AddFontDefault();
if (font == nullptr) {
throw std::runtime_error("ImGui: Could not load deafult font");
}
}
}
void ImGuiWrapper::create_device_objects()
{
create_fonts_texture();

View File

@ -21,6 +21,7 @@ class ImGuiWrapper
FontsMap m_fonts;
unsigned m_font_texture;
float m_style_scaling;
unsigned m_mouse_buttons;
bool m_disabled;
@ -32,6 +33,7 @@ public:
void read_glsl_version();
void set_display_size(float w, float h);
void set_style_scaling(float scaling);
bool update_mouse_data(wxMouseEvent &evt);
void new_frame();
@ -58,6 +60,7 @@ public:
bool want_text_input() const;
bool want_any_input() const;
private:
void init_default_font(float scaling);
void create_device_objects();
void create_fonts_texture();
void render_draw_data(ImDrawData *draw_data);

View File

@ -95,14 +95,10 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
_3DScene::remove_all_canvases();
// Slic3r::GUI::deregister_on_request_update_callback();
// destroy and set to null tabs and a platter
// set to null tabs and a platter
// to avoid any manipulations with them from App->wxEVT_IDLE after of the mainframe closing
wxGetApp().clear_tabs_list();
if (wxGetApp().plater_) {
// before creating a new plater let's delete old one
wxGetApp().plater_->Destroy();
wxGetApp().plater_ = nullptr;
}
wxGetApp().tabs_list.clear();
wxGetApp().plater_ = nullptr;
// propagate event
event.Skip();
@ -113,6 +109,7 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
update_ui_from_settings(); // FIXME (?)
}
void MainFrame::init_tabpanel()
{
m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL);
@ -239,11 +236,11 @@ void MainFrame::init_menubar()
// File menu
wxMenu* fileMenu = new wxMenu;
{
wxMenuItem* item_open = append_menu_item(fileMenu, wxID_ANY, _(L("&Open")) + dots + "\tCtrl+O", _(L("Open a project file")),
wxMenuItem* item_open = append_menu_item(fileMenu, wxID_ANY, _(L("&Open Project")) + dots + "\tCtrl+O", _(L("Open a project file")),
[this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "brick_add.png");
wxMenuItem* item_save = append_menu_item(fileMenu, wxID_ANY, _(L("&Save")) + "\tCtrl+S", _(L("Save current project file")),
wxMenuItem* item_save = append_menu_item(fileMenu, wxID_ANY, _(L("&Save Project")) + "\tCtrl+S", _(L("Save current project file")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename())); }, "disk.png");
wxMenuItem* item_save_as = append_menu_item(fileMenu, wxID_ANY, _(L("Save &as")) + dots + "\tCtrl+Alt+S", _(L("Save current project file as")),
wxMenuItem* item_save_as = append_menu_item(fileMenu, wxID_ANY, _(L("Save Project &as")) + dots + "\tCtrl+Alt+S", _(L("Save current project file as")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, "disk.png");
fileMenu->AppendSeparator();

View File

@ -1272,6 +1272,11 @@ void Plater::priv::update_ui_from_settings()
// $self->{buttons_sizer}->Show($self->{btn_reslice}, ! wxTheApp->{app_config}->get("background_processing"));
// $self->{buttons_sizer}->Layout;
// }
#if ENABLE_RETINA_GL
view3D->get_canvas3d()->update_ui_from_settings();
preview->get_canvas3d()->update_ui_from_settings();
#endif
}
ProgressStatusBar* Plater::priv::statusbar()
@ -1463,29 +1468,40 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
return obj_idxs;
}
// #define AUTOPLACEMENT_ON_LOAD
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects)
{
const BoundingBoxf bed_shape = bed_shape_bb();
const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast<double>(), 1.0) - 2.0 * Vec3d::Ones();
#ifndef AUTOPLACEMENT_ON_LOAD
bool need_arrange = false;
#endif /* AUTOPLACEMENT_ON_LOAD */
bool scaled_down = false;
std::vector<size_t> obj_idxs;
unsigned int obj_count = model.objects.size();
#ifdef AUTOPLACEMENT_ON_LOAD
ModelInstancePtrs new_instances;
#endif /* AUTOPLACEMENT_ON_LOAD */
for (ModelObject *model_object : model_objects) {
auto *object = model.add_object(*model_object);
std::string object_name = object->name.empty() ? fs::path(object->input_file).filename().string() : object->name;
obj_idxs.push_back(obj_count++);
if (model_object->instances.empty()) {
// if object has no defined position(s) we need to rearrange everything after loading
need_arrange = true;
// add a default instance and center object around origin
object->center_around_origin(); // also aligns object to Z = 0
ModelInstance* instance = object->add_instance();
#ifdef AUTOPLACEMENT_ON_LOAD
object->center_around_origin();
new_instances.emplace_back(object->add_instance());
#else /* AUTOPLACEMENT_ON_LOAD */
// if object has no defined position(s) we need to rearrange everything after loading object->center_around_origin();
need_arrange = true;
// add a default instance and center object around origin
object->center_around_origin(); // also aligns object to Z = 0
ModelInstance* instance = object->add_instance();
instance->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -object->origin_translation(2)));
#endif /* AUTOPLACEMENT_ON_LOAD */
}
const Vec3d size = object->bounding_box().size();
@ -1513,6 +1529,18 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
// print.add_model_object(object);
}
#ifdef AUTOPLACEMENT_ON_LOAD
// FIXME distance should be a config value /////////////////////////////////
auto min_obj_distance = static_cast<coord_t>(6/SCALING_FACTOR);
const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
assert(bed_shape_opt);
auto& bedpoints = bed_shape_opt->values;
Polyline bed; bed.points.reserve(bedpoints.size());
for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1)));
arr::find_new_position(model, new_instances, min_obj_distance, bed);
#endif /* AUTOPLACEMENT_ON_LOAD */
if (scaled_down) {
GUI::show_info(q,
_(L("Your object appears to be too large, so it was automatically scaled down to fit your print bed.")),
@ -2094,7 +2122,7 @@ void Plater::priv::fix_through_netfabb(const int obj_idx)
o->clear_instances();
for (auto instance: model_object->instances)
o->add_instance(*instance);
// o->invalidate_bounding_box();
o->invalidate_bounding_box();
if (o->volumes.size() == model_object->volumes.size()) {
for (int i = 0; i < o->volumes.size(); i++) {

View File

@ -87,6 +87,7 @@ void PreferencesDialog::build()
option = Option (def,"show_incompatible_presets");
m_optgroup->append_single_option_line(option);
// TODO: remove?
def.label = L("Use legacy OpenGL 1.1 rendering");
def.type = coBool;
def.tooltip = L("If you have rendering issues caused by a buggy OpenGL 2.0 driver, "
@ -96,6 +97,16 @@ void PreferencesDialog::build()
option = Option (def,"use_legacy_opengl");
m_optgroup->append_single_option_line(option);
#if __APPLE__
def.label = L("Use Retina resolution for the 3D scene");
def.type = coBool;
def.tooltip = L("If enabled, the 3D scene will be rendered in Retina resolution. "
"If you are experiencing 3D performance problems, disabling this option may help.");
def.default_value = new ConfigOptionBool{ app_config->get("use_retina_opengl") == "1" };
option = Option (def, "use_retina_opengl");
m_optgroup->append_single_option_line(option);
#endif
auto sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(m_optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
@ -110,8 +121,8 @@ void PreferencesDialog::build()
void PreferencesDialog::accept()
{
if (m_values.find("no_defaults") != m_values.end()||
m_values.find("use_legacy_opengl")!= m_values.end()) {
if (m_values.find("no_defaults") != m_values.end() ||
m_values.find("use_legacy_opengl") != m_values.end()) {
warning_catcher(this, _(L("You need to restart Slic3r to make the changes effective.")));
}

View File

@ -551,6 +551,9 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri
boost::filesystem::path dir = boost::filesystem::canonical(boost::filesystem::path(dir_path) / subdir).make_preferred();
m_dir_path = dir.string();
std::string errors_cummulative;
// Store the loaded presets into a new vector, otherwise the binary search for already existing presets would be broken.
// (see the "Preset already present, not loading" message).
std::deque<Preset> presets_loaded;
for (auto &dir_entry : boost::filesystem::directory_iterator(dir))
if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini") &&
// Ignore system and hidden files, which may be created by the DropBox synchronisation process.
@ -588,12 +591,13 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri
} catch (const std::runtime_error &err) {
throw std::runtime_error(std::string("Failed loading the preset file: ") + preset.file + "\n\tReason: " + err.what());
}
m_presets.emplace_back(preset);
presets_loaded.emplace_back(preset);
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
errors_cummulative += "\n";
}
}
m_presets.insert(m_presets.end(), std::make_move_iterator(presets_loaded.begin()), std::make_move_iterator(presets_loaded.end()));
std::sort(m_presets.begin() + m_num_default_presets, m_presets.end());
this->select_preset(first_visible_idx());
if (! errors_cummulative.empty())

View File

@ -1686,11 +1686,11 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) {
btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent &e) {
BonjourDialog dialog(parent);
if (dialog.show_and_lookup()) {
optgroup->set_value("print_host", std::move(dialog.get_selected()), true);
// FIXME: emit killfocus on the edit widget
optgroup->get_field("print_host")->field_changed();
}
});
@ -1704,7 +1704,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) {
std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config));
if (! host) {
const auto text = wxString::Format("%s",
@ -1745,6 +1745,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (openFileDialog.ShowModal() != wxID_CANCEL) {
optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true);
optgroup->get_field("printhost_cafile")->field_changed();
}
});

View File

@ -0,0 +1,29 @@
#ifndef slic3r_RetinaHelper_hpp_
#define slic3r_RetinaHelper_hpp_
class wxWindow;
namespace Slic3r {
namespace GUI {
class RetinaHelper
{
public:
RetinaHelper(wxWindow* window);
~RetinaHelper();
void set_use_retina(bool value);
bool get_use_retina();
float get_scale_factor();
private:
wxWindow* m_window;
void* m_self;
};
} // namespace GUI
} // namespace Slic3r
#endif // RetinaHelper_h

View File

@ -0,0 +1,15 @@
#import <Cocoa/Cocoa.h>
class wxEvtHandler;
@interface RetinaHelperImpl : NSObject
{
NSView *view;
wxEvtHandler* handler;
}
-(id)initWithView:(NSView *)view handler:(wxEvtHandler *)handler;
-(void)setViewWantsBestResolutionOpenGLSurface:(BOOL)value;
-(BOOL)getViewWantsBestResolutionOpenGLSurface;
-(float)getBackingScaleFactor;
@end

View File

@ -0,0 +1,111 @@
// The RetinaHelper was originally written by Andreas Stahl, 2013
#import "RetinaHelper.hpp"
#import "RetinaHelperImpl.hmm"
#import <OpenGL/OpenGL.h>
#import "wx/window.h"
@implementation RetinaHelperImpl
namespace Slic3r {
namespace GUI {
RetinaHelper::RetinaHelper(wxWindow* window) :
m_window(window)
{
m_self = nullptr;
m_self = [[RetinaHelperImpl alloc] initWithView:window->GetHandle() handler:window->GetEventHandler()];
}
RetinaHelper::~RetinaHelper()
{
[m_self release];
}
void RetinaHelper::set_use_retina(bool aValue)
{
[(id)m_self setViewWantsBestResolutionOpenGLSurface:aValue];
}
bool RetinaHelper::get_use_retina()
{
return [(id)m_self getViewWantsBestResolutionOpenGLSurface];
}
float RetinaHelper::get_scale_factor()
{
return [(id)m_self getViewWantsBestResolutionOpenGLSurface] ? [(id)m_self getBackingScaleFactor] : 1.0f;
}
} // namespace GUI
} // namespace Slic3r
-(id)initWithView:(NSView *)aView handler:(wxEvtHandler *)aHandler
{
self = [super init];
if (self) {
handler = aHandler;
view = aView;
// register for backing change notifications
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
if (nc) {
[nc addObserver:self selector:@selector(windowDidChangeBackingProperties:)
name:NSWindowDidChangeBackingPropertiesNotification object:nil];
}
}
return self;
}
-(void) dealloc
{
// unregister from all notifications
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
if (nc) {
[nc removeObserver:self];
}
[super dealloc];
}
-(void)setViewWantsBestResolutionOpenGLSurface:(BOOL)value
{
[view setWantsBestResolutionOpenGLSurface:value];
}
-(BOOL)getViewWantsBestResolutionOpenGLSurface
{
return [view wantsBestResolutionOpenGLSurface];
}
-(float)getBackingScaleFactor
{
return [[view window] backingScaleFactor];
}
- (void)windowDidChangeBackingProperties:(NSNotification *)notification
{
NSWindow *theWindow = (NSWindow *)[notification object];
if (theWindow == [view window]) {
CGFloat newBackingScaleFactor = [theWindow backingScaleFactor];
CGFloat oldBackingScaleFactor = [[[notification userInfo]
objectForKey:@"NSBackingPropertyOldScaleFactorKey"]
doubleValue];
if (newBackingScaleFactor != oldBackingScaleFactor) {
// generate a wx resize event and pass it to the handler's queue
wxSizeEvent *event = new wxSizeEvent();
// use the following line if this resize event should have the physical pixel resolution
// but that is not recommended, because ordinary resize events won't do so either
// which would necessitate a case-by-case switch in the resize handler method.
// NSRect nsrect = [view convertRectToBacking:[view bounds]];
NSRect nsrect = [view bounds];
wxRect rect = wxRect(nsrect.origin.x, nsrect.origin.y, nsrect.size.width, nsrect.size.height);
event->SetRect(rect);
event->SetSize(rect.GetSize());
handler->QueueEvent(event);
}
}
}
@end