Merge branch 'master' of https://github.com/prusa3d/Slic3r into objects_centering
This commit is contained in:
commit
c2d34cea89
46 changed files with 1724 additions and 451 deletions
|
@ -169,6 +169,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)
|
||||
|
|
Binary file not shown.
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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&)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; \
|
||||
|
|
|
@ -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_));
|
||||
|
|
|
@ -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](
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -573,6 +573,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 +606,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 +787,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 +838,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,20 +912,6 @@ 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
|
||||
{
|
||||
BoundingBoxf3 bb;
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
{
|
||||
TriangleMesh vol_mesh(v->mesh);
|
||||
vol_mesh.transform(v->get_matrix());
|
||||
bb.merge(vol_mesh.bounding_box());
|
||||
}
|
||||
return bb;
|
||||
}
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
void ModelObject::center_around_origin()
|
||||
{
|
||||
// calculate the displacements needed to
|
||||
|
|
|
@ -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,10 @@ 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
|
||||
void center_around_origin();
|
||||
void ensure_on_bed();
|
||||
void translate_instances(const Vec3d& vector);
|
||||
|
@ -261,7 +261,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 +281,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.
|
||||
|
|
|
@ -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) {
|
||||
|
@ -791,5 +814,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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -75,7 +75,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"This is mostly useful with Bowden extruders which suffer from oozing. "
|
||||
"This feature slows down both the print and the G-code generation.");
|
||||
def->cli = "avoid-crossing-perimeters!";
|
||||
def->mode = comAdvanced;
|
||||
def->mode = comExpert;
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("bed_temperature", coInts);
|
||||
|
@ -172,6 +172,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->cli = "bridge-speed=f";
|
||||
def->aliases = { "bridge_feed_rate" };
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(60);
|
||||
|
||||
def = this->add("brim_width", coFloat);
|
||||
|
@ -326,7 +327,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->sidetext = L("mm");
|
||||
def->cli = "elefant-foot-compensation=f";
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(0);
|
||||
|
||||
def = this->add("end_gcode", coString);
|
||||
|
@ -403,6 +404,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->cli = "external-perimeter-speed=s";
|
||||
def->ratio_over = "perimeter_speed";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloatOrPercent(50, true);
|
||||
|
||||
def = this->add("external_perimeters_first", coBool);
|
||||
|
@ -421,7 +423,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"Slic3r keeps adding perimeters, until more than 70% of the loop immediately above "
|
||||
"is supported.");
|
||||
def->cli = "extra-perimeters!";
|
||||
def->mode = comAdvanced;
|
||||
def->mode = comExpert;
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
def = this->add("extruder", coInt);
|
||||
|
@ -706,12 +708,14 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_values.push_back("EDGE");
|
||||
def->enum_values.push_back("NGEN");
|
||||
def->enum_values.push_back("PVA");
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionStrings { "PLA" };
|
||||
|
||||
def = this->add("filament_soluble", coBools);
|
||||
def->label = L("Soluble material");
|
||||
def->tooltip = L("Soluble material is most likely used for a soluble support.");
|
||||
def->cli = "filament-soluble!";
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionBools { false };
|
||||
|
||||
def = this->add("filament_cost", coFloats);
|
||||
|
@ -884,6 +888,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->sidetext = L("mm/s");
|
||||
def->cli = "gap-fill-speed=f";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(20);
|
||||
|
||||
def = this->add("gcode_comments", coBool);
|
||||
|
@ -1014,6 +1019,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->cli = "infill-speed=f";
|
||||
def->aliases = { "print_feed_rate", "infill_feed_rate" };
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(80);
|
||||
|
||||
def = this->add("inherits", coString);
|
||||
|
@ -1419,6 +1425,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->cli = "perimeter-speed=f";
|
||||
def->aliases = { "perimeter_feed_rate" };
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(60);
|
||||
|
||||
def = this->add("perimeters", coInt);
|
||||
|
@ -1627,7 +1634,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_labels.push_back(L("Nearest"));
|
||||
def->enum_labels.push_back(L("Aligned"));
|
||||
def->enum_labels.push_back(L("Rear"));
|
||||
def->mode = comAdvanced;
|
||||
def->mode = comSimple;
|
||||
def->default_value = new ConfigOptionEnum<SeamPosition>(spAligned);
|
||||
|
||||
#if 0
|
||||
|
@ -1673,6 +1680,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->max = 300000;
|
||||
def->enum_values.push_back("115200");
|
||||
def->enum_values.push_back("250000");
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionInt(250000);
|
||||
|
||||
def = this->add("skirt_distance", coFloat);
|
||||
|
@ -1726,6 +1734,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->cli = "small-perimeter-speed=s";
|
||||
def->ratio_over = "perimeter_speed";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloatOrPercent(15, false);
|
||||
|
||||
def = this->add("solid_infill_below_area", coFloat);
|
||||
|
@ -1782,6 +1791,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->ratio_over = "infill_speed";
|
||||
def->aliases = { "solid_infill_feed_rate" };
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloatOrPercent(20, false);
|
||||
|
||||
def = this->add("solid_layers", coInt);
|
||||
|
@ -1873,7 +1883,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->tooltip = L("If checked, supports will be generated automatically based on the overhang threshold value."\
|
||||
" If unchecked, supports will be generated inside the \"Support Enforcer\" volumes only.");
|
||||
def->cli = "support-material-auto!";
|
||||
def->mode = comAdvanced;
|
||||
def->mode = comSimple;
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
def = this->add("support_material_xy_spacing", coFloatOrPercent);
|
||||
|
@ -1905,7 +1915,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->category = L("Support material");
|
||||
def->tooltip = L("Only create support if it lies on a build plate. Don't create support on a print.");
|
||||
def->cli = "support-material-buildplate-only!";
|
||||
def->mode = comAdvanced;
|
||||
def->mode = comSimple;
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("support_material_contact_distance", coFloat);
|
||||
|
@ -2007,6 +2017,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->cli = "support-material-interface-speed=s";
|
||||
def->ratio_over = "support_material_speed";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloatOrPercent(100, true);
|
||||
|
||||
def = this->add("support_material_pattern", coEnum);
|
||||
|
@ -2041,6 +2052,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->sidetext = L("mm/s");
|
||||
def->cli = "support-material-speed=f";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(60);
|
||||
|
||||
def = this->add("support_material_synchronize_layers", coBool);
|
||||
|
@ -2143,6 +2155,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->cli = "top-solid-infill-speed=s";
|
||||
def->ratio_over = "solid_infill_speed";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloatOrPercent(15, false);
|
||||
|
||||
def = this->add("top_solid_layers", coInt);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "libslic3r/GCode/PreviewData.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Technologies.hpp"
|
||||
#include "slic3r/GUI/3DScene.hpp"
|
||||
#include "slic3r/GUI/BackgroundSlicingProcess.hpp"
|
||||
#include "slic3r/GUI/GLShader.hpp"
|
||||
|
@ -20,6 +21,10 @@
|
|||
#include "GUI_ObjectManipulation.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#if ENABLE_RETINA_GL
|
||||
#include "slic3r/Utils/RetinaHelper.hpp"
|
||||
#endif
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include <wx/glcanvas.h>
|
||||
|
@ -45,6 +50,7 @@
|
|||
#include <iostream>
|
||||
#include <float.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
static const float TRACKBALLSIZE = 0.8f;
|
||||
static const float GIMBALL_LOCK_THETA_MAX = 180.0f;
|
||||
|
@ -59,8 +65,6 @@ static const float VIEW_BOTTOM[2] = { 0.0f, 180.0f };
|
|||
static const float VIEW_FRONT[2] = { 0.0f, 90.0f };
|
||||
static const float VIEW_REAR[2] = { 180.0f, 90.0f };
|
||||
|
||||
static const float VARIABLE_LAYER_THICKNESS_BAR_WIDTH = 70.0f;
|
||||
static const float VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = 22.0f;
|
||||
static const float GIZMO_RESET_BUTTON_HEIGHT = 22.0f;
|
||||
static const float GIZMO_RESET_BUTTON_WIDTH = 70.f;
|
||||
|
||||
|
@ -191,9 +195,10 @@ Size::Size()
|
|||
{
|
||||
}
|
||||
|
||||
Size::Size(int width, int height)
|
||||
Size::Size(int width, int height, float scale_factor)
|
||||
: m_width(width)
|
||||
, m_height(height)
|
||||
, m_scale_factor(scale_factor)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -217,6 +222,16 @@ void Size::set_height(int height)
|
|||
m_height = height;
|
||||
}
|
||||
|
||||
int Size::get_scale_factor() const
|
||||
{
|
||||
return m_scale_factor;
|
||||
}
|
||||
|
||||
void Size::set_scale_factor(int scale_factor)
|
||||
{
|
||||
m_scale_factor = scale_factor;
|
||||
}
|
||||
|
||||
Rect::Rect()
|
||||
: m_left(0.0f)
|
||||
, m_top(0.0f)
|
||||
|
@ -330,6 +345,7 @@ void GLCanvas3D::Camera::set_scene_box(const BoundingBoxf3& box, GLCanvas3D& can
|
|||
|
||||
GLCanvas3D::Bed::Bed()
|
||||
: m_type(Custom)
|
||||
, m_scale_factor(1.0f)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -392,8 +408,10 @@ Point GLCanvas3D::Bed::point_projection(const Point& point) const
|
|||
}
|
||||
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
void GLCanvas3D::Bed::render(float theta, bool useVBOs) const
|
||||
void GLCanvas3D::Bed::render(float theta, bool useVBOs, float scale_factor) const
|
||||
{
|
||||
m_scale_factor = scale_factor;
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
case MK2:
|
||||
|
@ -420,8 +438,10 @@ void GLCanvas3D::Bed::render(float theta, bool useVBOs) const
|
|||
}
|
||||
}
|
||||
#else
|
||||
void GLCanvas3D::Bed::render(float theta) const
|
||||
void GLCanvas3D::Bed::render(float theta, float scale_factor) const
|
||||
{
|
||||
m_scale_factor = scale_factor;
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
case MK2:
|
||||
|
@ -675,7 +695,7 @@ void GLCanvas3D::Bed::_render_custom() const
|
|||
|
||||
// we need depth test for grid, otherwise it would disappear when looking the object from below
|
||||
::glEnable(GL_DEPTH_TEST);
|
||||
::glLineWidth(3.0f);
|
||||
::glLineWidth(3.0f * m_scale_factor);
|
||||
::glColor4f(0.2f, 0.2f, 0.2f, 0.4f);
|
||||
::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_vertices());
|
||||
::glDrawArrays(GL_LINES, 0, (GLsizei)gridlines_vcount);
|
||||
|
@ -873,6 +893,9 @@ GLCanvas3D::LayersEditing::~LayersEditing()
|
|||
delete m_slicing_parameters;
|
||||
}
|
||||
|
||||
const float GLCanvas3D::LayersEditing::THICKNESS_BAR_WIDTH = 70.0f;
|
||||
const float GLCanvas3D::LayersEditing::THICKNESS_RESET_BUTTON_HEIGHT = 22.0f;
|
||||
|
||||
bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename)
|
||||
{
|
||||
if (!m_shader.init(vertex_shader_filename, fragment_shader_filename))
|
||||
|
@ -993,7 +1016,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas)
|
|||
float w = (float)cnv_size.get_width();
|
||||
float h = (float)cnv_size.get_height();
|
||||
|
||||
return Rect(w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, 0.0f, w, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT);
|
||||
return Rect(w - thickness_bar_width(canvas), 0.0f, w, h - reset_button_height(canvas));
|
||||
}
|
||||
|
||||
Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas)
|
||||
|
@ -1002,7 +1025,7 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas)
|
|||
float w = (float)cnv_size.get_width();
|
||||
float h = (float)cnv_size.get_height();
|
||||
|
||||
return Rect(w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT, w, h);
|
||||
return Rect(w - thickness_bar_width(canvas), h - reset_button_height(canvas), w, h);
|
||||
}
|
||||
|
||||
Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
|
||||
|
@ -1014,7 +1037,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
|
|||
float zoom = canvas.get_camera_zoom();
|
||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||
|
||||
return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom);
|
||||
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom);
|
||||
}
|
||||
|
||||
Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas)
|
||||
|
@ -1026,7 +1049,7 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas
|
|||
float zoom = canvas.get_camera_zoom();
|
||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||
|
||||
return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom);
|
||||
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1037,6 +1060,8 @@ bool GLCanvas3D::LayersEditing::_is_initialized() const
|
|||
|
||||
void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const
|
||||
{
|
||||
// TODO: do this with ImGui
|
||||
|
||||
if (m_tooltip_texture.get_id() == 0)
|
||||
{
|
||||
std::string filename = resources_dir() + "/icons/variable_layer_height_tooltip.png";
|
||||
|
@ -1044,6 +1069,15 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas
|
|||
return;
|
||||
}
|
||||
|
||||
#if ENABLE_RETINA_GL
|
||||
const float scale = canvas.get_canvas_size().get_scale_factor();
|
||||
const float width = (float)m_tooltip_texture.get_width() * scale;
|
||||
const float height = (float)m_tooltip_texture.get_height() * scale;
|
||||
#else
|
||||
const float width = (float)m_tooltip_texture.get_width();
|
||||
const float height = (float)m_tooltip_texture.get_height();
|
||||
#endif
|
||||
|
||||
float zoom = canvas.get_camera_zoom();
|
||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||
float gap = 10.0f * inv_zoom;
|
||||
|
@ -1051,9 +1085,9 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas
|
|||
float bar_left = bar_rect.get_left();
|
||||
float reset_bottom = reset_rect.get_bottom();
|
||||
|
||||
float l = bar_left - (float)m_tooltip_texture.get_width() * inv_zoom - gap;
|
||||
float l = bar_left - width * inv_zoom - gap;
|
||||
float r = bar_left - gap;
|
||||
float t = reset_bottom + (float)m_tooltip_texture.get_height() * inv_zoom + gap;
|
||||
float t = reset_bottom + height * inv_zoom + gap;
|
||||
float b = reset_bottom + gap;
|
||||
|
||||
GLTexture::render_texture(m_tooltip_texture.get_id(), l, r, b, t);
|
||||
|
@ -1082,11 +1116,6 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas
|
|||
// The shader requires the original model coordinates when rendering to the texture, so we pass it the unit matrix
|
||||
m_shader.set_uniform("volume_world_matrix", UNIT_MATRIX);
|
||||
|
||||
GLsizei w = (GLsizei)m_layers_texture.width;
|
||||
GLsizei h = (GLsizei)m_layers_texture.height;
|
||||
GLsizei half_w = w / 2;
|
||||
GLsizei half_h = h / 2;
|
||||
|
||||
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
::glBindTexture(GL_TEXTURE_2D, m_z_texture_id);
|
||||
|
||||
|
@ -1267,6 +1296,25 @@ void GLCanvas3D::LayersEditing::update_slicing_parameters()
|
|||
}
|
||||
}
|
||||
|
||||
float GLCanvas3D::LayersEditing::thickness_bar_width(const GLCanvas3D &canvas)
|
||||
{
|
||||
#if ENABLE_RETINA_GL
|
||||
return canvas.get_canvas_size().get_scale_factor() * THICKNESS_BAR_WIDTH;
|
||||
#else
|
||||
return THICKNESS_BAR_WIDTH;
|
||||
#endif
|
||||
}
|
||||
|
||||
float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas)
|
||||
{
|
||||
#if ENABLE_RETINA_GL
|
||||
return canvas.get_canvas_size().get_scale_factor() * THICKNESS_RESET_BUTTON_HEIGHT;
|
||||
#else
|
||||
return THICKNESS_RESET_BUTTON_HEIGHT;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX);
|
||||
const Vec3d GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MAX);
|
||||
#if ENABLE_MOVE_MIN_THRESHOLD
|
||||
|
@ -1329,6 +1377,7 @@ GLCanvas3D::Selection::Selection()
|
|||
, m_valid(false)
|
||||
, m_bounding_box_dirty(true)
|
||||
, m_curved_arrow(16)
|
||||
, m_scale_factor(1.0f)
|
||||
{
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
m_quadric = ::gluNewQuadric();
|
||||
|
@ -2124,11 +2173,13 @@ void GLCanvas3D::Selection::erase()
|
|||
}
|
||||
}
|
||||
|
||||
void GLCanvas3D::Selection::render() const
|
||||
void GLCanvas3D::Selection::render(float scale_factor) const
|
||||
{
|
||||
if (!m_valid || is_empty())
|
||||
return;
|
||||
|
||||
m_scale_factor = scale_factor;
|
||||
|
||||
// render cumulative bounding box of selected volumes
|
||||
_render_selected_volumes();
|
||||
_render_synchronized_volumes();
|
||||
|
@ -2573,7 +2624,7 @@ void GLCanvas3D::Selection::_render_bounding_box(const BoundingBoxf3& box, float
|
|||
::glEnable(GL_DEPTH_TEST);
|
||||
|
||||
::glColor3fv(color);
|
||||
::glLineWidth(2.0f);
|
||||
::glLineWidth(2.0f * m_scale_factor);
|
||||
|
||||
::glBegin(GL_LINES);
|
||||
|
||||
|
@ -2866,14 +2917,11 @@ void GLCanvas3D::Selection::_ensure_on_bed()
|
|||
}
|
||||
}
|
||||
|
||||
const float GLCanvas3D::Gizmos::OverlayIconsScale = 1.0f;
|
||||
const float GLCanvas3D::Gizmos::OverlayBorder = 5.0f;
|
||||
const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayIconsScale;
|
||||
|
||||
GLCanvas3D::Gizmos::Gizmos()
|
||||
: m_enabled(false)
|
||||
, m_current(Undefined)
|
||||
{
|
||||
set_overlay_scale(1.0);
|
||||
}
|
||||
|
||||
GLCanvas3D::Gizmos::~Gizmos()
|
||||
|
@ -2977,6 +3025,13 @@ void GLCanvas3D::Gizmos::set_enabled(bool enable)
|
|||
m_enabled = enable;
|
||||
}
|
||||
|
||||
void GLCanvas3D::Gizmos::set_overlay_scale(float scale)
|
||||
{
|
||||
m_overlay_icons_scale = scale;
|
||||
m_overlay_border = 5.0f * scale;
|
||||
m_overlay_gap_y = 5.0f * scale;
|
||||
}
|
||||
|
||||
std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const GLCanvas3D::Selection& selection)
|
||||
{
|
||||
std::string name = "";
|
||||
|
@ -2986,22 +3041,22 @@ std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, con
|
|||
|
||||
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
||||
float height = _get_total_overlay_height();
|
||||
float top_y = 0.5f * (cnv_h - height) + OverlayBorder;
|
||||
float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
|
||||
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||
{
|
||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||
continue;
|
||||
|
||||
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale;
|
||||
float icon_size = (float)it->second->get_textures_size() * m_overlay_icons_scale;
|
||||
|
||||
bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size);
|
||||
bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size);
|
||||
if (inside)
|
||||
name = it->second->get_name();
|
||||
|
||||
if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On))
|
||||
it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off);
|
||||
|
||||
top_y += (icon_size + OverlayGapY);
|
||||
top_y += (icon_size + m_overlay_gap_y);
|
||||
}
|
||||
|
||||
return name;
|
||||
|
@ -3014,15 +3069,15 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec
|
|||
|
||||
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
||||
float height = _get_total_overlay_height();
|
||||
float top_y = 0.5f * (cnv_h - height) + OverlayBorder;
|
||||
float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
|
||||
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||
{
|
||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||
continue;
|
||||
|
||||
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale;
|
||||
float icon_size = (float)it->second->get_textures_size() * m_overlay_icons_scale;
|
||||
|
||||
bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size);
|
||||
bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size);
|
||||
if (it->second->is_activable(selection) && inside)
|
||||
{
|
||||
if ((it->second->get_state() == GLGizmoBase::On))
|
||||
|
@ -3039,7 +3094,7 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec
|
|||
else
|
||||
it->second->set_state(GLGizmoBase::Off);
|
||||
|
||||
top_y += (icon_size + OverlayGapY);
|
||||
top_y += (icon_size + m_overlay_gap_y);
|
||||
}
|
||||
|
||||
GizmosMap::iterator it = m_gizmos.find(m_current);
|
||||
|
@ -3111,18 +3166,18 @@ bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const
|
|||
|
||||
float cnv_h = (float)canvas.get_canvas_size().get_height();
|
||||
float height = _get_total_overlay_height();
|
||||
float top_y = 0.5f * (cnv_h - height) + OverlayBorder;
|
||||
float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
|
||||
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||
{
|
||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||
continue;
|
||||
|
||||
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale;
|
||||
float icon_size = (float)it->second->get_textures_size() * m_overlay_icons_scale;
|
||||
|
||||
if ((OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size))
|
||||
if ((m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size))
|
||||
return true;
|
||||
|
||||
top_y += (icon_size + OverlayGapY);
|
||||
top_y += (icon_size + m_overlay_gap_y);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -3395,7 +3450,7 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva
|
|||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||
|
||||
float height = _get_total_overlay_height();
|
||||
float scaled_border = OverlayBorder * inv_zoom;
|
||||
float scaled_border = m_overlay_border * inv_zoom;
|
||||
|
||||
float top_x = (-0.5f * cnv_w) * inv_zoom;
|
||||
float top_y = (0.5f * height) * inv_zoom;
|
||||
|
@ -3440,7 +3495,7 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva
|
|||
bg_uv_left = bg_uv_i_left;
|
||||
bg_i_left = bg_left;
|
||||
|
||||
if ((OverlayBorder > 0) && (bg_uv_top != bg_uv_i_top))
|
||||
if ((m_overlay_border > 0) && (bg_uv_top != bg_uv_i_top))
|
||||
{
|
||||
if (bg_uv_left != bg_uv_i_left)
|
||||
GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_top, bg_top, { { bg_uv_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_top }, { bg_uv_left, bg_uv_top } });
|
||||
|
@ -3451,15 +3506,15 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva
|
|||
GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_top, bg_top, { { bg_uv_i_right, bg_uv_i_top }, { bg_uv_right, bg_uv_i_top }, { bg_uv_right, bg_uv_top }, { bg_uv_i_right, bg_uv_top } });
|
||||
}
|
||||
|
||||
if ((OverlayBorder > 0) && (bg_uv_left != bg_uv_i_left))
|
||||
if ((m_overlay_border > 0) && (bg_uv_left != bg_uv_i_left))
|
||||
GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_bottom, bg_i_top, { { bg_uv_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_left, bg_uv_i_top } });
|
||||
|
||||
GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_bottom, bg_i_top, { { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top } });
|
||||
|
||||
if ((OverlayBorder > 0) && (bg_uv_right != bg_uv_i_right))
|
||||
if ((m_overlay_border > 0) && (bg_uv_right != bg_uv_i_right))
|
||||
GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_bottom, bg_i_top, { { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top } });
|
||||
|
||||
if ((OverlayBorder > 0) && (bg_uv_bottom != bg_uv_i_bottom))
|
||||
if ((m_overlay_border > 0) && (bg_uv_bottom != bg_uv_i_bottom))
|
||||
{
|
||||
if (bg_uv_left != bg_uv_i_left)
|
||||
GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_bottom, bg_i_bottom, { { bg_uv_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_left, bg_uv_i_bottom } });
|
||||
|
@ -3471,19 +3526,19 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva
|
|||
}
|
||||
}
|
||||
|
||||
top_x += OverlayBorder * inv_zoom;
|
||||
top_y -= OverlayBorder * inv_zoom;
|
||||
float scaled_gap_y = OverlayGapY * inv_zoom;
|
||||
top_x += m_overlay_border * inv_zoom;
|
||||
top_y -= m_overlay_border * inv_zoom;
|
||||
float scaled_gap_y = m_overlay_gap_y * inv_zoom;
|
||||
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||
{
|
||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||
continue;
|
||||
|
||||
float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale * inv_zoom;
|
||||
float icon_size = (float)it->second->get_textures_size() * m_overlay_icons_scale * inv_zoom;
|
||||
GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + icon_size, top_y - icon_size, top_y);
|
||||
#if ENABLE_IMGUI
|
||||
if (it->second->get_state() == GLGizmoBase::On)
|
||||
it->second->render_input_window(2.0f * OverlayBorder + icon_size * zoom, 0.5f * cnv_h - top_y * zoom, selection);
|
||||
it->second->render_input_window(2.0f * m_overlay_border + icon_size * zoom, 0.5f * cnv_h - top_y * zoom, selection);
|
||||
#endif // ENABLE_IMGUI
|
||||
top_y -= (icon_size + scaled_gap_y);
|
||||
}
|
||||
|
@ -3498,17 +3553,17 @@ void GLCanvas3D::Gizmos::_render_current_gizmo(const GLCanvas3D::Selection& sele
|
|||
|
||||
float GLCanvas3D::Gizmos::_get_total_overlay_height() const
|
||||
{
|
||||
float height = 2.0f * OverlayBorder;
|
||||
float height = 2.0f * m_overlay_border;
|
||||
|
||||
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||
{
|
||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||
continue;
|
||||
|
||||
height += (float)it->second->get_textures_size() * OverlayIconsScale + OverlayGapY;
|
||||
height += (float)it->second->get_textures_size() * m_overlay_icons_scale + m_overlay_gap_y;
|
||||
}
|
||||
|
||||
return height - OverlayGapY;
|
||||
return height - m_overlay_gap_y;
|
||||
}
|
||||
|
||||
float GLCanvas3D::Gizmos::_get_total_overlay_width() const
|
||||
|
@ -3519,10 +3574,10 @@ float GLCanvas3D::Gizmos::_get_total_overlay_width() const
|
|||
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||
continue;
|
||||
|
||||
max_icon_width = std::max(max_icon_width, (float)it->second->get_textures_size() * OverlayIconsScale);
|
||||
max_icon_width = std::max(max_icon_width, (float)it->second->get_textures_size() * m_overlay_icons_scale);
|
||||
}
|
||||
|
||||
return max_icon_width + 2.0f * OverlayBorder;
|
||||
return max_icon_width + 2.0f * m_overlay_border;
|
||||
}
|
||||
|
||||
GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const
|
||||
|
@ -3541,7 +3596,7 @@ GLCanvas3D::WarningTexture::WarningTexture()
|
|||
{
|
||||
}
|
||||
|
||||
bool GLCanvas3D::WarningTexture::generate(const std::string& msg)
|
||||
bool GLCanvas3D::WarningTexture::generate(const std::string& msg, const GLCanvas3D& canvas)
|
||||
{
|
||||
reset();
|
||||
|
||||
|
@ -3550,7 +3605,8 @@ bool GLCanvas3D::WarningTexture::generate(const std::string& msg)
|
|||
|
||||
wxMemoryDC memDC;
|
||||
// select default font
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
const float scale = canvas.get_canvas_size().get_scale_factor();
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Scale(scale);
|
||||
font.MakeLarger();
|
||||
font.MakeBold();
|
||||
memDC.SetFont(font);
|
||||
|
@ -3698,9 +3754,18 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
|
|||
wxMemoryDC memDC;
|
||||
wxMemoryDC mask_memDC;
|
||||
|
||||
// calculate scaling
|
||||
const float scale = canvas.get_canvas_size().get_scale_factor();
|
||||
const int scaled_square = std::floor((float)Px_Square * scale);
|
||||
const int scaled_title_offset = Px_Title_Offset * scale;
|
||||
const int scaled_text_offset = Px_Text_Offset * scale;
|
||||
const int scaled_square_contour = Px_Square_Contour * scale;
|
||||
const int scaled_border = Px_Border * scale;
|
||||
|
||||
// select default font
|
||||
memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
|
||||
mask_memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
|
||||
const wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Scale(scale);
|
||||
memDC.SetFont(font);
|
||||
mask_memDC.SetFont(font);
|
||||
|
||||
// calculates texture size
|
||||
wxCoord w, h;
|
||||
|
@ -3717,10 +3782,10 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
|
|||
max_text_height = std::max(max_text_height, (int)h);
|
||||
}
|
||||
|
||||
m_original_width = std::max(2 * Px_Border + title_width, 2 * (Px_Border + Px_Square_Contour) + Px_Square + Px_Text_Offset + max_text_width);
|
||||
m_original_height = 2 * (Px_Border + Px_Square_Contour) + title_height + Px_Title_Offset + items_count * Px_Square;
|
||||
m_original_width = std::max(2 * scaled_border + title_width, 2 * (scaled_border + scaled_square_contour) + scaled_square + scaled_text_offset + max_text_width);
|
||||
m_original_height = 2 * (scaled_border + scaled_square_contour) + title_height + scaled_title_offset + items_count * scaled_square;
|
||||
if (items_count > 1)
|
||||
m_original_height += (items_count - 1) * Px_Square_Contour;
|
||||
m_original_height += (items_count - 1) * scaled_square_contour;
|
||||
|
||||
int pow_of_two_size = (int)next_highest_power_of_2(std::max<uint32_t>(m_original_width, m_original_height));
|
||||
|
||||
|
@ -3744,8 +3809,8 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
|
|||
memDC.SetTextForeground(use_error_colors ? *wxWHITE : *wxBLACK);
|
||||
mask_memDC.SetTextForeground(*wxWHITE);
|
||||
|
||||
int title_x = Px_Border;
|
||||
int title_y = Px_Border;
|
||||
int title_x = scaled_border;
|
||||
int title_y = scaled_border;
|
||||
memDC.DrawText(title, title_x, title_y);
|
||||
mask_memDC.DrawText(title, title_x, title_y);
|
||||
|
||||
|
@ -3753,12 +3818,12 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
|
|||
mask_memDC.SetBrush(wxBrush(*wxWHITE));
|
||||
|
||||
// draw icons contours as background
|
||||
int squares_contour_x = Px_Border;
|
||||
int squares_contour_y = Px_Border + title_height + Px_Title_Offset;
|
||||
int squares_contour_width = Px_Square + 2 * Px_Square_Contour;
|
||||
int squares_contour_height = items_count * Px_Square + 2 * Px_Square_Contour;
|
||||
int squares_contour_x = scaled_border;
|
||||
int squares_contour_y = scaled_border + title_height + scaled_title_offset;
|
||||
int squares_contour_width = scaled_square + 2 * scaled_square_contour;
|
||||
int squares_contour_height = items_count * scaled_square + 2 * scaled_square_contour;
|
||||
if (items_count > 1)
|
||||
squares_contour_height += (items_count - 1) * Px_Square_Contour;
|
||||
squares_contour_height += (items_count - 1) * scaled_square_contour;
|
||||
|
||||
wxColour color(Squares_Border_Color[0], Squares_Border_Color[1], Squares_Border_Color[2]);
|
||||
wxPen pen(color);
|
||||
|
@ -3769,15 +3834,15 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
|
|||
mask_memDC.DrawRectangle(wxRect(squares_contour_x, squares_contour_y, squares_contour_width, squares_contour_height));
|
||||
|
||||
// draw items (colored icon + text)
|
||||
int icon_x = squares_contour_x + Px_Square_Contour;
|
||||
int icon_x = squares_contour_x + scaled_square_contour;
|
||||
int icon_x_inner = icon_x + 1;
|
||||
int icon_y = squares_contour_y + Px_Square_Contour;
|
||||
int icon_y_step = Px_Square + Px_Square_Contour;
|
||||
int icon_y = squares_contour_y + scaled_square_contour;
|
||||
int icon_y_step = scaled_square + scaled_square_contour;
|
||||
|
||||
int text_x = icon_x + Px_Square + Px_Text_Offset;
|
||||
int text_y_offset = (Px_Square - max_text_height) / 2;
|
||||
int text_x = icon_x + scaled_square + scaled_text_offset;
|
||||
int text_y_offset = (scaled_square - max_text_height) / 2;
|
||||
|
||||
int px_inner_square = Px_Square - 2;
|
||||
int px_inner_square = scaled_square - 2;
|
||||
|
||||
for (const GCodePreviewData::LegendItem& item : items)
|
||||
{
|
||||
|
@ -3791,7 +3856,7 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
|
|||
brush.SetColour(color);
|
||||
memDC.SetPen(pen);
|
||||
memDC.SetBrush(brush);
|
||||
memDC.DrawRectangle(wxRect(icon_x, icon_y, Px_Square, Px_Square));
|
||||
memDC.DrawRectangle(wxRect(icon_x, icon_y, scaled_square, scaled_square));
|
||||
|
||||
// draw icon interior
|
||||
color.Set(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2], item_color_bytes[3]);
|
||||
|
@ -3900,6 +3965,9 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
|
|||
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
|
||||
: m_canvas(canvas)
|
||||
, m_context(nullptr)
|
||||
#if ENABLE_RETINA_GL
|
||||
, m_retina_helper(nullptr)
|
||||
#endif
|
||||
, m_in_render(false)
|
||||
, m_toolbar(GLToolbar::Normal)
|
||||
, m_view_toolbar(nullptr)
|
||||
|
@ -3933,8 +4001,12 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
|
|||
, m_external_gizmo_widgets_parent(nullptr)
|
||||
#endif // not ENABLE_IMGUI
|
||||
{
|
||||
if (m_canvas != nullptr)
|
||||
if (m_canvas != nullptr) {
|
||||
m_timer.SetOwner(m_canvas);
|
||||
#if ENABLE_RETINA_GL
|
||||
m_retina_helper.reset(new RetinaHelper(canvas));
|
||||
#endif
|
||||
}
|
||||
|
||||
m_selection.set_volumes(&m_volumes.volumes);
|
||||
}
|
||||
|
@ -5061,6 +5133,12 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt)
|
|||
|
||||
void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
{
|
||||
#if ENABLE_RETINA_GL
|
||||
const float scale = m_retina_helper->get_scale_factor();
|
||||
evt.SetX(evt.GetX() * scale);
|
||||
evt.SetY(evt.GetY() * scale);
|
||||
#endif
|
||||
|
||||
#if ENABLE_IMGUI
|
||||
auto imgui = wxGetApp().imgui();
|
||||
if (imgui->update_mouse_data(evt)) {
|
||||
|
@ -5283,7 +5361,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||
wxGetApp().obj_manipul()->update_settings_value(m_selection);
|
||||
// forces a frame render to update the view before the context menu is shown
|
||||
render();
|
||||
post_event(Vec2dEvent(EVT_GLCANVAS_RIGHT_CLICK, pos.cast<double>()));
|
||||
|
||||
Vec2d logical_pos = pos.cast<double>();
|
||||
#if ENABLE_RETINA_GL
|
||||
const float factor = m_retina_helper->get_scale_factor();
|
||||
logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor));
|
||||
#endif
|
||||
post_event(Vec2dEvent(EVT_GLCANVAS_RIGHT_CLICK, logical_pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5549,7 +5633,15 @@ Size GLCanvas3D::get_canvas_size() const
|
|||
if (m_canvas != nullptr)
|
||||
m_canvas->GetSize(&w, &h);
|
||||
|
||||
return Size(w, h);
|
||||
#if ENABLE_RETINA_GL
|
||||
const float factor = m_retina_helper->get_scale_factor();
|
||||
w *= factor;
|
||||
h *= factor;
|
||||
#else
|
||||
const float factor = 1.0;
|
||||
#endif
|
||||
|
||||
return Size(w, h, factor);
|
||||
}
|
||||
|
||||
Point GLCanvas3D::get_local_mouse_position() const
|
||||
|
@ -5856,6 +5948,26 @@ void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool foc
|
|||
}
|
||||
}
|
||||
|
||||
void GLCanvas3D::update_ui_from_settings()
|
||||
{
|
||||
#if ENABLE_RETINA_GL
|
||||
const float orig_scaling = m_retina_helper->get_scale_factor();
|
||||
|
||||
const bool use_retina = wxGetApp().app_config->get("use_retina_opengl") == "1";
|
||||
BOOST_LOG_TRIVIAL(debug) << "GLCanvas3D: Use Retina OpenGL: " << use_retina;
|
||||
m_retina_helper->set_use_retina(use_retina);
|
||||
const float new_scaling = m_retina_helper->get_scale_factor();
|
||||
|
||||
if (new_scaling != orig_scaling) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "GLCanvas3D: Scaling factor: " << new_scaling;
|
||||
|
||||
m_camera.zoom /= orig_scaling;
|
||||
m_camera.zoom *= new_scaling;
|
||||
_refresh_if_shown_on_screen();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_is_shown_on_screen() const
|
||||
{
|
||||
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
|
||||
|
@ -6009,6 +6121,9 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
|
|||
|
||||
#if ENABLE_IMGUI
|
||||
wxGetApp().imgui()->set_display_size((float)w, (float)h);
|
||||
#if ENABLE_RETINA_GL
|
||||
wxGetApp().imgui()->set_style_scaling(m_retina_helper->get_scale_factor());
|
||||
#endif // ENABLE_RETINA_GL
|
||||
#endif // ENABLE_IMGUI
|
||||
|
||||
// ensures that this canvas is current
|
||||
|
@ -6289,10 +6404,15 @@ void GLCanvas3D::_render_background() const
|
|||
|
||||
void GLCanvas3D::_render_bed(float theta) const
|
||||
{
|
||||
float scale_factor = 1.0;
|
||||
#if ENABLE_RETINA_GL
|
||||
scale_factor = m_retina_helper->get_scale_factor();
|
||||
#endif
|
||||
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
m_bed.render(theta, m_use_VBOs);
|
||||
m_bed.render(theta, m_use_VBOs, scale_factor);
|
||||
#else
|
||||
m_bed.render(theta);
|
||||
m_bed.render(theta, scale_factor);
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
}
|
||||
|
||||
|
@ -6371,8 +6491,13 @@ void GLCanvas3D::_render_objects() const
|
|||
|
||||
void GLCanvas3D::_render_selection() const
|
||||
{
|
||||
float scale_factor = 1.0;
|
||||
#if ENABLE_RETINA_GL
|
||||
scale_factor = m_retina_helper->get_scale_factor();
|
||||
#endif
|
||||
|
||||
if (!m_gizmos.is_running())
|
||||
m_selection.render();
|
||||
m_selection.render(scale_factor);
|
||||
}
|
||||
|
||||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
|
@ -6455,18 +6580,28 @@ void GLCanvas3D::_render_current_gizmo() const
|
|||
|
||||
void GLCanvas3D::_render_gizmos_overlay() const
|
||||
{
|
||||
#if ENABLE_RETINA_GL
|
||||
m_gizmos.set_overlay_scale(m_retina_helper->get_scale_factor());
|
||||
#endif
|
||||
m_gizmos.render_overlay(*this, m_selection);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_toolbar() const
|
||||
{
|
||||
#if ENABLE_RETINA_GL
|
||||
m_toolbar.set_icons_scale(m_retina_helper->get_scale_factor());
|
||||
#endif
|
||||
m_toolbar.render(*this);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_view_toolbar() const
|
||||
{
|
||||
if (m_view_toolbar != nullptr)
|
||||
if (m_view_toolbar != nullptr) {
|
||||
#if ENABLE_RETINA_GL
|
||||
m_view_toolbar->set_icons_scale(m_retina_helper->get_scale_factor());
|
||||
#endif
|
||||
m_view_toolbar->render(*this);
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_SHOW_CAMERA_TARGET
|
||||
|
@ -8167,7 +8302,7 @@ void GLCanvas3D::_generate_legend_texture(const GCodePreviewData& preview_data,
|
|||
|
||||
void GLCanvas3D::_generate_warning_texture(const std::string& msg)
|
||||
{
|
||||
m_warning_texture.generate(msg);
|
||||
m_warning_texture.generate(msg, *this);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_reset_warning_texture()
|
||||
|
@ -8192,6 +8327,10 @@ void GLCanvas3D::_resize_toolbars() const
|
|||
float zoom = get_camera_zoom();
|
||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||
|
||||
#if ENABLE_RETINA_GL
|
||||
m_toolbar.set_icons_scale(m_retina_helper->get_scale_factor());
|
||||
#endif
|
||||
|
||||
GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation();
|
||||
|
||||
switch (m_toolbar.get_layout_type())
|
||||
|
@ -8235,6 +8374,10 @@ void GLCanvas3D::_resize_toolbars() const
|
|||
|
||||
if (m_view_toolbar != nullptr)
|
||||
{
|
||||
#if ENABLE_RETINA_GL
|
||||
m_view_toolbar->set_icons_scale(m_retina_helper->get_scale_factor());
|
||||
#endif
|
||||
|
||||
// places the toolbar on the bottom-left corner of the 3d scene
|
||||
float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar->get_height()) * inv_zoom;
|
||||
float left = -0.5f * (float)cnv_size.get_width() * inv_zoom;
|
||||
|
|
|
@ -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
|
||||
|
@ -680,10 +703,6 @@ public:
|
|||
private:
|
||||
class Gizmos
|
||||
{
|
||||
static const float OverlayIconsScale;
|
||||
static const float OverlayBorder;
|
||||
static const float OverlayGapY;
|
||||
|
||||
public:
|
||||
enum EType : unsigned char
|
||||
{
|
||||
|
@ -704,6 +723,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 +736,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 +827,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 +857,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 +1058,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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -539,6 +539,7 @@ void GUI_App::update_mode()
|
|||
obj_list()->get_sizer()->Show(mode > comSimple);
|
||||
sidebar().set_mode_value(mode);
|
||||
// sidebar().show_buttons(mode == comExpert);
|
||||
obj_list()->unselect_objects();
|
||||
obj_list()->update_selections();
|
||||
obj_list()->update_object_menu();
|
||||
|
||||
|
|
|
@ -21,6 +21,25 @@ 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 =
|
||||
{
|
||||
{ L("Layers and Perimeters"), { "layer_height" , "perimeters", "top_solid_layers", "bottom_solid_layers" } },
|
||||
{ L("Infill") , { "fill_density", "fill_pattern" } },
|
||||
{ L("Support material") , { "support_material", "support_material_auto", "support_material_threshold",
|
||||
"support_material_pattern", "support_material_buildplate_only",
|
||||
"support_material_spacing" } },
|
||||
{ L("Extruders") , { "wipe_into_infill", "wipe_into_objects" } }
|
||||
};
|
||||
|
||||
// pt_SLA
|
||||
FreqSettingsBundle FREQ_SETTINGS_BUNDLE_SLA =
|
||||
{
|
||||
{ L("Pad and Support") , { "supports_enable", "pad_enable" } }
|
||||
};
|
||||
|
||||
ObjectList::ObjectList(wxWindow* parent) :
|
||||
wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE),
|
||||
m_parent(parent)
|
||||
|
@ -591,6 +610,20 @@ 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 FreqSettingsBundle& bundle = wxGetApp().plater()->printer_technology() == ptSLA ?
|
||||
FREQ_SETTINGS_BUNDLE_SLA : FREQ_SETTINGS_BUNDLE_FFF;
|
||||
|
||||
for (auto& it : bundle)
|
||||
{
|
||||
if (bundle_name == _(it.first))
|
||||
return it.second;
|
||||
}
|
||||
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;
|
||||
|
@ -677,6 +710,27 @@ void ObjectList::get_settings_choice(const wxString& category_name)
|
|||
|
||||
|
||||
// Add settings item for object
|
||||
update_settings_item();
|
||||
}
|
||||
|
||||
void ObjectList::get_freq_settings_choice(const wxString& bundle_name)
|
||||
{
|
||||
const std::vector<std::string>& options = get_options_for_bundle(bundle_name);
|
||||
|
||||
auto opt_keys = m_config->keys();
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
// Add settings item for object
|
||||
update_settings_item();
|
||||
}
|
||||
|
||||
void ObjectList::update_settings_item()
|
||||
{
|
||||
auto item = GetSelection();
|
||||
if (item) {
|
||||
if (m_objects_model->GetItemType(item) == itInstance)
|
||||
|
@ -688,7 +742,7 @@ void ObjectList::get_settings_choice(const wxString& category_name)
|
|||
else {
|
||||
auto panel = wxGetApp().sidebar().scrolled_panel();
|
||||
panel->Freeze();
|
||||
wxGetApp().obj_settings()->UpdateAndShow(true);//obj_manipul()->update_settings_list();
|
||||
wxGetApp().obj_settings()->UpdateAndShow(true);
|
||||
panel->Thaw();
|
||||
}
|
||||
}
|
||||
|
@ -711,7 +765,7 @@ void ObjectList::append_menu_item_add_generic(wxMenuItem* menu, const int type)
|
|||
menu->SetSubMenu(sub_menu);
|
||||
}
|
||||
|
||||
void ObjectList::append_menu_items_add_volume(wxMenu* menu, wxMenuItem* *item_separator)
|
||||
void ObjectList::append_menu_items_add_volume(wxMenu* menu)
|
||||
{
|
||||
// Note: id accords to type of the sub-object, so sequence of the menu items is important
|
||||
std::vector<std::string> menu_object_types_items = {L("Add part"), // ~ModelVolume::MODEL_PART
|
||||
|
@ -725,8 +779,6 @@ void ObjectList::append_menu_items_add_volume(wxMenu* menu, wxMenuItem* *item_se
|
|||
if (settings_id != wxNOT_FOUND)
|
||||
menu->Destroy(settings_id);
|
||||
}
|
||||
if (*item_separator)
|
||||
menu->Destroy(*item_separator);
|
||||
|
||||
const ConfigOptionMode mode = wxGetApp().get_mode();
|
||||
|
||||
|
@ -743,8 +795,6 @@ void ObjectList::append_menu_items_add_volume(wxMenu* menu, wxMenuItem* *item_se
|
|||
[this](wxCommandEvent&) { load_generic_subobject(_(L("Box")).ToUTF8().data(), ModelVolume::SUPPORT_BLOCKER); },
|
||||
*m_bmp_vector[ModelVolume::SUPPORT_BLOCKER]);
|
||||
|
||||
*item_separator = nullptr;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -758,8 +808,6 @@ void ObjectList::append_menu_items_add_volume(wxMenu* menu, wxMenuItem* *item_se
|
|||
|
||||
menu->Append(menu_item);
|
||||
}
|
||||
|
||||
*item_separator = menu->AppendSeparator();
|
||||
}
|
||||
|
||||
wxMenuItem* ObjectList::append_menu_item_split(wxMenu* menu)
|
||||
|
@ -768,23 +816,57 @@ wxMenuItem* ObjectList::append_menu_item_split(wxMenu* menu)
|
|||
[this](wxCommandEvent&) { split(); }, m_bmp_split, menu);
|
||||
}
|
||||
|
||||
wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu)
|
||||
wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_)
|
||||
{
|
||||
// Update (delete old & create new) settings popupmenu
|
||||
const auto settings_id = menu->FindItem(_("Add settings"));
|
||||
PrusaMenu* menu = dynamic_cast<PrusaMenu*>(menu_);
|
||||
// Delete old items from settings popupmenu
|
||||
auto settings_id = menu->FindItem(_("Add settings"));
|
||||
if (settings_id != wxNOT_FOUND)
|
||||
menu->Destroy(settings_id);
|
||||
|
||||
if (wxGetApp().get_mode() == comSimple)
|
||||
return nullptr;
|
||||
for (auto& it : FREQ_SETTINGS_BUNDLE_FFF)
|
||||
{
|
||||
settings_id = menu->FindItem(_(it.first));
|
||||
if (settings_id != wxNOT_FOUND)
|
||||
menu->Destroy(settings_id);
|
||||
}
|
||||
for (auto& it : FREQ_SETTINGS_BUNDLE_SLA)
|
||||
{
|
||||
settings_id = menu->FindItem(_(it.first));
|
||||
if (settings_id != wxNOT_FOUND)
|
||||
menu->Destroy(settings_id);
|
||||
}
|
||||
|
||||
auto menu_item = new wxMenuItem(menu, wxID_ANY, _(L("Add settings")));
|
||||
menu_item->SetBitmap(m_bmp_cog);
|
||||
menu->DestroySeparators(); // delete old separators
|
||||
|
||||
const auto sel_vol = get_selected_model_volume();
|
||||
if (sel_vol && sel_vol->type() >= ModelVolume::SUPPORT_ENFORCER)
|
||||
menu_item->Enable(false);
|
||||
else
|
||||
return nullptr;
|
||||
|
||||
const ConfigOptionMode mode = wxGetApp().get_mode();
|
||||
if (mode == comSimple)
|
||||
return nullptr;
|
||||
|
||||
// Create new items for settings popupmenu
|
||||
|
||||
menu->m_separator_frst = menu->AppendSeparator();
|
||||
|
||||
// Add frequently settings
|
||||
create_freq_settings_popupmenu(menu);
|
||||
|
||||
if (mode == comAdvanced)
|
||||
return nullptr;
|
||||
|
||||
menu->m_separator_scnd = menu->AppendSeparator();
|
||||
|
||||
// Add full settings list
|
||||
auto menu_item = new wxMenuItem(menu, wxID_ANY, _(L("Add settings")));
|
||||
menu_item->SetBitmap(m_bmp_cog);
|
||||
|
||||
// const auto sel_vol = get_selected_model_volume();
|
||||
// if (sel_vol && sel_vol->type() >= ModelVolume::SUPPORT_ENFORCER)
|
||||
// menu_item->Enable(false);
|
||||
// else
|
||||
menu_item->SetSubMenu(create_settings_popupmenu(menu));
|
||||
|
||||
return menu->Append(menu_item);
|
||||
|
@ -828,8 +910,8 @@ void ObjectList::create_part_popupmenu(wxMenu *menu)
|
|||
menu->AppendSeparator();
|
||||
append_menu_item_change_type(menu);
|
||||
|
||||
// Append settings popupmenu
|
||||
menu->AppendSeparator();
|
||||
// rest of a object_sla_menu will be added later in:
|
||||
// - append_menu_item_settings() -> for "Add (settings)"
|
||||
}
|
||||
|
||||
void ObjectList::create_instance_popupmenu(wxMenu*menu)
|
||||
|
@ -854,6 +936,24 @@ wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu)
|
|||
return menu;
|
||||
}
|
||||
|
||||
void ObjectList::create_freq_settings_popupmenu(wxMenu *menu)
|
||||
{
|
||||
const FreqSettingsBundle& bundle = wxGetApp().plater()->printer_technology() == ptFFF ?
|
||||
FREQ_SETTINGS_BUNDLE_FFF : FREQ_SETTINGS_BUNDLE_SLA;
|
||||
|
||||
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();
|
||||
|
||||
for (auto& it : bundle) {
|
||||
if (it.first.empty() || it.first == "Extruders" && extruders_cnt == 1)
|
||||
continue;
|
||||
|
||||
append_menu_item(menu, wxID_ANY, _(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);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectList::update_opt_keys(t_config_option_keys& opt_keys)
|
||||
{
|
||||
auto full_current_opts = get_options(false);
|
||||
|
@ -1177,15 +1277,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()
|
||||
|
@ -1824,7 +1921,7 @@ void ObjectList::update_settings_items()
|
|||
|
||||
void ObjectList::update_object_menu()
|
||||
{
|
||||
append_menu_items_add_volume(&m_menu_object, &m_mi_volumes_settings_separator);
|
||||
append_menu_items_add_volume(&m_menu_object);
|
||||
}
|
||||
|
||||
void ObjectList::instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idxs)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
class wxBoxSizer;
|
||||
class wxMenuItem;
|
||||
class PrusaObjectDataViewModel;
|
||||
class PrusaMenu;
|
||||
|
||||
namespace Slic3r {
|
||||
class ConfigOptionsGroup;
|
||||
|
@ -104,15 +105,14 @@ class ObjectList : public wxDataViewCtrl
|
|||
wxBitmap m_bmp_cog;
|
||||
wxBitmap m_bmp_split;
|
||||
|
||||
wxMenu m_menu_object;
|
||||
wxMenu m_menu_part;
|
||||
wxMenu m_menu_sla_object;
|
||||
wxMenu m_menu_instance;
|
||||
PrusaMenu m_menu_object;
|
||||
PrusaMenu m_menu_part;
|
||||
PrusaMenu m_menu_sla_object;
|
||||
PrusaMenu m_menu_instance;
|
||||
wxMenuItem* m_menu_item_split { nullptr };
|
||||
wxMenuItem* m_menu_item_split_part { nullptr };
|
||||
wxMenuItem* m_menu_item_settings { nullptr };
|
||||
wxMenuItem* m_menu_item_split_instances { nullptr };
|
||||
wxMenuItem* m_mi_volumes_settings_separator { nullptr };
|
||||
|
||||
std::vector<wxBitmap*> m_bmp_vector;
|
||||
|
||||
|
@ -162,8 +162,11 @@ public:
|
|||
void key_event(wxKeyEvent& event);
|
||||
|
||||
void get_settings_choice(const wxString& category_name);
|
||||
void get_freq_settings_choice(const wxString& bundle_name);
|
||||
void update_settings_item();
|
||||
|
||||
void append_menu_item_add_generic(wxMenuItem* menu, const int type);
|
||||
void append_menu_items_add_volume(wxMenu* menu, wxMenuItem* *item_separator);
|
||||
void append_menu_items_add_volume(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_split(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_settings(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_change_type(wxMenu* menu);
|
||||
|
@ -173,6 +176,7 @@ public:
|
|||
void create_part_popupmenu(wxMenu*menu);
|
||||
void create_instance_popupmenu(wxMenu*menu);
|
||||
wxMenu* create_settings_popupmenu(wxMenu *parent_menu);
|
||||
void create_freq_settings_popupmenu(wxMenu *parent_menu);
|
||||
|
||||
void update_opt_keys(t_config_option_keys& t_optopt_keys);
|
||||
|
||||
|
|
|
@ -267,7 +267,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 +278,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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -94,6 +94,16 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
|
|||
// m_plater->print = undef;
|
||||
_3DScene::remove_all_canvases();
|
||||
// Slic3r::GUI::deregister_on_request_update_callback();
|
||||
|
||||
// destroy and 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;
|
||||
}
|
||||
|
||||
// propagate event
|
||||
event.Skip();
|
||||
});
|
||||
|
|
|
@ -934,13 +934,11 @@ struct Plater::priv
|
|||
MainFrame *main_frame;
|
||||
|
||||
// Object popup menu
|
||||
wxMenu object_menu;
|
||||
PrusaMenu object_menu;
|
||||
// Part popup menu
|
||||
wxMenu part_menu;
|
||||
PrusaMenu part_menu;
|
||||
// SLA-Object popup menu
|
||||
wxMenu sla_object_menu;
|
||||
|
||||
wxMenuItem* separator_volumes_settings{ nullptr };
|
||||
PrusaMenu sla_object_menu;
|
||||
|
||||
// Data
|
||||
Slic3r::DynamicPrintConfig *config; // FIXME: leak?
|
||||
|
@ -1274,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()
|
||||
|
@ -1470,24 +1473,19 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
|
|||
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();
|
||||
|
||||
bool need_arrange = false;
|
||||
bool scaled_down = false;
|
||||
std::vector<size_t> obj_idxs;
|
||||
unsigned int obj_count = model.objects.size();
|
||||
|
||||
ModelInstancePtrs new_instances;
|
||||
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();
|
||||
instance->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -object->origin_translation(2)));
|
||||
object->center_around_origin();
|
||||
new_instances.emplace_back(object->add_instance());
|
||||
}
|
||||
|
||||
const Vec3d size = object->bounding_box().size();
|
||||
|
@ -1515,6 +1513,17 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
|
|||
// print.add_model_object(object);
|
||||
}
|
||||
|
||||
// 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);
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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.")),
|
||||
|
@ -2096,7 +2105,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++) {
|
||||
|
@ -2472,8 +2481,6 @@ bool Plater::priv::complit_init_sla_object_menu()
|
|||
append_menu_item(&sla_object_menu, wxID_ANY, _(L("Optimize orientation")), _(L("Optimize the rotation of the object for better print results.")),
|
||||
[this](wxCommandEvent&) { sla_optimize_rotation(); });
|
||||
|
||||
sla_object_menu.AppendSeparator();
|
||||
|
||||
// ui updates needs to be binded to the parent panel
|
||||
if (q != nullptr)
|
||||
{
|
||||
|
@ -2493,8 +2500,6 @@ bool Plater::priv::complit_init_part_menu()
|
|||
auto obj_list = sidebar->obj_list();
|
||||
obj_list->append_menu_item_change_type(&part_menu);
|
||||
|
||||
part_menu.AppendSeparator();
|
||||
|
||||
// ui updates needs to be binded to the parent panel
|
||||
if (q != nullptr)
|
||||
{
|
||||
|
@ -2621,7 +2626,7 @@ bool Plater::priv::can_mirror() const
|
|||
|
||||
void Plater::priv::update_object_menu()
|
||||
{
|
||||
sidebar->obj_list()->append_menu_items_add_volume(&object_menu, &separator_volumes_settings);
|
||||
sidebar->obj_list()->append_menu_items_add_volume(&object_menu);
|
||||
}
|
||||
|
||||
// Plater / Public
|
||||
|
|
|
@ -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.")));
|
||||
}
|
||||
|
||||
|
|
|
@ -2393,6 +2393,25 @@ void PrusaModeSizer::SetMode(const int mode)
|
|||
mode_btns[m]->SetState(m == mode);
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// PrusaMenu
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void PrusaMenu::DestroySeparators()
|
||||
{
|
||||
if (m_separator_frst) {
|
||||
Destroy(m_separator_frst);
|
||||
m_separator_frst = nullptr;
|
||||
}
|
||||
|
||||
if (m_separator_scnd) {
|
||||
Destroy(m_separator_scnd);
|
||||
m_separator_scnd = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ************************************** EXPERIMENTS ***************************************
|
||||
|
||||
// *****************************************************************************
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <wx/button.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/slider.h>
|
||||
#include <wx/menu.h>
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
@ -920,6 +921,29 @@ private:
|
|||
};
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// PrusaMenu
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class PrusaMenu : public wxMenu
|
||||
{
|
||||
public:
|
||||
PrusaMenu(const wxString& title, long style = 0)
|
||||
: wxMenu(title, style) {}
|
||||
|
||||
PrusaMenu(long style = 0)
|
||||
: wxMenu(style) {}
|
||||
|
||||
~PrusaMenu() {}
|
||||
|
||||
void DestroySeparators();
|
||||
|
||||
wxMenuItem* m_separator_frst { nullptr }; // use like separator before settings item
|
||||
wxMenuItem* m_separator_scnd { nullptr }; // use like separator between settings items
|
||||
};
|
||||
|
||||
|
||||
// ******************************* EXPERIMENTS **********************************************
|
||||
// ******************************************************************************************
|
||||
|
||||
|
|
29
src/slic3r/Utils/RetinaHelper.hpp
Normal file
29
src/slic3r/Utils/RetinaHelper.hpp
Normal 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
|
15
src/slic3r/Utils/RetinaHelperImpl.hmm
Normal file
15
src/slic3r/Utils/RetinaHelperImpl.hmm
Normal 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
|
111
src/slic3r/Utils/RetinaHelperImpl.mm
Normal file
111
src/slic3r/Utils/RetinaHelperImpl.mm
Normal 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
|
Loading…
Reference in a new issue