mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-08-09 06:45:25 -06:00
Merge branch 'master' into tm_colldetection_upgr
This commit is contained in:
commit
3999ffe713
131 changed files with 71551 additions and 40902 deletions
|
@ -50,7 +50,6 @@ if (SLIC3R_GUI)
|
|||
if(WIN32)
|
||||
message(STATUS "WXWIN environment set to: $ENV{WXWIN}")
|
||||
elseif(UNIX)
|
||||
message(STATUS "wx-config path: ${wxWidgets_CONFIG_EXECUTABLE}")
|
||||
set(wxWidgets_USE_UNICODE ON)
|
||||
if(SLIC3R_STATIC)
|
||||
set(wxWidgets_USE_STATIC ON)
|
||||
|
@ -72,6 +71,10 @@ if (SLIC3R_GUI)
|
|||
find_package(wxWidgets 3.1 REQUIRED COMPONENTS base core adv html gl)
|
||||
endif ()
|
||||
|
||||
if(UNIX)
|
||||
message(STATUS "wx-config path: ${wxWidgets_CONFIG_EXECUTABLE}")
|
||||
endif()
|
||||
|
||||
include(${wxWidgets_USE_FILE})
|
||||
endif()
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -310,16 +310,15 @@ ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines)
|
|||
polylines->insert(polylines->end(), tp.begin(), tp.end());
|
||||
}
|
||||
|
||||
void
|
||||
ExPolygon::get_trapezoids(Polygons* polygons) const
|
||||
/*
|
||||
void ExPolygon::get_trapezoids(Polygons* polygons) const
|
||||
{
|
||||
ExPolygons expp;
|
||||
expp.push_back(*this);
|
||||
boost::polygon::get_trapezoids(*polygons, expp);
|
||||
}
|
||||
|
||||
void
|
||||
ExPolygon::get_trapezoids(Polygons* polygons, double angle) const
|
||||
void ExPolygon::get_trapezoids(Polygons* polygons, double angle) const
|
||||
{
|
||||
ExPolygon clone = *this;
|
||||
clone.rotate(PI/2 - angle, Point(0,0));
|
||||
|
@ -327,12 +326,12 @@ ExPolygon::get_trapezoids(Polygons* polygons, double angle) const
|
|||
for (Polygons::iterator polygon = polygons->begin(); polygon != polygons->end(); ++polygon)
|
||||
polygon->rotate(-(PI/2 - angle), Point(0,0));
|
||||
}
|
||||
*/
|
||||
|
||||
// This algorithm may return more trapezoids than necessary
|
||||
// (i.e. it may break a single trapezoid in several because
|
||||
// other parts of the object have x coordinates in the middle)
|
||||
void
|
||||
ExPolygon::get_trapezoids2(Polygons* polygons) const
|
||||
void ExPolygon::get_trapezoids2(Polygons* polygons) const
|
||||
{
|
||||
// get all points of this ExPolygon
|
||||
Points pp = *this;
|
||||
|
@ -370,8 +369,7 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExPolygon::get_trapezoids2(Polygons* polygons, double angle) const
|
||||
void ExPolygon::get_trapezoids2(Polygons* polygons, double angle) const
|
||||
{
|
||||
ExPolygon clone = *this;
|
||||
clone.rotate(PI/2 - angle, Point(0,0));
|
||||
|
@ -382,8 +380,7 @@ ExPolygon::get_trapezoids2(Polygons* polygons, double angle) const
|
|||
|
||||
// While this triangulates successfully, it's NOT a constrained triangulation
|
||||
// as it will create more vertices on the boundaries than the ones supplied.
|
||||
void
|
||||
ExPolygon::triangulate(Polygons* polygons) const
|
||||
void ExPolygon::triangulate(Polygons* polygons) const
|
||||
{
|
||||
// first make trapezoids
|
||||
Polygons trapezoids;
|
||||
|
@ -394,8 +391,8 @@ ExPolygon::triangulate(Polygons* polygons) const
|
|||
polygon->triangulate_convex(polygons);
|
||||
}
|
||||
|
||||
void
|
||||
ExPolygon::triangulate_pp(Polygons* polygons) const
|
||||
/*
|
||||
void ExPolygon::triangulate_pp(Polygons* polygons) const
|
||||
{
|
||||
// convert polygons
|
||||
std::list<TPPLPoly> input;
|
||||
|
@ -452,9 +449,113 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
|
|||
polygons->push_back(p);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void
|
||||
ExPolygon::triangulate_p2t(Polygons* polygons) const
|
||||
std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygon &ex)
|
||||
{
|
||||
std::list<TPPLPoly> input;
|
||||
// contour
|
||||
{
|
||||
input.emplace_back();
|
||||
TPPLPoly &p = input.back();
|
||||
p.Init(int(ex.contour.points.size()));
|
||||
for (const Point &point : ex.contour.points) {
|
||||
size_t i = &point - &ex.contour.points.front();
|
||||
p[i].x = point(0);
|
||||
p[i].y = point(1);
|
||||
}
|
||||
p.SetHole(false);
|
||||
}
|
||||
// holes
|
||||
for (const Polygon &hole : ex.holes) {
|
||||
input.emplace_back();
|
||||
TPPLPoly &p = input.back();
|
||||
p.Init(hole.points.size());
|
||||
for (const Point &point : hole.points) {
|
||||
size_t i = &point - &hole.points.front();
|
||||
p[i].x = point(0);
|
||||
p[i].y = point(1);
|
||||
}
|
||||
p.SetHole(true);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygons &expps)
|
||||
{
|
||||
std::list<TPPLPoly> input;
|
||||
for (const ExPolygon &ex : expps) {
|
||||
// contour
|
||||
{
|
||||
input.emplace_back();
|
||||
TPPLPoly &p = input.back();
|
||||
p.Init(int(ex.contour.points.size()));
|
||||
for (const Point &point : ex.contour.points) {
|
||||
size_t i = &point - &ex.contour.points.front();
|
||||
p[i].x = point(0);
|
||||
p[i].y = point(1);
|
||||
}
|
||||
p.SetHole(false);
|
||||
}
|
||||
// holes
|
||||
for (const Polygon &hole : ex.holes) {
|
||||
input.emplace_back();
|
||||
TPPLPoly &p = input.back();
|
||||
p.Init(hole.points.size());
|
||||
for (const Point &point : hole.points) {
|
||||
size_t i = &point - &hole.points.front();
|
||||
p[i].x = point(0);
|
||||
p[i].y = point(1);
|
||||
}
|
||||
p.SetHole(true);
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
std::vector<Point> polypartition_output_to_triangles(const std::list<TPPLPoly> &output)
|
||||
{
|
||||
size_t num_triangles = 0;
|
||||
for (const TPPLPoly &poly : output)
|
||||
if (poly.GetNumPoints() >= 3)
|
||||
num_triangles += (size_t)poly.GetNumPoints() - 2;
|
||||
std::vector<Point> triangles;
|
||||
triangles.reserve(triangles.size() + num_triangles * 3);
|
||||
for (const TPPLPoly &poly : output) {
|
||||
long num_points = poly.GetNumPoints();
|
||||
if (num_points >= 3) {
|
||||
const TPPLPoint *pt0 = &poly[0];
|
||||
const TPPLPoint *pt1 = nullptr;
|
||||
const TPPLPoint *pt2 = &poly[1];
|
||||
for (long i = 2; i < num_points; ++ i) {
|
||||
pt1 = pt2;
|
||||
pt2 = &poly[i];
|
||||
triangles.emplace_back(coord_t(pt0->x), coord_t(pt0->y));
|
||||
triangles.emplace_back(coord_t(pt1->x), coord_t(pt1->y));
|
||||
triangles.emplace_back(coord_t(pt2->x), coord_t(pt2->y));
|
||||
}
|
||||
}
|
||||
}
|
||||
return triangles;
|
||||
}
|
||||
|
||||
void ExPolygon::triangulate_pp(Points *triangles) const
|
||||
{
|
||||
ExPolygons expp = union_ex(simplify_polygons(to_polygons(*this), true));
|
||||
std::list<TPPLPoly> input = expoly_to_polypartition_input(expp);
|
||||
// perform triangulation
|
||||
std::list<TPPLPoly> output;
|
||||
int res = TPPLPartition().Triangulate_MONO(&input, &output);
|
||||
// int TPPLPartition::Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles) {
|
||||
if (res != 1)
|
||||
throw std::runtime_error("Triangulation failed");
|
||||
*triangles = polypartition_output_to_triangles(output);
|
||||
}
|
||||
|
||||
// Uses the Poly2tri library maintained by Jan Niklas Hasse @jhasse // https://github.com/jhasse/poly2tri
|
||||
// See https://github.com/jhasse/poly2tri/blob/master/README.md for the limitations of the library!
|
||||
// No duplicate points are allowed, no very close points, holes must not touch outer contour etc.
|
||||
void ExPolygon::triangulate_p2t(Polygons* polygons) const
|
||||
{
|
||||
ExPolygons expp = simplify_polygons_ex(*this, true);
|
||||
|
||||
|
@ -478,16 +579,21 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const
|
|||
}
|
||||
|
||||
// perform triangulation
|
||||
cdt.Triangulate();
|
||||
std::vector<p2t::Triangle*> triangles = cdt.GetTriangles();
|
||||
|
||||
for (std::vector<p2t::Triangle*>::const_iterator triangle = triangles.begin(); triangle != triangles.end(); ++triangle) {
|
||||
Polygon p;
|
||||
for (int i = 0; i <= 2; ++i) {
|
||||
p2t::Point* point = (*triangle)->GetPoint(i);
|
||||
p.points.push_back(Point(point->x, point->y));
|
||||
try {
|
||||
cdt.Triangulate();
|
||||
std::vector<p2t::Triangle*> triangles = cdt.GetTriangles();
|
||||
|
||||
for (std::vector<p2t::Triangle*>::const_iterator triangle = triangles.begin(); triangle != triangles.end(); ++triangle) {
|
||||
Polygon p;
|
||||
for (int i = 0; i <= 2; ++i) {
|
||||
p2t::Point* point = (*triangle)->GetPoint(i);
|
||||
p.points.push_back(Point(point->x, point->y));
|
||||
}
|
||||
polygons->push_back(p);
|
||||
}
|
||||
polygons->push_back(p);
|
||||
} catch (const std::runtime_error & /* err */) {
|
||||
assert(false);
|
||||
// just ignore, don't triangulate
|
||||
}
|
||||
|
||||
for (p2t::Point *ptr : ContourPoints)
|
||||
|
@ -495,8 +601,7 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const
|
|||
}
|
||||
}
|
||||
|
||||
Lines
|
||||
ExPolygon::lines() const
|
||||
Lines ExPolygon::lines() const
|
||||
{
|
||||
Lines lines = this->contour.lines();
|
||||
for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) {
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
#include "Polyline.hpp"
|
||||
#include <vector>
|
||||
|
||||
// polygon class of the polypartition library
|
||||
class TPPLPoly;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class ExPolygon;
|
||||
|
@ -55,12 +58,13 @@ public:
|
|||
void simplify(double tolerance, ExPolygons* expolygons) const;
|
||||
void medial_axis(double max_width, double min_width, ThickPolylines* polylines) const;
|
||||
void medial_axis(double max_width, double min_width, Polylines* polylines) const;
|
||||
void get_trapezoids(Polygons* polygons) const;
|
||||
void get_trapezoids(Polygons* polygons, double angle) const;
|
||||
// void get_trapezoids(Polygons* polygons) const;
|
||||
// void get_trapezoids(Polygons* polygons, double angle) const;
|
||||
void get_trapezoids2(Polygons* polygons) const;
|
||||
void get_trapezoids2(Polygons* polygons, double angle) const;
|
||||
void triangulate(Polygons* polygons) const;
|
||||
void triangulate_pp(Polygons* polygons) const;
|
||||
// Triangulate into triples of points.
|
||||
void triangulate_pp(Points *triangles) const;
|
||||
void triangulate_p2t(Polygons* polygons) const;
|
||||
Lines lines() const;
|
||||
};
|
||||
|
@ -297,6 +301,10 @@ extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons);
|
|||
|
||||
extern bool remove_sticks(ExPolygon &poly);
|
||||
|
||||
extern std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygons &expp);
|
||||
extern std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygon &ex);
|
||||
extern std::vector<Point> polypartition_output_to_triangles(const std::list<TPPLPoly> &output);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
// start Boost
|
||||
|
|
|
@ -6,9 +6,12 @@
|
|||
#include <float.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../BoundingBox.hpp"
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../Utils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -38,6 +41,7 @@ struct FillParams
|
|||
// in this case we don't try to make more continuous paths
|
||||
bool complete;
|
||||
};
|
||||
static_assert(IsTriviallyCopyable<FillParams>::value, "FillParams class is not POD (and it should be - see constructor).");
|
||||
|
||||
class Fill
|
||||
{
|
||||
|
|
|
@ -582,10 +582,7 @@ namespace Slic3r {
|
|||
|
||||
IdToLayerHeightsProfileMap::iterator obj_layer_heights_profile = m_layer_heights_profiles.find(object.first);
|
||||
if (obj_layer_heights_profile != m_layer_heights_profiles.end())
|
||||
{
|
||||
object.second->layer_height_profile = obj_layer_heights_profile->second;
|
||||
object.second->layer_height_profile_valid = true;
|
||||
}
|
||||
|
||||
IdToSlaSupportPointsMap::iterator obj_sla_support_points = m_sla_support_points.find(object.first);
|
||||
if (obj_sla_support_points != m_sla_support_points.end() && !obj_sla_support_points->second.empty())
|
||||
|
@ -1926,7 +1923,7 @@ namespace Slic3r {
|
|||
for (const ModelObject* object : model.objects)
|
||||
{
|
||||
++count;
|
||||
std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
|
||||
const std::vector<double> &layer_height_profile = object->layer_height_profile;
|
||||
if ((layer_height_profile.size() >= 4) && ((layer_height_profile.size() % 2) == 0))
|
||||
{
|
||||
sprintf(buffer, "object_id=%d|", count);
|
||||
|
|
|
@ -279,8 +279,8 @@ void AMFParserContext::startElement(const char *name, const char **atts)
|
|||
node_type_new = NODE_TYPE_VERTICES;
|
||||
else if (strcmp(name, "volume") == 0) {
|
||||
assert(! m_volume);
|
||||
m_volume = m_object->add_volume(TriangleMesh());
|
||||
node_type_new = NODE_TYPE_VOLUME;
|
||||
m_volume = m_object->add_volume(TriangleMesh());
|
||||
node_type_new = NODE_TYPE_VOLUME;
|
||||
}
|
||||
} else if (m_path[2] == NODE_TYPE_INSTANCE) {
|
||||
assert(m_instance);
|
||||
|
@ -528,6 +528,7 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
}
|
||||
stl_get_size(&stl);
|
||||
m_volume->mesh.repair();
|
||||
m_volume->center_geometry();
|
||||
m_volume->calculate_convex_hull();
|
||||
m_volume_facets.clear();
|
||||
m_volume = nullptr;
|
||||
|
@ -578,7 +579,6 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
break;
|
||||
p = end + 1;
|
||||
}
|
||||
m_object->layer_height_profile_valid = true;
|
||||
}
|
||||
else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "sla_support_points") == 0) {
|
||||
// Parse object's layer height profile, a semicolon separated list of floats.
|
||||
|
@ -642,13 +642,17 @@ void AMFParserContext::endDocument()
|
|||
continue;
|
||||
}
|
||||
for (const Instance &instance : object.second.instances)
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
{
|
||||
#else
|
||||
if (instance.deltax_set && instance.deltay_set) {
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
ModelInstance *mi = m_model.objects[object.second.idx]->add_instance();
|
||||
mi->set_offset(Vec3d(instance.deltax_set ? (double)instance.deltax : 0.0, instance.deltay_set ? (double)instance.deltay : 0.0, instance.deltaz_set ? (double)instance.deltaz : 0.0));
|
||||
mi->set_rotation(Vec3d(instance.rx_set ? (double)instance.rx : 0.0, instance.ry_set ? (double)instance.ry : 0.0, instance.rz_set ? (double)instance.rz : 0.0));
|
||||
mi->set_scaling_factor(Vec3d(instance.scalex_set ? (double)instance.scalex : 1.0, instance.scaley_set ? (double)instance.scaley : 1.0, instance.scalez_set ? (double)instance.scalez : 1.0));
|
||||
mi->set_mirror(Vec3d(instance.mirrorx_set ? (double)instance.mirrorx : 1.0, instance.mirrory_set ? (double)instance.mirrory : 1.0, instance.mirrorz_set ? (double)instance.mirrorz : 1.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -885,7 +889,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
|
|||
stream << " <metadata type=\"slic3r." << key << "\">" << object->config.serialize(key) << "</metadata>\n";
|
||||
if (!object->name.empty())
|
||||
stream << " <metadata type=\"name\">" << xml_escape(object->name) << "</metadata>\n";
|
||||
std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
|
||||
const std::vector<double> &layer_height_profile = object->layer_height_profile;
|
||||
if (layer_height_profile.size() >= 4 && (layer_height_profile.size() % 2) == 0) {
|
||||
// Store the layer height profile as a single semicolon separated list.
|
||||
stream << " <metadata type=\"slic3r.layer_height_profile\">";
|
||||
|
@ -919,12 +923,14 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
|
|||
auto &stl = volume->mesh.stl;
|
||||
if (stl.v_shared == nullptr)
|
||||
stl_generate_shared_vertices(&stl);
|
||||
for (size_t i = 0; i < stl.stats.shared_vertices; ++ i) {
|
||||
const Transform3d& matrix = volume->get_matrix();
|
||||
for (size_t i = 0; i < stl.stats.shared_vertices; ++i) {
|
||||
stream << " <vertex>\n";
|
||||
stream << " <coordinates>\n";
|
||||
stream << " <x>" << stl.v_shared[i](0) << "</x>\n";
|
||||
stream << " <y>" << stl.v_shared[i](1) << "</y>\n";
|
||||
stream << " <z>" << stl.v_shared[i](2) << "</z>\n";
|
||||
Vec3d v = matrix * stl.v_shared[i].cast<double>();
|
||||
stream << " <x>" << v(0) << "</x>\n";
|
||||
stream << " <y>" << v(1) << "</y>\n";
|
||||
stream << " <z>" << v(2) << "</z>\n";
|
||||
stream << " </coordinates>\n";
|
||||
stream << " </vertex>\n";
|
||||
}
|
||||
|
|
|
@ -1589,9 +1589,11 @@ void GCode::process_layer(
|
|||
auto objects_by_extruder_it = by_extruder.find(extruder_id);
|
||||
if (objects_by_extruder_it == by_extruder.end())
|
||||
continue;
|
||||
|
||||
// We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
|
||||
for (int print_wipe_extrusions=const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) {
|
||||
if (print_wipe_extrusions == 0)
|
||||
bool is_anything_overridden = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden();
|
||||
for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) {
|
||||
if (is_anything_overridden && print_wipe_extrusions == 0)
|
||||
gcode+="; PURGING FINISHED\n";
|
||||
|
||||
for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
|
||||
|
@ -1628,7 +1630,7 @@ void GCode::process_layer(
|
|||
m_layer = layers[layer_id].layer();
|
||||
}
|
||||
for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
|
||||
const auto& by_region_specific = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden() ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
|
||||
const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
|
||||
|
||||
if (print.config().infill_first) {
|
||||
gcode += this->extrude_infill(print, by_region_specific);
|
||||
|
|
|
@ -1,24 +1,140 @@
|
|||
#include "PostProcessor.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
namespace Slic3r {
|
||||
// The standard Windows includes.
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
|
||||
//FIXME Ignore until we include boost::process
|
||||
void run_post_process_scripts(const std::string &path, const PrintConfig &config)
|
||||
// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
|
||||
// This routine appends the given argument to a command line such that CommandLineToArgvW will return the argument string unchanged.
|
||||
// Arguments in a command line should be separated by spaces; this function does not add these spaces.
|
||||
// Argument - Supplies the argument to encode.
|
||||
// CommandLine - Supplies the command line to which we append the encoded argument string.
|
||||
static void quote_argv_winapi(const std::wstring &argument, std::wstring &commmand_line_out)
|
||||
{
|
||||
// Don't quote unless we actually need to do so --- hopefully avoid problems if programs won't parse quotes properly.
|
||||
if (argument.empty() == false && argument.find_first_of(L" \t\n\v\"") == argument.npos)
|
||||
commmand_line_out.append(argument);
|
||||
else {
|
||||
commmand_line_out.push_back(L'"');
|
||||
for (auto it = argument.begin(); ; ++ it) {
|
||||
unsigned number_backslashes = 0;
|
||||
while (it != argument.end() && *it == L'\\') {
|
||||
++ it;
|
||||
++ number_backslashes;
|
||||
}
|
||||
if (it == argument.end()) {
|
||||
// Escape all backslashes, but let the terminating double quotation mark we add below be interpreted as a metacharacter.
|
||||
commmand_line_out.append(number_backslashes * 2, L'\\');
|
||||
break;
|
||||
} else if (*it == L'"') {
|
||||
// Escape all backslashes and the following double quotation mark.
|
||||
commmand_line_out.append(number_backslashes * 2 + 1, L'\\');
|
||||
commmand_line_out.push_back(*it);
|
||||
} else {
|
||||
// Backslashes aren't special here.
|
||||
commmand_line_out.append(number_backslashes, L'\\');
|
||||
commmand_line_out.push_back(*it);
|
||||
}
|
||||
}
|
||||
commmand_line_out.push_back(L'"');
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
static DWORD execute_process_winapi(const std::wstring &command_line)
|
||||
{
|
||||
// Extract the current environment to be passed to the child process.
|
||||
std::wstring envstr;
|
||||
{
|
||||
wchar_t *env = GetEnvironmentStrings();
|
||||
assert(env != nullptr);
|
||||
const wchar_t* var = env;
|
||||
size_t totallen = 0;
|
||||
size_t len;
|
||||
while ((len = wcslen(var)) > 0) {
|
||||
totallen += len + 1;
|
||||
var += len + 1;
|
||||
}
|
||||
envstr = std::wstring(env, totallen);
|
||||
FreeEnvironmentStrings(env);
|
||||
}
|
||||
|
||||
STARTUPINFOW startup_info;
|
||||
memset(&startup_info, 0, sizeof(startup_info));
|
||||
startup_info.cb = sizeof(STARTUPINFO);
|
||||
#if 0
|
||||
startup_info.dwFlags = STARTF_USESHOWWINDOW;
|
||||
startup_info.wShowWindow = SW_HIDE;
|
||||
#endif
|
||||
PROCESS_INFORMATION process_info;
|
||||
if (! ::CreateProcessW(
|
||||
nullptr /* lpApplicationName */, (LPWSTR)command_line.c_str(), nullptr /* lpProcessAttributes */, nullptr /* lpThreadAttributes */, false /* bInheritHandles */,
|
||||
CREATE_UNICODE_ENVIRONMENT /* | CREATE_NEW_CONSOLE */ /* dwCreationFlags */, (LPVOID)envstr.c_str(), nullptr /* lpCurrentDirectory */, &startup_info, &process_info))
|
||||
throw std::runtime_error(std::string("Failed starting the script ") + boost::nowide::narrow(command_line) + ", Win32 error: " + std::to_string(int(::GetLastError())));
|
||||
::WaitForSingleObject(process_info.hProcess, INFINITE);
|
||||
ULONG rc = 0;
|
||||
::GetExitCodeProcess(process_info.hProcess, &rc);
|
||||
::CloseHandle(process_info.hThread);
|
||||
::CloseHandle(process_info.hProcess);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Run the script. If it is a perl script, run it through the bundled perl interpreter.
|
||||
// If it is a batch file, run it through the cmd.exe.
|
||||
// Otherwise run it directly.
|
||||
static int run_script_win32(const std::string &script, const std::string &gcode)
|
||||
{
|
||||
// Unpack the argument list provided by the user.
|
||||
int nArgs;
|
||||
LPWSTR *szArglist = CommandLineToArgvW(boost::nowide::widen(script).c_str(), &nArgs);
|
||||
if (szArglist == nullptr || nArgs <= 0) {
|
||||
// CommandLineToArgvW failed. Maybe the command line escapment is invalid?
|
||||
throw std::runtime_error(std::string("Post processing script ") + script + " on file " + gcode + " failed. CommandLineToArgvW() refused to parse the command line path.");
|
||||
}
|
||||
|
||||
std::wstring command_line;
|
||||
std::wstring command = szArglist[0];
|
||||
if (! boost::filesystem::exists(boost::filesystem::path(command)))
|
||||
throw std::runtime_error(std::string("The configured post-processing script does not exist: ") + boost::nowide::narrow(command));
|
||||
if (boost::iends_with(command, L".pl")) {
|
||||
// This is a perl script. Run it through the perl interpreter.
|
||||
// The current process may be slic3r.exe or slic3r-console.exe.
|
||||
// Find the path of the process:
|
||||
wchar_t wpath_exe[_MAX_PATH + 1];
|
||||
::GetModuleFileNameW(nullptr, wpath_exe, _MAX_PATH);
|
||||
boost::filesystem::path path_exe(wpath_exe);
|
||||
boost::filesystem::path path_perl = path_exe.parent_path() / "perl" / "perl.exe";
|
||||
if (! boost::filesystem::exists(path_perl)) {
|
||||
LocalFree(szArglist);
|
||||
throw std::runtime_error(std::string("Perl interpreter ") + path_perl.string() + " does not exist.");
|
||||
}
|
||||
// Replace it with the current perl interpreter.
|
||||
quote_argv_winapi(boost::nowide::widen(path_perl.string()), command_line);
|
||||
command_line += L" ";
|
||||
} else if (boost::iends_with(command, ".bat")) {
|
||||
// Run a batch file through the command line interpreter.
|
||||
command_line = L"cmd.exe /C ";
|
||||
}
|
||||
|
||||
for (int i = 0; i < nArgs; ++ i) {
|
||||
quote_argv_winapi(szArglist[i], command_line);
|
||||
command_line += L" ";
|
||||
}
|
||||
LocalFree(szArglist);
|
||||
quote_argv_winapi(boost::nowide::widen(gcode), command_line);
|
||||
return (int)execute_process_winapi(command_line);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <boost/process/system.hpp>
|
||||
#ifndef WIN32
|
||||
#include <sys/stat.h> //for getting filesystem UID/GID
|
||||
#include <unistd.h> //for getting current UID/GID
|
||||
#include <boost/process.hpp>
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -33,44 +149,38 @@ void run_post_process_scripts(const std::string &path, const PrintConfig &config
|
|||
if (! boost::filesystem::exists(gcode_file))
|
||||
throw std::runtime_error(std::string("Post-processor can't find exported gcode file"));
|
||||
|
||||
for (std::string script: config.post_process.values) {
|
||||
// Ignore empty post processing script lines.
|
||||
boost::trim(script);
|
||||
if (script.empty())
|
||||
continue;
|
||||
BOOST_LOG_TRIVIAL(info) << "Executing script " << script << " on file " << path;
|
||||
if (! boost::filesystem::exists(boost::filesystem::path(script)))
|
||||
throw std::runtime_error(std::string("The configured post-processing script does not exist: ") + script);
|
||||
#ifndef WIN32
|
||||
struct stat info;
|
||||
if (stat(script.c_str(), &info))
|
||||
throw std::runtime_error(std::string("Cannot read information for post-processing script: ") + script);
|
||||
boost::filesystem::perms script_perms = boost::filesystem::status(script).permissions();
|
||||
//if UID matches, check UID perm. else if GID matches, check GID perm. Otherwise check other perm.
|
||||
if (!(script_perms & ((info.st_uid == geteuid()) ? boost::filesystem::perms::owner_exe
|
||||
: ((info.st_gid == getegid()) ? boost::filesystem::perms::group_exe
|
||||
: boost::filesystem::perms::others_exe))))
|
||||
throw std::runtime_error(std::string("The configured post-processing script is not executable: check permissions. ") + script);
|
||||
#endif
|
||||
int result = 0;
|
||||
for (const std::string &scripts : config.post_process.values) {
|
||||
std::vector<std::string> lines;
|
||||
boost::split(lines, scripts, boost::is_any_of("\r\n"));
|
||||
for (std::string script : lines) {
|
||||
// Ignore empty post processing script lines.
|
||||
boost::trim(script);
|
||||
if (script.empty())
|
||||
continue;
|
||||
BOOST_LOG_TRIVIAL(info) << "Executing script " << script << " on file " << path;
|
||||
#ifdef WIN32
|
||||
if (boost::iends_with(file, ".gcode")) {
|
||||
// The current process may be slic3r.exe or slic3r-console.exe.
|
||||
// Find the path of the process:
|
||||
wchar_t wpath_exe[_MAX_PATH + 1];
|
||||
::GetModuleFileNameW(nullptr, wpath_exe, _MAX_PATH);
|
||||
boost::filesystem::path path_exe(wpath_exe);
|
||||
// Replace it with the current perl interpreter.
|
||||
result = boost::process::system((path_exe.parent_path() / "perl5.24.0.exe").string(), script, gcode_file);
|
||||
} else
|
||||
int result = run_script_win32(script, gcode_file.string());
|
||||
#else
|
||||
result = boost::process::system(script, gcode_file);
|
||||
//FIXME testing existence of a script is risky, as the script line may contain the script and some additional command line parameters.
|
||||
// We would have to process the script line into parameters before testing for the existence of the command, the command may be looked up
|
||||
// in the PATH etc.
|
||||
if (! boost::filesystem::exists(boost::filesystem::path(script)))
|
||||
throw std::runtime_error(std::string("The configured post-processing script does not exist: ") + script);
|
||||
struct stat info;
|
||||
if (stat(script.c_str(), &info))
|
||||
throw std::runtime_error(std::string("Cannot read information for post-processing script: ") + script);
|
||||
boost::filesystem::perms script_perms = boost::filesystem::status(script).permissions();
|
||||
//if UID matches, check UID perm. else if GID matches, check GID perm. Otherwise check other perm.
|
||||
if (!(script_perms & ((info.st_uid == geteuid()) ? boost::filesystem::perms::owner_exe
|
||||
: ((info.st_gid == getegid()) ? boost::filesystem::perms::group_exe
|
||||
: boost::filesystem::perms::others_exe))))
|
||||
throw std::runtime_error(std::string("The configured post-processing script is not executable: check permissions. ") + script);
|
||||
int result = boost::process::system(script, gcode_file);
|
||||
if (result < 0)
|
||||
BOOST_LOG_TRIVIAL(error) << "Script " << script << " on file " << path << " failed. Negative error code returned.";
|
||||
#endif
|
||||
if (result < 0)
|
||||
BOOST_LOG_TRIVIAL(error) << "Script " << script << " on file " << path << " failed. Negative error code returned.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
|
|
|
@ -207,8 +207,7 @@ static bool sort_pointfs(const Vec3d& a, const Vec3d& b)
|
|||
}
|
||||
|
||||
// This implementation is based on Andrew's monotone chain 2D convex hull algorithm
|
||||
Polygon
|
||||
convex_hull(Points points)
|
||||
Polygon convex_hull(Points points)
|
||||
{
|
||||
assert(points.size() >= 3);
|
||||
// sort input points
|
||||
|
@ -1182,59 +1181,47 @@ Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation,
|
|||
Vec3d extract_euler_angles(const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>& rotation_matrix)
|
||||
{
|
||||
#if ENABLE_NEW_EULER_ANGLES
|
||||
// reference: http://www.gregslabaugh.net/publications/euler.pdf
|
||||
auto is_approx = [](double value, double test_value) -> bool { return std::abs(value - test_value) < EPSILON; };
|
||||
|
||||
bool x_only = is_approx(rotation_matrix(0, 0), 1.0) && is_approx(rotation_matrix(0, 1), 0.0) && is_approx(rotation_matrix(0, 2), 0.0) && is_approx(rotation_matrix(1, 0), 0.0) && is_approx(rotation_matrix(2, 0), 0.0);
|
||||
bool y_only = is_approx(rotation_matrix(0, 1), 0.0) && is_approx(rotation_matrix(1, 0), 0.0) && is_approx(rotation_matrix(1, 1), 1.0) && is_approx(rotation_matrix(1, 2), 0.0) && is_approx(rotation_matrix(2, 1), 0.0);
|
||||
bool z_only = is_approx(rotation_matrix(0, 2), 0.0) && is_approx(rotation_matrix(1, 2), 0.0) && is_approx(rotation_matrix(2, 0), 0.0) && is_approx(rotation_matrix(2, 1), 0.0) && is_approx(rotation_matrix(2, 2), 1.0);
|
||||
// bool xy_only = is_approx(rotation_matrix(0, 1), 0.0); // Rx * Ry
|
||||
bool yx_only = is_approx(rotation_matrix(1, 0), 0.0); // Ry * Rx
|
||||
// bool xz_only = is_approx(rotation_matrix(0, 2), 0.0); // Rx * Rz
|
||||
// bool zx_only = is_approx(rotation_matrix(2, 0), 0.0); // Rz * Rx
|
||||
// bool yz_only = is_approx(rotation_matrix(1, 2), 0.0); // Ry * Rz
|
||||
// bool zy_only = is_approx(rotation_matrix(2, 1), 0.0); // Rz * Ry
|
||||
|
||||
Vec3d angles = Vec3d::Zero();
|
||||
if (x_only || y_only || z_only)
|
||||
Vec3d angles1 = Vec3d::Zero();
|
||||
Vec3d angles2 = Vec3d::Zero();
|
||||
if (is_approx(std::abs(rotation_matrix(2, 0)), 1.0))
|
||||
{
|
||||
angles = rotation_matrix.eulerAngles(0, 1, 2);
|
||||
if (x_only && (std::abs(angles(1)) == (double)PI) && (std::abs(angles(2)) == (double)PI))
|
||||
angles1(2) = 0.0;
|
||||
if (rotation_matrix(2, 0) < 0.0) // == -1.0
|
||||
{
|
||||
angles(0) -= (double)PI;
|
||||
angles(1) = 0.0;
|
||||
angles(2) = 0.0;
|
||||
angles1(1) = 0.5 * (double)PI;
|
||||
angles1(0) = angles1(2) + ::atan2(rotation_matrix(0, 1), rotation_matrix(0, 2));
|
||||
}
|
||||
else // == 1.0
|
||||
{
|
||||
angles1(1) = - 0.5 * (double)PI;
|
||||
angles1(0) = - angles1(2) + ::atan2(- rotation_matrix(0, 1), - rotation_matrix(0, 2));
|
||||
}
|
||||
angles2 = angles1;
|
||||
}
|
||||
else
|
||||
{
|
||||
double cy_abs = ::sqrt(sqr(rotation_matrix(0, 0)) + sqr(rotation_matrix(1, 0)));
|
||||
angles(0) = ::atan2(rotation_matrix(2, 1), rotation_matrix(2, 2));
|
||||
angles(1) = ::atan2(-rotation_matrix(2, 0), cy_abs);
|
||||
angles(2) = ::atan2(rotation_matrix(1, 0), rotation_matrix(0, 0));
|
||||
if (yx_only && (angles(2) == (double)PI))
|
||||
{
|
||||
angles(0) -= (double)PI;
|
||||
angles(1) = (double)PI - angles(1);
|
||||
angles(2) = 0.0;
|
||||
}
|
||||
angles1(1) = -::asin(rotation_matrix(2, 0));
|
||||
double inv_cos1 = 1.0 / ::cos(angles1(1));
|
||||
angles1(0) = ::atan2(rotation_matrix(2, 1) * inv_cos1, rotation_matrix(2, 2) * inv_cos1);
|
||||
angles1(2) = ::atan2(rotation_matrix(1, 0) * inv_cos1, rotation_matrix(0, 0) * inv_cos1);
|
||||
|
||||
angles2(1) = (double)PI - angles1(1);
|
||||
double inv_cos2 = 1.0 / ::cos(angles2(1));
|
||||
angles2(0) = ::atan2(rotation_matrix(2, 1) * inv_cos2, rotation_matrix(2, 2) * inv_cos2);
|
||||
angles2(2) = ::atan2(rotation_matrix(1, 0) * inv_cos2, rotation_matrix(0, 0) * inv_cos2);
|
||||
}
|
||||
|
||||
// // debug check
|
||||
// Geometry::Transformation t;
|
||||
// t.set_rotation(angles);
|
||||
// if (!t.get_matrix().matrix().block(0, 0, 3, 3).isApprox(rotation_matrix))
|
||||
// {
|
||||
// std::cout << "something went wrong in extracting euler angles from matrix" << std::endl;
|
||||
//
|
||||
//// Eigen::Matrix<double, 3, 3, Eigen::DontAlign> m = t.get_matrix().matrix().block(0, 0, 3, 3);
|
||||
//// for (int r = 0; r < 3; ++r)
|
||||
//// {
|
||||
//// for (int c = 0; c < 3; ++c)
|
||||
//// {
|
||||
//// std::cout << r << ", " << c << ": " << m(r, c) << " - " << rotation_matrix(r, c) << std::endl;
|
||||
//// }
|
||||
//// }
|
||||
// }
|
||||
// The following euristic is the best found up to now (in the sense that it works fine with the greatest number of edge use-cases)
|
||||
// but there are other use-cases were it does not
|
||||
// We need to improve it
|
||||
double min_1 = angles1.cwiseAbs().minCoeff();
|
||||
double min_2 = angles2.cwiseAbs().minCoeff();
|
||||
bool use_1 = (min_1 < min_2) || (is_approx(min_1, min_2) && (angles1.norm() <= angles2.norm()));
|
||||
|
||||
Vec3d angles = use_1 ? angles1 : angles2;
|
||||
#else
|
||||
auto y_only = [](const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>& matrix) -> bool {
|
||||
return (matrix(0, 1) == 0.0) && (matrix(1, 0) == 0.0) && (matrix(1, 1) == 1.0) && (matrix(1, 2) == 0.0) && (matrix(2, 1) == 0.0);
|
||||
|
@ -1303,13 +1290,18 @@ void Transformation::Flags::set(bool dont_translate, bool dont_rotate, bool dont
|
|||
}
|
||||
|
||||
Transformation::Transformation()
|
||||
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
: m_offset(Vec3d::Zero())
|
||||
, m_rotation(Vec3d::Zero())
|
||||
, m_scaling_factor(Vec3d::Ones())
|
||||
, m_mirror(Vec3d::Ones())
|
||||
, m_matrix(Transform3d::Identity())
|
||||
, m_dirty(false)
|
||||
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
{
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
reset();
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
}
|
||||
|
||||
Transformation::Transformation(const Transform3d& transform)
|
||||
|
@ -1428,6 +1420,18 @@ void Transformation::set_from_transform(const Transform3d& transform)
|
|||
// std::cout << "something went wrong in extracting data from matrix" << std::endl;
|
||||
}
|
||||
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
void Transformation::reset()
|
||||
{
|
||||
m_offset = Vec3d::Zero();
|
||||
m_rotation = Vec3d::Zero();
|
||||
m_scaling_factor = Vec3d::Ones();
|
||||
m_mirror = Vec3d::Ones();
|
||||
m_matrix = Transform3d::Identity();
|
||||
m_dirty = false;
|
||||
}
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
const Transform3d& Transformation::get_matrix(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const
|
||||
{
|
||||
if (m_dirty || m_flags.needs_update(dont_translate, dont_rotate, dont_scale, dont_mirror))
|
||||
|
|
|
@ -237,7 +237,7 @@ public:
|
|||
void set_rotation(const Vec3d& rotation);
|
||||
void set_rotation(Axis axis, double rotation);
|
||||
|
||||
Vec3d get_scaling_factor() const { return m_scaling_factor; }
|
||||
const Vec3d& get_scaling_factor() const { return m_scaling_factor; }
|
||||
double get_scaling_factor(Axis axis) const { return m_scaling_factor(axis); }
|
||||
|
||||
void set_scaling_factor(const Vec3d& scaling_factor);
|
||||
|
@ -251,6 +251,10 @@ public:
|
|||
|
||||
void set_from_transform(const Transform3d& transform);
|
||||
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
void reset();
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const;
|
||||
|
||||
Transformation operator * (const Transformation& other) const;
|
||||
|
|
|
@ -570,10 +570,11 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
|
|||
this->sla_support_points = rhs.sla_support_points;
|
||||
this->layer_height_ranges = rhs.layer_height_ranges;
|
||||
this->layer_height_profile = rhs.layer_height_profile;
|
||||
this->layer_height_profile_valid = rhs.layer_height_profile_valid;
|
||||
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());
|
||||
|
@ -602,10 +603,11 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
|
|||
this->sla_support_points = std::move(rhs.sla_support_points);
|
||||
this->layer_height_ranges = std::move(rhs.layer_height_ranges);
|
||||
this->layer_height_profile = std::move(rhs.layer_height_profile);
|
||||
this->layer_height_profile_valid = std::move(rhs.layer_height_profile_valid);
|
||||
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);
|
||||
|
@ -641,6 +643,9 @@ ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh)
|
|||
{
|
||||
ModelVolume* v = new ModelVolume(this, mesh);
|
||||
this->volumes.push_back(v);
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
v->center_geometry();
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
this->invalidate_bounding_box();
|
||||
return v;
|
||||
}
|
||||
|
@ -649,6 +654,9 @@ ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh)
|
|||
{
|
||||
ModelVolume* v = new ModelVolume(this, std::move(mesh));
|
||||
this->volumes.push_back(v);
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
v->center_geometry();
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
this->invalidate_bounding_box();
|
||||
return v;
|
||||
}
|
||||
|
@ -657,6 +665,9 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other)
|
|||
{
|
||||
ModelVolume* v = new ModelVolume(this, other);
|
||||
this->volumes.push_back(v);
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
v->center_geometry();
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
this->invalidate_bounding_box();
|
||||
return v;
|
||||
}
|
||||
|
@ -665,6 +676,9 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&me
|
|||
{
|
||||
ModelVolume* v = new ModelVolume(this, other, std::move(mesh));
|
||||
this->volumes.push_back(v);
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
v->center_geometry();
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
this->invalidate_bounding_box();
|
||||
return v;
|
||||
}
|
||||
|
@ -675,6 +689,23 @@ void ModelObject::delete_volume(size_t idx)
|
|||
delete *i;
|
||||
this->volumes.erase(i);
|
||||
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
if (this->volumes.size() == 1)
|
||||
{
|
||||
// only one volume left
|
||||
// we need to collapse the volume transform into the instances transforms because now when selecting this volume
|
||||
// it will be seen as a single full instance ans so its volume transform may be ignored
|
||||
ModelVolume* v = this->volumes.front();
|
||||
Transform3d v_t = v->get_transformation().get_matrix();
|
||||
for (ModelInstance* inst : this->instances)
|
||||
{
|
||||
inst->set_transformation(Geometry::Transformation(inst->get_transformation().get_matrix() * v_t));
|
||||
}
|
||||
Geometry::Transformation t;
|
||||
v->set_transformation(t);
|
||||
v->set_new_unique_id();
|
||||
}
|
||||
#else
|
||||
if (this->volumes.size() == 1)
|
||||
{
|
||||
// only one volume left
|
||||
|
@ -691,6 +722,7 @@ void ModelObject::delete_volume(size_t idx)
|
|||
v->set_offset(Vec3d::Zero());
|
||||
v->set_new_unique_id();
|
||||
}
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
this->invalidate_bounding_box();
|
||||
}
|
||||
|
@ -755,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;
|
||||
}
|
||||
|
@ -814,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
|
||||
|
@ -868,15 +912,73 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
|
|||
return bb;
|
||||
}
|
||||
|
||||
// Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane.
|
||||
// This method is cheap in that it does not make any unnecessary copy of the volume meshes.
|
||||
// This method is used by the auto arrange function.
|
||||
Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance)
|
||||
{
|
||||
Points pts;
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part()) {
|
||||
const stl_file &stl = v->mesh.stl;
|
||||
Transform3d trafo = trafo_instance * v->get_matrix();
|
||||
if (stl.v_shared == nullptr) {
|
||||
// Using the STL faces.
|
||||
for (unsigned int i = 0; i < stl.stats.number_of_facets; ++ i) {
|
||||
const stl_facet &facet = stl.facet_start[i];
|
||||
for (size_t j = 0; j < 3; ++ j) {
|
||||
Vec3d p = trafo * facet.vertex[j].cast<double>();
|
||||
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Using the shared vertices should be a bit quicker than using the STL faces.
|
||||
for (int i = 0; i < stl.stats.shared_vertices; ++ i) {
|
||||
Vec3d p = trafo * stl.v_shared[i].cast<double>();
|
||||
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
|
||||
}
|
||||
}
|
||||
}
|
||||
std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) < b(0) || (a(0) == b(0) && a(1) < b(1)); });
|
||||
pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) == b(0) && a(1) == b(1); }), pts.end());
|
||||
|
||||
Polygon hull;
|
||||
int n = (int)pts.size();
|
||||
if (n >= 3) {
|
||||
int k = 0;
|
||||
hull.points.resize(2 * n);
|
||||
// Build lower hull
|
||||
for (int i = 0; i < n; ++ i) {
|
||||
while (k >= 2 && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
|
||||
-- k;
|
||||
hull[k ++] = pts[i];
|
||||
}
|
||||
// Build upper hull
|
||||
for (int i = n-2, t = k+1; i >= 0; i--) {
|
||||
while (k >= t && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
|
||||
-- k;
|
||||
hull[k ++] = pts[i];
|
||||
}
|
||||
hull.points.resize(k);
|
||||
assert(hull.points.front() == hull.points.back());
|
||||
hull.points.pop_back();
|
||||
}
|
||||
return hull;
|
||||
}
|
||||
|
||||
void ModelObject::center_around_origin()
|
||||
{
|
||||
// calculate the displacements needed to
|
||||
// center this object around the origin
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
BoundingBoxf3 bb = full_raw_mesh_bounding_box();
|
||||
#else
|
||||
BoundingBoxf3 bb;
|
||||
for (ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part())
|
||||
bb.merge(v->mesh.bounding_box());
|
||||
|
||||
bb.merge(v->mesh.bounding_box());
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
// Shift is the vector from the center of the bounding box to the origin
|
||||
Vec3d shift = -bb.center();
|
||||
|
||||
|
@ -1042,7 +1144,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
|
|||
|
||||
if (keep_upper) { upper->add_volume(*volume); }
|
||||
if (keep_lower) { lower->add_volume(*volume); }
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
TriangleMesh upper_mesh, lower_mesh;
|
||||
|
||||
// Transform the mesh by the combined transformation matrix
|
||||
|
@ -1050,7 +1153,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
|
|||
|
||||
// Perform cut
|
||||
TriangleMeshSlicer tms(&volume->mesh);
|
||||
tms.cut(z, &upper_mesh, &lower_mesh);
|
||||
tms.cut(float(z), &upper_mesh, &lower_mesh);
|
||||
|
||||
// Reset volume transformation except for offset
|
||||
const Vec3d offset = volume->get_offset();
|
||||
|
@ -1067,14 +1170,14 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
|
|||
}
|
||||
|
||||
if (keep_upper && upper_mesh.facets_count() > 0) {
|
||||
ModelVolume* vol = upper->add_volume(upper_mesh);
|
||||
vol->name = volume->name;
|
||||
ModelVolume* vol = upper->add_volume(upper_mesh);
|
||||
vol->name = volume->name;
|
||||
vol->config = volume->config;
|
||||
vol->set_material(volume->material_id(), *volume->material());
|
||||
}
|
||||
if (keep_lower && lower_mesh.facets_count() > 0) {
|
||||
ModelVolume* vol = lower->add_volume(lower_mesh);
|
||||
vol->name = volume->name;
|
||||
ModelVolume* vol = lower->add_volume(lower_mesh);
|
||||
vol->name = volume->name;
|
||||
vol->config = volume->config;
|
||||
vol->set_material(volume->material_id(), *volume->material());
|
||||
|
||||
|
@ -1153,7 +1256,9 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
|
|||
for (const ModelInstance *model_instance : this->instances)
|
||||
new_object->add_instance(*model_instance);
|
||||
ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh));
|
||||
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
new_vol->center_geometry();
|
||||
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
for (ModelInstance* model_instance : new_object->instances)
|
||||
{
|
||||
|
@ -1321,10 +1426,20 @@ int ModelVolume::extruder_id() const
|
|||
|
||||
void ModelVolume::center_geometry()
|
||||
{
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
Vec3d shift = mesh.bounding_box().center();
|
||||
if (!shift.isApprox(Vec3d::Zero()))
|
||||
{
|
||||
mesh.translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
|
||||
m_convex_hull.translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
|
||||
translate(shift);
|
||||
}
|
||||
#else
|
||||
Vec3d shift = -mesh.bounding_box().center();
|
||||
mesh.translate((float)shift(0), (float)shift(1), (float)shift(2));
|
||||
m_convex_hull.translate((float)shift(0), (float)shift(1), (float)shift(2));
|
||||
translate(-shift);
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
}
|
||||
|
||||
void ModelVolume::calculate_convex_hull()
|
||||
|
|
|
@ -170,12 +170,8 @@ public:
|
|||
// Variation of a layer thickness for spans of Z coordinates.
|
||||
t_layer_height_ranges layer_height_ranges;
|
||||
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
|
||||
// The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
|
||||
// The pairs of <z, layer_height> are packed into a 1D array.
|
||||
std::vector<coordf_t> layer_height_profile;
|
||||
// layer_height_profile is initialized when the layer editing mode is entered.
|
||||
// Only if the user really modified the layer height, layer_height_profile_valid is set
|
||||
// and used subsequently by the PrintObject.
|
||||
bool layer_height_profile_valid;
|
||||
|
||||
// This vector holds position of selected support points for SLA. The data are
|
||||
// saved in mesh coordinates to allow using them for several instances.
|
||||
|
@ -209,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;
|
||||
|
@ -223,6 +219,16 @@ public:
|
|||
BoundingBoxf3 raw_bounding_box() const;
|
||||
// A snug bounding box around the transformed non-modifier object volumes.
|
||||
BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const;
|
||||
// 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;
|
||||
|
||||
// Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane.
|
||||
// This method is cheap in that it does not make any unnecessary copy of the volume meshes.
|
||||
// This method is used by the auto arrange function.
|
||||
Polygon convex_hull_2d(const Transform3d &trafo_instance);
|
||||
|
||||
void center_around_origin();
|
||||
void ensure_on_bed();
|
||||
void translate_instances(const Vec3d& vector);
|
||||
|
@ -261,7 +267,8 @@ protected:
|
|||
void set_model(Model *model) { m_model = model; }
|
||||
|
||||
private:
|
||||
ModelObject(Model *model) : layer_height_profile_valid(false), m_model(model), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false) {}
|
||||
ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()),
|
||||
m_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {}
|
||||
~ModelObject();
|
||||
|
||||
/* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
|
||||
|
@ -280,6 +287,8 @@ private:
|
|||
// Bounding box, cached.
|
||||
mutable BoundingBoxf3 m_bounding_box;
|
||||
mutable bool m_bounding_box_valid;
|
||||
mutable BoundingBoxf3 m_raw_mesh_bounding_box;
|
||||
mutable bool m_raw_mesh_bounding_box_valid;
|
||||
};
|
||||
|
||||
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
|
||||
|
@ -319,6 +328,9 @@ public:
|
|||
// Extruder ID is only valid for FFF. Returns -1 for SLA or if the extruder ID is not applicable (support volumes).
|
||||
int extruder_id() const;
|
||||
|
||||
void set_splittable(const int val) { m_is_splittable = val; }
|
||||
int is_splittable() const { return m_is_splittable; }
|
||||
|
||||
// Split this volume, append the result to the object owning this volume.
|
||||
// Return the number of volumes created from this one.
|
||||
// This is useful to assign different materials to different volumes of an object.
|
||||
|
@ -391,6 +403,12 @@ private:
|
|||
TriangleMesh m_convex_hull;
|
||||
Geometry::Transformation m_transformation;
|
||||
|
||||
// flag to optimize the checking if the volume is splittable
|
||||
// -1 -> is unknown value (before first cheking)
|
||||
// 0 -> is not splittable
|
||||
// 1 -> is splittable
|
||||
int m_is_splittable {-1};
|
||||
|
||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object)
|
||||
{
|
||||
if (mesh.stl.stats.number_of_facets > 1)
|
||||
|
@ -455,7 +473,7 @@ public:
|
|||
void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); }
|
||||
void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); }
|
||||
|
||||
Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
|
||||
const Vec3d& get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
|
||||
double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); }
|
||||
|
||||
void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); }
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace arr {
|
|||
|
||||
using namespace libnest2d;
|
||||
|
||||
// Only for debugging. Prints the model object vertices on stdout.
|
||||
std::string toString(const Model& model, bool holes = true) {
|
||||
std::stringstream ss;
|
||||
|
||||
|
@ -78,6 +79,7 @@ std::string toString(const Model& model, bool holes = true) {
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
// Debugging: Save model to svg file.
|
||||
void toSVG(SVG& svg, const Model& model) {
|
||||
for(auto objptr : model.objects) {
|
||||
if(!objptr) continue;
|
||||
|
@ -121,6 +123,10 @@ Box boundingBox(const Box& pilebb, const Box& ibb ) {
|
|||
return Box(minc, maxc);
|
||||
}
|
||||
|
||||
// This is "the" object function which is evaluated many times for each vertex
|
||||
// (decimated with the accuracy parameter) of each object. Therefore it is
|
||||
// upmost crucial for this function to be as efficient as it possibly can be but
|
||||
// at the same time, it has to provide reasonable results.
|
||||
std::tuple<double /*score*/, Box /*farthest point from bin center*/>
|
||||
objfunc(const PointImpl& bincenter,
|
||||
const shapelike::Shapes<PolygonImpl>& merged_pile,
|
||||
|
@ -253,6 +259,8 @@ objfunc(const PointImpl& bincenter,
|
|||
return std::make_tuple(score, fullbb);
|
||||
}
|
||||
|
||||
// Fill in the placer algorithm configuration with values carefully chosen for
|
||||
// Slic3r.
|
||||
template<class PConf>
|
||||
void fillConfig(PConf& pcfg) {
|
||||
|
||||
|
@ -274,13 +282,19 @@ void fillConfig(PConf& pcfg) {
|
|||
pcfg.parallel = true;
|
||||
}
|
||||
|
||||
// Type trait for an arranger class for different bin types (box, circle,
|
||||
// polygon, etc...)
|
||||
template<class TBin>
|
||||
class AutoArranger {};
|
||||
|
||||
|
||||
// A class encapsulating the libnest2d Nester class and extending it with other
|
||||
// management and spatial index structures for acceleration.
|
||||
template<class TBin>
|
||||
class _ArrBase {
|
||||
protected:
|
||||
|
||||
// Useful type shortcuts...
|
||||
using Placer = TPacker<TBin>;
|
||||
using Selector = FirstFitSelection;
|
||||
using Packer = Nester<Placer, Selector>;
|
||||
|
@ -289,15 +303,15 @@ protected:
|
|||
using Pile = sl::Shapes<PolygonImpl>;
|
||||
|
||||
Packer m_pck;
|
||||
PConfig m_pconf; // Placement configuration
|
||||
PConfig m_pconf; // Placement configuration
|
||||
double m_bin_area;
|
||||
SpatIndex m_rtree;
|
||||
SpatIndex m_smallsrtree;
|
||||
double m_norm;
|
||||
Pile m_merged_pile;
|
||||
Box m_pilebb;
|
||||
ItemGroup m_remaining;
|
||||
ItemGroup m_items;
|
||||
SpatIndex m_rtree; // spatial index for the normal (bigger) objects
|
||||
SpatIndex m_smallsrtree; // spatial index for only the smaller items
|
||||
double m_norm; // A coefficient to scale distances
|
||||
Pile m_merged_pile; // The already merged pile (vector of items)
|
||||
Box m_pilebb; // The bounding box of the merged pile.
|
||||
ItemGroup m_remaining; // Remaining items (m_items at the beginning)
|
||||
ItemGroup m_items; // The items to be packed
|
||||
public:
|
||||
|
||||
_ArrBase(const TBin& bin, Distance dist,
|
||||
|
@ -308,6 +322,8 @@ public:
|
|||
{
|
||||
fillConfig(m_pconf);
|
||||
|
||||
// Set up a callback that is called just before arranging starts
|
||||
// This functionality is provided by the Nester class (m_pack).
|
||||
m_pconf.before_packing =
|
||||
[this](const Pile& merged_pile, // merged pile
|
||||
const ItemGroup& items, // packed items
|
||||
|
@ -342,18 +358,44 @@ 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();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class AutoArranger<Box>: public _ArrBase<Box> {
|
||||
// Arranger specialization for a Box shaped bin.
|
||||
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)
|
||||
{
|
||||
|
||||
// Here we set up the actual object function that calls the common
|
||||
// object function for all bin shapes than does an additional inside
|
||||
// check for the arranged pile.
|
||||
m_pconf.object_function = [this, bin] (const Item &item) {
|
||||
|
||||
auto result = objfunc(bin.center(),
|
||||
|
@ -387,15 +429,16 @@ inline lnCircle to_lnCircle(const Circle& circ) {
|
|||
return lnCircle({circ.center()(0), circ.center()(1)}, circ.radius());
|
||||
}
|
||||
|
||||
template<>
|
||||
class AutoArranger<lnCircle>: public _ArrBase<lnCircle> {
|
||||
// Arranger specialization for circle shaped bin.
|
||||
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.
|
||||
m_pconf.object_function = [this, &bin] (const Item &item) {
|
||||
|
||||
auto result = objfunc(bin.center(),
|
||||
|
@ -431,12 +474,13 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
|
||||
// Arranger specialization for a generalized polygon.
|
||||
// Warning: this is unfinished business. It may or may not work.
|
||||
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) {
|
||||
|
@ -461,8 +505,10 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template<> // Specialization with no bin
|
||||
class AutoArranger<bool>: public _ArrBase<Box> {
|
||||
// Specialization with no bin. In this case the arranger should just arrange
|
||||
// all objects into a minimum sized pile but it is not limited by a bin. A
|
||||
// consequence is that only one pile should be created.
|
||||
template<> class AutoArranger<bool>: public _ArrBase<Box> {
|
||||
public:
|
||||
|
||||
AutoArranger(Distance dist, std::function<void(unsigned)> progressind,
|
||||
|
@ -490,14 +536,15 @@ public:
|
|||
|
||||
// A container which stores a pointer to the 3D object and its projected
|
||||
// 2D shape from top view.
|
||||
using ShapeData2D =
|
||||
std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
|
||||
using ShapeData2D = std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
|
||||
|
||||
ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
||||
ShapeData2D ret;
|
||||
|
||||
auto s = std::accumulate(model.objects.begin(), model.objects.end(), size_t(0),
|
||||
[](size_t s, ModelObject* o){
|
||||
// Count all the items on the bin (all the object's instances)
|
||||
auto s = std::accumulate(model.objects.begin(), model.objects.end(),
|
||||
size_t(0), [](size_t s, ModelObject* o)
|
||||
{
|
||||
return s + o->instances.size();
|
||||
});
|
||||
|
||||
|
@ -506,23 +553,47 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
|||
for(ModelObject* objptr : model.objects) {
|
||||
if(objptr) {
|
||||
|
||||
TriangleMesh rmesh = objptr->raw_mesh();
|
||||
// TODO export the exact 2D projection. Cannot do it as libnest2d
|
||||
// does not support concave shapes (yet).
|
||||
ClipperLib::Path clpath;
|
||||
//WIP Vojtech's optimization of the calculation of the convex hull is not working correctly yet.
|
||||
#if 1
|
||||
{
|
||||
TriangleMesh rmesh = objptr->raw_mesh();
|
||||
|
||||
ModelInstance * finst = objptr->instances.front();
|
||||
ModelInstance * finst = objptr->instances.front();
|
||||
|
||||
// Object instances should carry the same scaling and
|
||||
// x, y rotation that is why we use the first instance.
|
||||
// The next line will apply only the full mirroring and scaling
|
||||
rmesh.transform(finst->get_matrix(true, true, false, false));
|
||||
rmesh.rotate_x(float(finst->get_rotation()(X)));
|
||||
rmesh.rotate_y(float(finst->get_rotation()(Y)));
|
||||
|
||||
// TODO export the exact 2D projection. Cannot do it as libnest2d
|
||||
// does not support concave shapes (yet).
|
||||
auto p = rmesh.convex_hull();
|
||||
|
||||
p.make_clockwise();
|
||||
p.append(p.first_point());
|
||||
clpath = Slic3rMultiPoint_to_ClipperPath(p);
|
||||
}
|
||||
#else
|
||||
// Object instances should carry the same scaling and
|
||||
// x, y rotation that is why we use the first instance.
|
||||
// The next line will apply only the full mirroring and scaling
|
||||
rmesh.transform(finst->get_matrix(true, true, false, false));
|
||||
rmesh.rotate_x(float(finst->get_rotation()(X)));
|
||||
rmesh.rotate_y(float(finst->get_rotation()(Y)));
|
||||
|
||||
// TODO export the exact 2D projection
|
||||
auto p = rmesh.convex_hull();
|
||||
|
||||
p.make_clockwise();
|
||||
p.append(p.first_point());
|
||||
auto clpath = Slic3rMultiPoint_to_ClipperPath(p);
|
||||
{
|
||||
ModelInstance *finst = objptr->instances.front();
|
||||
Vec3d rotation = finst->get_rotation();
|
||||
rotation.z() = 0.;
|
||||
Transform3d trafo_instance = Geometry::assemble_transform(Vec3d::Zero(), rotation, finst->get_scaling_factor(), finst->get_mirror());
|
||||
Polygon p = objptr->convex_hull_2d(trafo_instance);
|
||||
assert(! p.points.empty());
|
||||
p.reverse();
|
||||
assert(! p.is_counter_clockwise());
|
||||
p.append(p.first_point());
|
||||
clpath = Slic3rMultiPoint_to_ClipperPath(p);
|
||||
}
|
||||
#endif
|
||||
|
||||
for(ModelInstance* objinst : objptr->instances) {
|
||||
if(objinst) {
|
||||
|
@ -549,6 +620,8 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
// Apply the calculated translations and rotations (currently disabled) to the
|
||||
// Model object instances.
|
||||
void applyResult(
|
||||
IndexedPackGroup::value_type& group,
|
||||
Coord batch_offset,
|
||||
|
@ -576,6 +649,7 @@ void applyResult(
|
|||
}
|
||||
}
|
||||
|
||||
// Get the type of bed geometry from a simple vector of points.
|
||||
BedShapeHint bedShape(const Polyline &bed) {
|
||||
BedShapeHint ret;
|
||||
|
||||
|
@ -654,11 +728,15 @@ BedShapeHint bedShape(const Polyline &bed) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool arrange(Model &model,
|
||||
coord_t min_obj_distance,
|
||||
const Polyline &bed,
|
||||
BedShapeHint bedhint,
|
||||
bool first_bin_only,
|
||||
// The final client function to arrange the Model. A progress indicator and
|
||||
// a stop predicate can be also be passed to control the process.
|
||||
bool arrange(Model &model, // The model with the geometries
|
||||
coord_t min_obj_distance, // Has to be in scaled (clipper) measure
|
||||
const Polyline &bed, // The bed geometry.
|
||||
BedShapeHint bedhint, // Hint about the bed geometry type.
|
||||
bool first_bin_only, // What to do is not all items fit.
|
||||
|
||||
// Controlling callbacks.
|
||||
std::function<void (unsigned)> progressind,
|
||||
std::function<bool ()> stopcondition)
|
||||
{
|
||||
|
@ -759,5 +837,174 @@ bool arrange(Model &model,
|
|||
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
|
||||
|
|
|
@ -270,9 +270,22 @@ namespace client
|
|||
{
|
||||
std::string out;
|
||||
switch (type) {
|
||||
case TYPE_BOOL: out = boost::lexical_cast<std::string>(data.b); break;
|
||||
case TYPE_INT: out = boost::lexical_cast<std::string>(data.i); break;
|
||||
case TYPE_DOUBLE: out = boost::lexical_cast<std::string>(data.d); break;
|
||||
case TYPE_BOOL: out = data.b ? "true" : "false"; break;
|
||||
case TYPE_INT: out = std::to_string(data.i); break;
|
||||
case TYPE_DOUBLE:
|
||||
#if 0
|
||||
// The default converter produces trailing zeros after the decimal point.
|
||||
out = std::to_string(data.d);
|
||||
#else
|
||||
// ostringstream default converter produces no trailing zeros after the decimal point.
|
||||
// It seems to be doing what the old boost::to_string() did.
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << data.d;
|
||||
out = ss.str();
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case TYPE_STRING: out = *data.s; break;
|
||||
default: break;
|
||||
}
|
||||
|
|
|
@ -287,18 +287,8 @@ std::vector<unsigned int> Print::object_extruders() const
|
|||
{
|
||||
std::vector<unsigned int> extruders;
|
||||
extruders.reserve(m_regions.size() * 3);
|
||||
|
||||
for (const PrintRegion *region : m_regions) {
|
||||
// these checks reflect the same logic used in the GUI for enabling/disabling
|
||||
// extruder selection fields
|
||||
if (region->config().perimeters.value > 0 || m_config.brim_width.value > 0)
|
||||
extruders.emplace_back(region->config().perimeter_extruder - 1);
|
||||
if (region->config().fill_density.value > 0)
|
||||
extruders.emplace_back(region->config().infill_extruder - 1);
|
||||
if (region->config().top_solid_layers.value > 0 || region->config().bottom_solid_layers.value > 0)
|
||||
extruders.emplace_back(region->config().solid_infill_extruder - 1);
|
||||
}
|
||||
|
||||
for (const PrintRegion *region : m_regions)
|
||||
region->collect_object_printing_extruders(extruders);
|
||||
sort_remove_duplicates(extruders);
|
||||
return extruders;
|
||||
}
|
||||
|
@ -366,37 +356,6 @@ double Print::max_allowed_layer_height() const
|
|||
return nozzle_diameter_max;
|
||||
}
|
||||
|
||||
static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders)
|
||||
{
|
||||
if (opt.value > (int)num_extruders)
|
||||
// assign the default extruder
|
||||
opt.value = 1;
|
||||
}
|
||||
|
||||
static PrintObjectConfig object_config_from_model(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders)
|
||||
{
|
||||
PrintObjectConfig config = default_object_config;
|
||||
normalize_and_apply_config(config, object.config);
|
||||
// Clamp invalid extruders to the default extruder (with index 1).
|
||||
clamp_exturder_to_default(config.support_material_extruder, num_extruders);
|
||||
clamp_exturder_to_default(config.support_material_interface_extruder, num_extruders);
|
||||
return config;
|
||||
}
|
||||
|
||||
static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders)
|
||||
{
|
||||
PrintRegionConfig config = default_region_config;
|
||||
normalize_and_apply_config(config, volume.get_object()->config);
|
||||
normalize_and_apply_config(config, volume.config);
|
||||
if (! volume.material_id().empty())
|
||||
normalize_and_apply_config(config, volume.material()->config);
|
||||
// Clamp invalid extruders to the default extruder (with index 1).
|
||||
clamp_exturder_to_default(config.infill_extruder, num_extruders);
|
||||
clamp_exturder_to_default(config.perimeter_extruder, num_extruders);
|
||||
clamp_exturder_to_default(config.solid_infill_extruder, num_extruders);
|
||||
return config;
|
||||
}
|
||||
|
||||
// Caller is responsible for supplying models whose objects don't collide
|
||||
// and have explicit instance positions.
|
||||
void Print::add_model_object(ModelObject* model_object, int idx)
|
||||
|
@ -433,7 +392,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
|
|||
if (! volume->is_model_part() && ! volume->is_modifier())
|
||||
continue;
|
||||
// Get the config applied to this volume.
|
||||
PrintRegionConfig config = region_config_from_model_volume(m_default_region_config, *volume, 99999);
|
||||
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, 99999);
|
||||
// Find an existing print region with the same config.
|
||||
size_t region_id = size_t(-1);
|
||||
for (size_t i = 0; i < m_regions.size(); ++ i)
|
||||
|
@ -514,12 +473,12 @@ bool Print::apply_config(DynamicPrintConfig config)
|
|||
// If the new config for this volume differs from the other
|
||||
// volume configs currently associated to this region, it means
|
||||
// the region subdivision does not make sense anymore.
|
||||
if (! this_region_config.equals(region_config_from_model_volume(m_default_region_config, volume, 99999))) {
|
||||
if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999))) {
|
||||
rearrange_regions = true;
|
||||
goto exit_for_rearrange_regions;
|
||||
}
|
||||
} else {
|
||||
this_region_config = region_config_from_model_volume(m_default_region_config, volume, 99999);
|
||||
this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999);
|
||||
this_region_config_set = true;
|
||||
}
|
||||
for (const PrintRegionConfig &cfg : other_region_configs) {
|
||||
|
@ -563,10 +522,6 @@ exit_for_rearrange_regions:
|
|||
invalidated = true;
|
||||
}
|
||||
|
||||
// Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads.
|
||||
for (PrintObject *object : m_objects)
|
||||
object->update_layer_height_profile();
|
||||
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
|
@ -888,8 +843,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
if (model_parts_differ || modifiers_differ ||
|
||||
model_object.origin_translation != model_object_new.origin_translation ||
|
||||
model_object.layer_height_ranges != model_object_new.layer_height_ranges ||
|
||||
model_object.layer_height_profile != model_object_new.layer_height_profile ||
|
||||
model_object.layer_height_profile_valid != model_object_new.layer_height_profile_valid) {
|
||||
model_object.layer_height_profile != model_object_new.layer_height_profile) {
|
||||
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
|
||||
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
|
||||
for (auto it = range.first; it != range.second; ++ it) {
|
||||
|
@ -915,7 +869,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
if (object_config_changed)
|
||||
model_object.config = model_object_new.config;
|
||||
if (! object_diff.empty() || object_config_changed) {
|
||||
PrintObjectConfig new_config = object_config_from_model(m_default_object_config, model_object, num_extruders);
|
||||
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders);
|
||||
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
|
||||
for (auto it = range.first; it != range.second; ++ it) {
|
||||
t_config_option_keys diff = it->print_object->config().diff(new_config);
|
||||
|
@ -957,7 +911,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
old.emplace_back(&(*it));
|
||||
}
|
||||
// Generate a list of trafos and XY offsets for instances of a ModelObject
|
||||
PrintObjectConfig config = object_config_from_model(m_default_object_config, *model_object, num_extruders);
|
||||
PrintObjectConfig config = PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders);
|
||||
std::vector<PrintInstances> new_print_instances = print_objects_from_model_object(*model_object);
|
||||
if (old.empty()) {
|
||||
// Simple case, just generate new instances.
|
||||
|
@ -1048,11 +1002,11 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
// If the new config for this volume differs from the other
|
||||
// volume configs currently associated to this region, it means
|
||||
// the region subdivision does not make sense anymore.
|
||||
if (! this_region_config.equals(region_config_from_model_volume(m_default_region_config, volume, num_extruders)))
|
||||
if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, volume, num_extruders)))
|
||||
// Regions were split. Reset this print_object.
|
||||
goto print_object_end;
|
||||
} else {
|
||||
this_region_config = region_config_from_model_volume(m_default_region_config, volume, num_extruders);
|
||||
this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, num_extruders);
|
||||
for (size_t i = 0; i < region_id; ++i) {
|
||||
const PrintRegion ®ion_other = *m_regions[i];
|
||||
if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
|
||||
|
@ -1103,7 +1057,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
int region_id = -1;
|
||||
if (&print_object == &print_object0) {
|
||||
// Get the config applied to this volume.
|
||||
PrintRegionConfig config = region_config_from_model_volume(m_default_region_config, *volume, num_extruders);
|
||||
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, num_extruders);
|
||||
// Find an existing print region with the same config.
|
||||
int idx_empty_slot = -1;
|
||||
for (int i = 0; i < (int)m_regions.size(); ++ i) {
|
||||
|
@ -1139,13 +1093,6 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
}
|
||||
}
|
||||
|
||||
// Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads.
|
||||
for (PrintObject *object : m_objects)
|
||||
if (! object->layer_height_profile_valid)
|
||||
// No need to call the next line as the step should already be invalidated above.
|
||||
// update_apply_status(object->invalidate_step(posSlice));
|
||||
object->update_layer_height_profile();
|
||||
|
||||
//FIXME there may be a race condition with the G-code export running at the background thread.
|
||||
this->update_object_placeholders();
|
||||
|
||||
|
@ -1178,7 +1125,7 @@ std::string Print::validate() const
|
|||
// Check horizontal clearance.
|
||||
{
|
||||
Polygons convex_hulls_other;
|
||||
for (PrintObject *object : m_objects) {
|
||||
for (const PrintObject *object : m_objects) {
|
||||
// Get convex hull of all meshes assigned to this print object.
|
||||
Polygon convex_hull;
|
||||
{
|
||||
|
@ -1239,46 +1186,63 @@ std::string Print::validate() const
|
|||
return L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter and Repetier G-code flavors.");
|
||||
if (! m_config.use_relative_e_distances)
|
||||
return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).");
|
||||
SlicingParameters slicing_params0 = m_objects.front()->slicing_parameters();
|
||||
|
||||
const PrintObject* tallest_object = m_objects.front(); // let's find the tallest object
|
||||
for (const auto* object : m_objects)
|
||||
if (*(object->layer_height_profile.end()-2) > *(tallest_object->layer_height_profile.end()-2) )
|
||||
tallest_object = object;
|
||||
|
||||
for (PrintObject *object : m_objects) {
|
||||
SlicingParameters slicing_params = object->slicing_parameters();
|
||||
if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
|
||||
std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON)
|
||||
return L("The Wipe Tower is only supported for multiple objects if they have equal layer heigths");
|
||||
if (slicing_params.raft_layers() != slicing_params0.raft_layers())
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers");
|
||||
if (object->config().support_material_contact_distance != m_objects.front()->config().support_material_contact_distance)
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance");
|
||||
if (! equal_layering(slicing_params, slicing_params0))
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
|
||||
|
||||
if (m_config.variable_layer_height) { // comparing layer height profiles
|
||||
bool failed = false;
|
||||
// layer_height_profile should be set by Print::apply().
|
||||
if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size()) {
|
||||
int i = 0;
|
||||
while (i < object->layer_height_profile.size() && i < tallest_object->layer_height_profile.size()) {
|
||||
if (std::abs(tallest_object->layer_height_profile[i] - object->layer_height_profile[i])) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
if (i == object->layer_height_profile.size()-2) // this element contains this objects max z
|
||||
if (tallest_object->layer_height_profile[i] > object->layer_height_profile[i]) // the difference does not matter in this case
|
||||
++i;
|
||||
}
|
||||
if (m_objects.size() > 1) {
|
||||
bool has_custom_layering = false;
|
||||
std::vector<std::vector<coordf_t>> layer_height_profiles;
|
||||
for (const PrintObject *object : m_objects) {
|
||||
has_custom_layering = ! object->model_object()->layer_height_ranges.empty() || ! object->model_object()->layer_height_profile.empty();
|
||||
if (has_custom_layering) {
|
||||
layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>());
|
||||
break;
|
||||
}
|
||||
else
|
||||
failed = true;
|
||||
}
|
||||
SlicingParameters slicing_params0 = m_objects.front()->slicing_parameters();
|
||||
size_t tallest_object_idx = 0;
|
||||
if (has_custom_layering)
|
||||
PrintObject::update_layer_height_profile(*m_objects.front()->model_object(), slicing_params0, layer_height_profiles.front());
|
||||
for (size_t i = 1; i < m_objects.size(); ++ i) {
|
||||
const PrintObject *object = m_objects[i];
|
||||
const SlicingParameters slicing_params = object->slicing_parameters();
|
||||
if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
|
||||
std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON)
|
||||
return L("The Wipe Tower is only supported for multiple objects if they have equal layer heigths");
|
||||
if (slicing_params.raft_layers() != slicing_params0.raft_layers())
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers");
|
||||
if (object->config().support_material_contact_distance != m_objects.front()->config().support_material_contact_distance)
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance");
|
||||
if (! equal_layering(slicing_params, slicing_params0))
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
|
||||
if (has_custom_layering) {
|
||||
PrintObject::update_layer_height_profile(*object->model_object(), slicing_params, layer_height_profiles[i]);
|
||||
if (*(layer_height_profiles[i].end()-2) > *(layer_height_profiles[tallest_object_idx].end()-2))
|
||||
tallest_object_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed)
|
||||
return L("The Wipe tower is only supported if all objects have the same layer height profile");
|
||||
if (has_custom_layering) {
|
||||
const std::vector<coordf_t> &layer_height_profile_tallest = layer_height_profiles[tallest_object_idx];
|
||||
for (size_t idx_object = 0; idx_object < m_objects.size(); ++ idx_object) {
|
||||
const PrintObject *object = m_objects[idx_object];
|
||||
const std::vector<coordf_t> &layer_height_profile = layer_height_profiles[idx_object];
|
||||
bool failed = false;
|
||||
if (layer_height_profile_tallest.size() >= layer_height_profile.size()) {
|
||||
int i = 0;
|
||||
while (i < layer_height_profile.size() && i < layer_height_profile_tallest.size()) {
|
||||
if (std::abs(layer_height_profile_tallest[i] - layer_height_profile[i])) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
++ i;
|
||||
if (i == layer_height_profile.size() - 2) // this element contains this objects max z
|
||||
if (layer_height_profile_tallest[i] > layer_height_profile[i]) // the difference does not matter in this case
|
||||
++ i;
|
||||
}
|
||||
} else
|
||||
failed = true;
|
||||
if (failed)
|
||||
return L("The Wipe tower is only supported if all objects have the same layer height profile");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,10 @@ public:
|
|||
// Average diameter of nozzles participating on extruding this region.
|
||||
coordf_t bridging_height_avg(const PrintConfig &print_config) const;
|
||||
|
||||
// Collect extruder indices used to print this region's object.
|
||||
void collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const;
|
||||
static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, std::vector<unsigned int> &object_extruders);
|
||||
|
||||
// Methods modifying the PrintRegion's state:
|
||||
public:
|
||||
Print* print() { return m_print; }
|
||||
|
@ -79,16 +83,6 @@ public:
|
|||
// vector of (vectors of volume ids), indexed by region_id
|
||||
std::vector<std::vector<int>> region_volumes;
|
||||
|
||||
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
|
||||
// The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
|
||||
// layer_height_profile must not be set by the background thread.
|
||||
std::vector<coordf_t> layer_height_profile;
|
||||
// There is a layer_height_profile at both PrintObject and ModelObject. The layer_height_profile at the ModelObject
|
||||
// is used for interactive editing and for loading / storing into a project file (AMF file as of today).
|
||||
// This flag indicates that the layer_height_profile at the UI has been updated, therefore the backend needs to get it.
|
||||
// This flag is necessary as we cannot safely clear the layer_height_profile if the background calculation is running.
|
||||
bool layer_height_profile_valid;
|
||||
|
||||
// this is set to true when LayerRegion->slices is split in top/internal/bottom
|
||||
// so that next call to make_perimeters() performs a union() before computing loops
|
||||
bool typed_slices;
|
||||
|
@ -129,23 +123,19 @@ public:
|
|||
SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
|
||||
void delete_support_layer(int idx);
|
||||
|
||||
// To be used over the layer_height_profile of both the PrintObject and ModelObject
|
||||
// to initialize the height profile with the height ranges.
|
||||
bool update_layer_height_profile(std::vector<coordf_t> &layer_height_profile) const;
|
||||
|
||||
// Process layer_height_ranges, the raft layers and first layer thickness into layer_height_profile.
|
||||
// The layer_height_profile may be later modified interactively by the user to refine layers at sloping surfaces.
|
||||
bool update_layer_height_profile();
|
||||
|
||||
void reset_layer_height_profile();
|
||||
|
||||
void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action);
|
||||
// Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters.
|
||||
// Returns true, if the layer_height_profile was changed.
|
||||
static bool update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector<coordf_t> &layer_height_profile);
|
||||
|
||||
// Collect the slicing parameters, to be used by variable layer thickness algorithm,
|
||||
// by the interactive layer height editor and by the printing process itself.
|
||||
// The slicing parameters are dependent on various configuration values
|
||||
// (layer height, first layer height, raft settings, print nozzle diameter etc).
|
||||
SlicingParameters slicing_parameters() const;
|
||||
SlicingParameters slicing_parameters() const;
|
||||
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object);
|
||||
|
||||
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
|
||||
std::vector<unsigned int> object_extruders() const;
|
||||
|
||||
// Called when slicing to SVG (see Print.pm sub export_svg), and used by perimeters.t
|
||||
void slice();
|
||||
|
@ -172,13 +162,16 @@ protected:
|
|||
// Invalidate steps based on a set of parameters changed.
|
||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||
|
||||
static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders);
|
||||
static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders);
|
||||
|
||||
private:
|
||||
void make_perimeters();
|
||||
void prepare_infill();
|
||||
void infill();
|
||||
void generate_support_material();
|
||||
|
||||
void _slice();
|
||||
void _slice(const std::vector<coordf_t> &layer_height_profile);
|
||||
std::string _fix_slicing_errors();
|
||||
void _simplify_slices(double distance);
|
||||
void _make_perimeters();
|
||||
|
|
|
@ -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);
|
||||
|
@ -2487,7 +2500,6 @@ void PrintConfigDef::init_sla_params()
|
|||
def->label = L("Generate supports");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("Generate supports for the models");
|
||||
def->sidetext = L("");
|
||||
def->cli = "";
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
|
@ -2549,7 +2561,6 @@ void PrintConfigDef::init_sla_params()
|
|||
def->tooltip = L("Merging bridges or pillars into another pillars can "
|
||||
"increase the radius. Zero means no increase, one means "
|
||||
"full increase.");
|
||||
def->sidetext = L("");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->max = 1;
|
||||
|
@ -2631,14 +2642,13 @@ void PrintConfigDef::init_sla_params()
|
|||
def->label = L("Use pad");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("Add a pad underneath the supported model");
|
||||
def->sidetext = L("");
|
||||
def->cli = "";
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
def = this->add("pad_wall_thickness", coFloat);
|
||||
def->label = L("Pad wall thickness");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("");
|
||||
// def->tooltip = L("");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
|
@ -2647,7 +2657,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def = this->add("pad_wall_height", coFloat);
|
||||
def->label = L("Pad wall height");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("");
|
||||
// def->tooltip = L("");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
|
@ -2656,7 +2666,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def = this->add("pad_max_merge_distance", coFloat);
|
||||
def->label = L("Max merge distance");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("");
|
||||
// def->tooltip = L("");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
|
@ -2665,7 +2675,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def = this->add("pad_edge_radius", coFloat);
|
||||
def->label = L("Pad edge radius");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("");
|
||||
// def->tooltip = L("");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
|
|
|
@ -38,8 +38,7 @@ namespace Slic3r {
|
|||
PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_instances) :
|
||||
PrintObjectBaseWithState(print, model_object),
|
||||
typed_slices(false),
|
||||
size(Vec3crd::Zero()),
|
||||
layer_height_profile_valid(false)
|
||||
size(Vec3crd::Zero())
|
||||
{
|
||||
// Compute the translation to be applied to our meshes so that we work with smaller coordinates
|
||||
{
|
||||
|
@ -65,8 +64,6 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta
|
|||
}
|
||||
this->set_copies(copies);
|
||||
}
|
||||
|
||||
this->layer_height_profile = model_object->layer_height_profile;
|
||||
}
|
||||
|
||||
PrintBase::ApplyStatus PrintObject::set_copies(const Points &points)
|
||||
|
@ -106,7 +103,10 @@ void PrintObject::slice()
|
|||
if (! this->set_started(posSlice))
|
||||
return;
|
||||
m_print->set_status(10, "Processing triangulated mesh");
|
||||
this->_slice();
|
||||
std::vector<coordf_t> layer_height_profile;
|
||||
this->update_layer_height_profile(*this->model_object(), this->slicing_parameters(), layer_height_profile);
|
||||
m_print->throw_if_canceled();
|
||||
this->_slice(layer_height_profile);
|
||||
m_print->throw_if_canceled();
|
||||
// Fix the model.
|
||||
//FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend.
|
||||
|
@ -455,7 +455,6 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
|||
|| opt_key == "first_layer_height"
|
||||
|| opt_key == "raft_layers") {
|
||||
steps.emplace_back(posSlice);
|
||||
this->reset_layer_height_profile();
|
||||
}
|
||||
else if (
|
||||
opt_key == "clip_multipart_objects"
|
||||
|
@ -542,7 +541,6 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
|||
} else {
|
||||
// for legacy, if we can't handle this option let's invalidate all steps
|
||||
this->invalidate_all_steps();
|
||||
this->reset_layer_height_profile();
|
||||
invalidated = true;
|
||||
}
|
||||
}
|
||||
|
@ -1329,55 +1327,107 @@ void PrintObject::bridge_over_infill()
|
|||
}
|
||||
}
|
||||
|
||||
static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders)
|
||||
{
|
||||
if (opt.value > (int)num_extruders)
|
||||
// assign the default extruder
|
||||
opt.value = 1;
|
||||
}
|
||||
|
||||
PrintObjectConfig PrintObject::object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders)
|
||||
{
|
||||
PrintObjectConfig config = default_object_config;
|
||||
normalize_and_apply_config(config, object.config);
|
||||
// Clamp invalid extruders to the default extruder (with index 1).
|
||||
clamp_exturder_to_default(config.support_material_extruder, num_extruders);
|
||||
clamp_exturder_to_default(config.support_material_interface_extruder, num_extruders);
|
||||
return config;
|
||||
}
|
||||
|
||||
PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders)
|
||||
{
|
||||
PrintRegionConfig config = default_region_config;
|
||||
normalize_and_apply_config(config, volume.get_object()->config);
|
||||
normalize_and_apply_config(config, volume.config);
|
||||
if (! volume.material_id().empty())
|
||||
normalize_and_apply_config(config, volume.material()->config);
|
||||
// Clamp invalid extruders to the default extruder (with index 1).
|
||||
clamp_exturder_to_default(config.infill_extruder, num_extruders);
|
||||
clamp_exturder_to_default(config.perimeter_extruder, num_extruders);
|
||||
clamp_exturder_to_default(config.solid_infill_extruder, num_extruders);
|
||||
return config;
|
||||
}
|
||||
|
||||
SlicingParameters PrintObject::slicing_parameters() const
|
||||
{
|
||||
return SlicingParameters::create_from_config(
|
||||
this->print()->config(), m_config,
|
||||
unscale<double>(this->size(2)), this->print()->object_extruders());
|
||||
unscale<double>(this->size(2)), this->object_extruders());
|
||||
}
|
||||
|
||||
bool PrintObject::update_layer_height_profile(std::vector<coordf_t> &layer_height_profile) const
|
||||
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object)
|
||||
{
|
||||
PrintConfig print_config;
|
||||
PrintObjectConfig object_config;
|
||||
PrintRegionConfig default_region_config;
|
||||
print_config .apply(full_config, true);
|
||||
object_config.apply(full_config, true);
|
||||
default_region_config.apply(full_config, true);
|
||||
size_t num_extruders = print_config.nozzle_diameter.size();
|
||||
object_config = object_config_from_model_object(object_config, model_object, num_extruders);
|
||||
|
||||
std::vector<unsigned int> object_extruders;
|
||||
for (const ModelVolume *model_volume : model_object.volumes)
|
||||
if (model_volume->is_model_part())
|
||||
PrintRegion::collect_object_printing_extruders(
|
||||
print_config,
|
||||
region_config_from_model_volume(default_region_config, *model_volume, num_extruders),
|
||||
object_extruders);
|
||||
sort_remove_duplicates(object_extruders);
|
||||
|
||||
return SlicingParameters::create_from_config(print_config, object_config, model_object.bounding_box().max.z(), object_extruders);
|
||||
}
|
||||
|
||||
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
|
||||
std::vector<unsigned int> PrintObject::object_extruders() const
|
||||
{
|
||||
std::vector<unsigned int> extruders;
|
||||
extruders.reserve(this->region_volumes.size() * 3);
|
||||
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region)
|
||||
if (! this->region_volumes[idx_region].empty())
|
||||
m_print->get_region(idx_region)->collect_object_printing_extruders(extruders);
|
||||
sort_remove_duplicates(extruders);
|
||||
return extruders;
|
||||
}
|
||||
|
||||
bool PrintObject::update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector<coordf_t> &layer_height_profile)
|
||||
{
|
||||
bool updated = false;
|
||||
|
||||
// If the layer height profile is not set, try to use the one stored at the ModelObject.
|
||||
if (layer_height_profile.empty()) {
|
||||
layer_height_profile = this->model_object()->layer_height_profile;
|
||||
layer_height_profile = model_object.layer_height_profile;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
// Verify the layer_height_profile.
|
||||
SlicingParameters slicing_params = this->slicing_parameters();
|
||||
if (! layer_height_profile.empty() &&
|
||||
// Must not be of even length.
|
||||
((layer_height_profile.size() & 1) != 0 ||
|
||||
// Last entry must be at the top of the object.
|
||||
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_height()) > 1e-3))
|
||||
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_height()) > 1e-3))
|
||||
layer_height_profile.clear();
|
||||
|
||||
if (layer_height_profile.empty()) {
|
||||
if (0)
|
||||
// if (this->layer_height_profile.empty())
|
||||
layer_height_profile = layer_height_profile_adaptive(slicing_params, this->model_object()->layer_height_ranges, this->model_object()->volumes);
|
||||
layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_height_ranges, model_object.volumes);
|
||||
else
|
||||
layer_height_profile = layer_height_profile_from_ranges(slicing_params, this->model_object()->layer_height_ranges);
|
||||
layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_height_ranges);
|
||||
updated = true;
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
// This must be called from the main thread as it modifies the layer_height_profile.
|
||||
bool PrintObject::update_layer_height_profile()
|
||||
{
|
||||
// If the layer height profile has been marked as invalid for some reason (modified at the UI level
|
||||
// or invalidated due to the slicing parameters), clear it now.
|
||||
if (! this->layer_height_profile_valid) {
|
||||
this->layer_height_profile.clear();
|
||||
this->layer_height_profile_valid = true;
|
||||
}
|
||||
return this->update_layer_height_profile(this->layer_height_profile);
|
||||
}
|
||||
|
||||
// 1) Decides Z positions of the layers,
|
||||
// 2) Initializes layers and their regions
|
||||
// 3) Slices the object meshes
|
||||
|
@ -1387,7 +1437,7 @@ bool PrintObject::update_layer_height_profile()
|
|||
// Resulting expolygons of layer regions are marked as Internal.
|
||||
//
|
||||
// this should be idempotent
|
||||
void PrintObject::_slice()
|
||||
void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info();
|
||||
|
||||
|
@ -1406,7 +1456,7 @@ void PrintObject::_slice()
|
|||
{
|
||||
this->clear_layers();
|
||||
// Object layers (pairs of bottom/top Z coordinate), without the raft.
|
||||
std::vector<coordf_t> object_layers = generate_object_layers(slicing_params, this->layer_height_profile);
|
||||
std::vector<coordf_t> object_layers = generate_object_layers(slicing_params, layer_height_profile);
|
||||
// Reserve object layers for the raft. Last layer of the raft is the contact layer.
|
||||
int id = int(slicing_params.raft_layers());
|
||||
slice_zs.reserve(object_layers.size());
|
||||
|
@ -2198,22 +2248,4 @@ void PrintObject::_generate_support_material()
|
|||
support_material.generate(*this);
|
||||
}
|
||||
|
||||
void PrintObject::reset_layer_height_profile()
|
||||
{
|
||||
// Reset the layer_heigth_profile.
|
||||
this->layer_height_profile.clear();
|
||||
this->layer_height_profile_valid = false;
|
||||
// Reset the source layer_height_profile if it exists at the ModelObject.
|
||||
this->model_object()->layer_height_profile.clear();
|
||||
this->model_object()->layer_height_profile_valid = false;
|
||||
}
|
||||
|
||||
void PrintObject::adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action)
|
||||
{
|
||||
update_layer_height_profile(m_model_object->layer_height_profile);
|
||||
Slic3r::adjust_layer_height_profile(slicing_parameters(), m_model_object->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action));
|
||||
m_model_object->layer_height_profile_valid = true;
|
||||
layer_height_profile_valid = false;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -61,4 +61,20 @@ coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const
|
|||
return this->nozzle_dmr_avg(print_config) * sqrt(m_config.bridge_flow_ratio.value);
|
||||
}
|
||||
|
||||
void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, std::vector<unsigned int> &object_extruders)
|
||||
{
|
||||
// These checks reflect the same logic used in the GUI for enabling/disabling extruder selection fields.
|
||||
if (region_config.perimeters.value > 0 || print_config.brim_width.value > 0)
|
||||
object_extruders.emplace_back(region_config.perimeter_extruder - 1);
|
||||
if (region_config.fill_density.value > 0)
|
||||
object_extruders.emplace_back(region_config.infill_extruder - 1);
|
||||
if (region_config.top_solid_layers.value > 0 || region_config.bottom_solid_layers.value > 0)
|
||||
object_extruders.emplace_back(region_config.solid_infill_extruder - 1);
|
||||
}
|
||||
|
||||
void PrintRegion::collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const
|
||||
{
|
||||
collect_object_printing_extruders(print()->config(), this->config(), object_extruders);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
#include <functional>
|
||||
#include <numeric>
|
||||
|
||||
#include "ExPolygon.hpp"
|
||||
#include "TriangleMesh.hpp"
|
||||
#include <libslic3r/ExPolygon.hpp>
|
||||
#include <libslic3r/TriangleMesh.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace sla {
|
||||
|
@ -53,7 +53,7 @@ struct Contour3D {
|
|||
|
||||
void merge(const Contour3D& ctr) {
|
||||
auto s3 = coord_t(points.size());
|
||||
auto s = coord_t(indices.size());
|
||||
auto s = indices.size();
|
||||
|
||||
points.insert(points.end(), ctr.points.begin(), ctr.points.end());
|
||||
indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end());
|
||||
|
@ -62,6 +62,17 @@ struct Contour3D {
|
|||
auto& idx = indices[n]; x(idx) += s3; y(idx) += s3; z(idx) += s3;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the index triangle structure to OBJ file for debugging purposes.
|
||||
void to_obj(std::ostream& stream) {
|
||||
for(auto& p : points) {
|
||||
stream << "v " << p.transpose() << "\n";
|
||||
}
|
||||
|
||||
for(auto& f : indices) {
|
||||
stream << "f " << (f + Vec3i(1, 1, 1)).transpose() << "\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//using PointSet = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::DontAlign>; //Eigen::MatrixXd;
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
#include "SLASpatIndex.hpp"
|
||||
#include "SLABasePool.hpp"
|
||||
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Model.hpp"
|
||||
#include <libslic3r/ClipperUtils.hpp>
|
||||
#include <libslic3r/Model.hpp>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
|
@ -164,7 +164,13 @@ Contour3D sphere(double rho, Portion portion = make_portion(0.0, 2.0*PI),
|
|||
return ret;
|
||||
}
|
||||
|
||||
Contour3D cylinder(double r, double h, size_t ssteps) {
|
||||
// Down facing cylinder in Z direction with arguments:
|
||||
// r: radius
|
||||
// h: Height
|
||||
// ssteps: how many edges will create the base circle
|
||||
// sp: starting point
|
||||
Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d sp = {0,0,0})
|
||||
{
|
||||
Contour3D ret;
|
||||
|
||||
auto steps = int(ssteps);
|
||||
|
@ -173,9 +179,10 @@ Contour3D cylinder(double r, double h, size_t ssteps) {
|
|||
points.reserve(2*ssteps);
|
||||
double a = 2*PI/steps;
|
||||
|
||||
Vec3d jp = {0, 0, 0};
|
||||
Vec3d endp = {0, 0, h};
|
||||
Vec3d jp = sp;
|
||||
Vec3d endp = {sp(X), sp(Y), sp(Z) + h};
|
||||
|
||||
// Upper circle points
|
||||
for(int i = 0; i < steps; ++i) {
|
||||
double phi = i*a;
|
||||
double ex = endp(X) + r*std::cos(phi);
|
||||
|
@ -183,6 +190,7 @@ Contour3D cylinder(double r, double h, size_t ssteps) {
|
|||
points.emplace_back(ex, ey, endp(Z));
|
||||
}
|
||||
|
||||
// Lower circle points
|
||||
for(int i = 0; i < steps; ++i) {
|
||||
double phi = i*a;
|
||||
double x = jp(X) + r*std::cos(phi);
|
||||
|
@ -190,6 +198,7 @@ Contour3D cylinder(double r, double h, size_t ssteps) {
|
|||
points.emplace_back(x, y, jp(Z));
|
||||
}
|
||||
|
||||
// Now create long triangles connecting upper and lower circles
|
||||
indices.reserve(2*ssteps);
|
||||
auto offs = steps;
|
||||
for(int i = 0; i < steps - 1; ++i) {
|
||||
|
@ -197,10 +206,26 @@ Contour3D cylinder(double r, double h, size_t ssteps) {
|
|||
indices.emplace_back(i, offs + i + 1, i + 1);
|
||||
}
|
||||
|
||||
// Last triangle connecting the first and last vertices
|
||||
auto last = steps - 1;
|
||||
indices.emplace_back(0, last, offs);
|
||||
indices.emplace_back(last, offs + last, offs);
|
||||
|
||||
// According to the slicing algorithms, we need to aid them with generating
|
||||
// a watertight body. So we create a triangle fan for the upper and lower
|
||||
// ending of the cylinder to close the geometry.
|
||||
points.emplace_back(jp); size_t ci = points.size() - 1;
|
||||
for(int i = 0; i < steps - 1; ++i)
|
||||
indices.emplace_back(i + offs + 1, i + offs, ci);
|
||||
|
||||
indices.emplace_back(offs, steps + offs - 1, ci);
|
||||
|
||||
points.emplace_back(endp); ci = points.size() - 1;
|
||||
for(int i = 0; i < steps - 1; ++i)
|
||||
indices.emplace_back(ci, i, i + 1);
|
||||
|
||||
indices.emplace_back(steps - 1, 0, ci);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -352,36 +377,15 @@ struct Pillar {
|
|||
r(radius), steps(st), endpoint(endp), starts_from_head(false)
|
||||
{
|
||||
assert(steps > 0);
|
||||
int steps_1 = int(steps - 1);
|
||||
|
||||
auto& points = mesh.points;
|
||||
auto& indices = mesh.indices;
|
||||
points.reserve(2*steps);
|
||||
double a = 2*PI/steps;
|
||||
double h = jp(Z) - endp(Z);
|
||||
assert(h > 0); // Endpoint is below the starting point
|
||||
|
||||
for(size_t i = 0; i < steps; ++i) {
|
||||
double phi = i*a;
|
||||
double x = jp(X) + r*std::cos(phi);
|
||||
double y = jp(Y) + r*std::sin(phi);
|
||||
points.emplace_back(x, y, jp(Z));
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < steps; ++i) {
|
||||
double phi = i*a;
|
||||
double ex = endp(X) + r*std::cos(phi);
|
||||
double ey = endp(Y) + r*std::sin(phi);
|
||||
points.emplace_back(ex, ey, endp(Z));
|
||||
}
|
||||
|
||||
indices.reserve(2*steps);
|
||||
int offs = int(steps);
|
||||
for(int i = 0; i < steps_1 ; ++i) {
|
||||
indices.emplace_back(i, i + offs, offs + i + 1);
|
||||
indices.emplace_back(i, offs + i + 1, i + 1);
|
||||
}
|
||||
|
||||
indices.emplace_back(0, steps_1, offs);
|
||||
indices.emplace_back(steps_1, offs + steps_1, offs);
|
||||
// We just create a bridge geometry with the pillar parameters and
|
||||
// move the data.
|
||||
Contour3D body = cylinder(radius, h, st, endp);
|
||||
mesh.points.swap(body.points);
|
||||
mesh.indices.swap(body.indices);
|
||||
}
|
||||
|
||||
Pillar(const Junction& junc, const Vec3d& endp):
|
||||
|
@ -826,45 +830,46 @@ public:
|
|||
const TriangleMesh& merged_mesh() const {
|
||||
if(meshcache_valid) return meshcache;
|
||||
|
||||
meshcache = TriangleMesh();
|
||||
Contour3D merged;
|
||||
|
||||
for(auto& head : heads()) {
|
||||
if(m_ctl.stopcondition()) break;
|
||||
if(head.is_valid()) {
|
||||
auto&& m = mesh(head.mesh);
|
||||
meshcache.merge(m);
|
||||
}
|
||||
if(head.is_valid())
|
||||
merged.merge(head.mesh);
|
||||
}
|
||||
|
||||
for(auto& stick : pillars()) {
|
||||
if(m_ctl.stopcondition()) break;
|
||||
meshcache.merge(mesh(stick.mesh));
|
||||
meshcache.merge(mesh(stick.base));
|
||||
merged.merge(stick.mesh);
|
||||
merged.merge(stick.base);
|
||||
}
|
||||
|
||||
for(auto& j : junctions()) {
|
||||
if(m_ctl.stopcondition()) break;
|
||||
meshcache.merge(mesh(j.mesh));
|
||||
merged.merge(j.mesh);
|
||||
}
|
||||
|
||||
for(auto& cb : compact_bridges()) {
|
||||
if(m_ctl.stopcondition()) break;
|
||||
meshcache.merge(mesh(cb.mesh));
|
||||
merged.merge(cb.mesh);
|
||||
}
|
||||
|
||||
for(auto& bs : bridges()) {
|
||||
if(m_ctl.stopcondition()) break;
|
||||
meshcache.merge(mesh(bs.mesh));
|
||||
merged.merge(bs.mesh);
|
||||
}
|
||||
|
||||
|
||||
if(m_ctl.stopcondition()) {
|
||||
// In case of failure we have to return an empty mesh
|
||||
meshcache = TriangleMesh();
|
||||
return meshcache;
|
||||
}
|
||||
|
||||
meshcache = mesh(merged);
|
||||
|
||||
// TODO: Is this necessary?
|
||||
meshcache.repair();
|
||||
//meshcache.repair();
|
||||
|
||||
BoundingBoxf3&& bb = meshcache.bounding_box();
|
||||
model_height = bb.max(Z) - bb.min(Z);
|
||||
|
@ -1555,7 +1560,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||
}
|
||||
|
||||
double d = distance(jp, jn);
|
||||
if(jn(Z) <= gndlvl + nearhead.r_back_mm || d > max_len) break;
|
||||
break;
|
||||
|
||||
double chkd = bridge_mesh_intersect(jp, dirv(jp, jn),
|
||||
pradius,
|
||||
|
|
|
@ -440,10 +440,6 @@ std::vector<float> SLAPrint::calculate_heights(const BoundingBoxf3& bb3d,
|
|||
auto flh = float(layer_height);
|
||||
auto gnd = float(bb3d.min(Z));
|
||||
|
||||
// The first layer (the one before the initial height) is added only
|
||||
// if there is no pad and no elevation value
|
||||
if(minZ >= gnd) heights.emplace_back(minZ);
|
||||
|
||||
for(float h = minZ + initial_layer_height; h < maxZ; h += flh)
|
||||
if(h >= gnd) heights.emplace_back(h);
|
||||
|
||||
|
@ -510,6 +506,9 @@ void SLAPrint::process()
|
|||
po.m_supportdata.reset(new SLAPrintObject::SupportData());
|
||||
po.m_supportdata->emesh = EigenMesh3D(po.transformed_mesh());
|
||||
|
||||
// If supports are disabled, we can skip the model scan.
|
||||
if(!po.m_config.supports_enable.getBool()) return;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Support point count "
|
||||
<< mo.sla_support_points.size();
|
||||
|
||||
|
@ -693,7 +692,6 @@ void SLAPrint::process()
|
|||
// model_slice method. Only difference is that here it works with
|
||||
// scaled coordinates
|
||||
po.m_level_ids.clear();
|
||||
if(sminZ >= smodelgnd) po.m_level_ids.emplace_back(sminZ);
|
||||
for(LevelID h = sminZ + sih; h < smaxZ; h += slh)
|
||||
if(h >= smodelgnd) po.m_level_ids.emplace_back(h);
|
||||
|
||||
|
|
|
@ -3,11 +3,14 @@
|
|||
#ifndef slic3r_Slicing_hpp_
|
||||
#define slic3r_Slicing_hpp_
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Utils.hpp"
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
|
@ -91,6 +94,8 @@ struct SlicingParameters
|
|||
coordf_t object_print_z_min;
|
||||
coordf_t object_print_z_max;
|
||||
};
|
||||
static_assert(IsTriviallyCopyable<SlicingParameters>::value, "SlicingParameters class is not POD (and it should be - see constructor).");
|
||||
|
||||
|
||||
// The two slicing parameters lead to the same layering as long as the variable layer thickness is not in action.
|
||||
inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters &sp2)
|
||||
|
@ -131,7 +136,7 @@ extern std::vector<coordf_t> layer_height_profile_adaptive(
|
|||
const ModelVolumePtrs &volumes);
|
||||
|
||||
|
||||
enum LayerHeightEditActionType {
|
||||
enum LayerHeightEditActionType : unsigned int {
|
||||
LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0,
|
||||
LAYER_HEIGHT_EDIT_ACTION_DECREASE = 1,
|
||||
LAYER_HEIGHT_EDIT_ACTION_REDUCE = 2,
|
||||
|
|
|
@ -9,20 +9,16 @@
|
|||
#define ENABLE_SHOW_CAMERA_TARGET 0
|
||||
// Log debug messages to console when changing selection
|
||||
#define ENABLE_SELECTION_DEBUG_OUTPUT 0
|
||||
// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active
|
||||
#define ENABLE_RENDER_SELECTION_CENTER 0
|
||||
|
||||
//====================
|
||||
// 1.42.0.alpha1 techs
|
||||
//====================
|
||||
#define ENABLE_1_42_0_ALPHA1 1
|
||||
|
||||
// Uses a unique opengl context
|
||||
#define ENABLE_USE_UNIQUE_GLCONTEXT (1 && ENABLE_1_42_0_ALPHA1)
|
||||
// Disable synchronization of unselected instances
|
||||
#define DISABLE_INSTANCES_SYNCH (0 && ENABLE_1_42_0_ALPHA1)
|
||||
// Keeps objects on bed while scaling them using the scale gizmo
|
||||
#define ENABLE_ENSURE_ON_BED_WHILE_SCALING (1 && ENABLE_1_42_0_ALPHA1)
|
||||
// All rotations made using the rotate gizmo are done with respect to the world reference system
|
||||
#define ENABLE_WORLD_ROTATIONS (1 && ENABLE_1_42_0_ALPHA1)
|
||||
// Scene's GUI made using imgui library
|
||||
#define ENABLE_IMGUI (1 && ENABLE_1_42_0_ALPHA1)
|
||||
#define DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI (1 && ENABLE_IMGUI)
|
||||
|
@ -30,12 +26,6 @@
|
|||
#define ENABLE_SLA_SUPPORT_GIZMO_MOD (1 && ENABLE_1_42_0_ALPHA1)
|
||||
// Use wxDataViewRender instead of wxDataViewCustomRenderer
|
||||
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1)
|
||||
// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active
|
||||
#define ENABLE_RENDER_SELECTION_CENTER (0 && ENABLE_1_42_0_ALPHA1)
|
||||
// Show visual hints in the 3D scene when sidebar matrix fields have focus
|
||||
#define ENABLE_SIDEBAR_VISUAL_HINTS (1 && ENABLE_1_42_0_ALPHA1)
|
||||
// Separate rendering for opaque and transparent volumes
|
||||
#define ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_1_42_0_ALPHA1)
|
||||
|
||||
//====================
|
||||
// 1.42.0.alpha2 techs
|
||||
|
@ -45,7 +35,7 @@
|
|||
// Improves navigation between sidebar fields
|
||||
#define ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION (1 && ENABLE_1_42_0_ALPHA2)
|
||||
// Adds print bed models to 3D scene
|
||||
#define ENABLE_PRINT_BED_MODELS (0 && ENABLE_1_42_0_ALPHA2)
|
||||
#define ENABLE_PRINT_BED_MODELS (1 && ENABLE_1_42_0_ALPHA2)
|
||||
#endif // _technologies_h_
|
||||
|
||||
|
||||
|
@ -62,3 +52,7 @@
|
|||
#define ENABLE_GENERIC_SUBPARTS_PLACEMENT (1 && ENABLE_1_42_0_ALPHA4)
|
||||
// Reworked management of bed shape changes
|
||||
#define ENABLE_REWORKED_BED_SHAPE_CHANGE (1 && ENABLE_1_42_0_ALPHA4)
|
||||
// Use anisotropic filtering on bed plate texture
|
||||
#define ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES (1 && ENABLE_1_42_0_ALPHA4)
|
||||
// Bunch of fixes related to volumes centering
|
||||
#define ENABLE_VOLUMES_CENTERING_FIXES (1 && ENABLE_1_42_0_ALPHA4)
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd>& facets )
|
||||
TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd>& facets)
|
||||
: repaired(false)
|
||||
{
|
||||
stl_initialize(&this->stl);
|
||||
|
@ -99,6 +99,8 @@ TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other)
|
|||
return *this;
|
||||
}
|
||||
|
||||
// #define SLIC3R_TRACE_REPAIR
|
||||
|
||||
void TriangleMesh::repair()
|
||||
{
|
||||
if (this->repaired) return;
|
||||
|
@ -109,7 +111,9 @@ void TriangleMesh::repair()
|
|||
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() started";
|
||||
|
||||
// checking exact
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_check_facets_exact(&stl);
|
||||
stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge);
|
||||
stl.stats.facets_w_2_bad_edge = (stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge);
|
||||
|
@ -124,7 +128,9 @@ void TriangleMesh::repair()
|
|||
for (int i = 0; i < iterations; i++) {
|
||||
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
||||
//printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations);
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_nearby";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_check_facets_nearby(&stl, tolerance);
|
||||
//printf(" Fixed %d edges.\n", stl.stats.edges_fixed - last_edges_fixed);
|
||||
//last_edges_fixed = stl.stats.edges_fixed;
|
||||
|
@ -137,7 +143,9 @@ void TriangleMesh::repair()
|
|||
|
||||
// remove_unconnected
|
||||
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_remove_unconnected_facets(&stl);
|
||||
}
|
||||
|
||||
|
@ -146,26 +154,36 @@ void TriangleMesh::repair()
|
|||
// Don't fill holes, the current algorithm does more harm than good on complex holes.
|
||||
// Rather let the slicing algorithm close gaps in 2D slices.
|
||||
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_fill_holes";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_fill_holes(&stl);
|
||||
stl_clear_error(&stl);
|
||||
}
|
||||
#endif
|
||||
|
||||
// normal_directions
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_directions";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_fix_normal_directions(&stl);
|
||||
|
||||
// normal_values
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_values";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_fix_normal_values(&stl);
|
||||
|
||||
// always calculate the volume and reverse all normals if volume is negative
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_calculate_volume";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_calculate_volume(&stl);
|
||||
|
||||
// neighbors
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_verify_neighbors";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_verify_neighbors(&stl);
|
||||
|
||||
this->repaired = true;
|
||||
|
@ -507,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
|
||||
|
@ -1992,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();
|
||||
|
|
|
@ -159,6 +159,16 @@ template<class T> size_t next_highest_power_of_2(T v,
|
|||
extern std::string xml_escape(std::string text);
|
||||
|
||||
|
||||
#if defined __GNUC__ & __GNUC__ < 5
|
||||
// Older GCCs don't have std::is_trivially_copyable
|
||||
// cf. https://gcc.gnu.org/onlinedocs/gcc-4.9.4/libstdc++/manual/manual/status.html#status.iso.2011
|
||||
#warning "GCC version < 5, faking std::is_trivially_copyable"
|
||||
template<typename T> struct IsTriviallyCopyable { static constexpr bool value = true; };
|
||||
#else
|
||||
template<typename T> struct IsTriviallyCopyable : public std::is_trivially_copyable<T> {};
|
||||
#endif
|
||||
|
||||
|
||||
class ScopeGuard
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||
* https://github.com/jhasse/poly2tri
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
|
@ -29,10 +29,16 @@
|
|||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "shapes.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
namespace p2t {
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const Point& point) {
|
||||
return out << point.x << "," << point.y;
|
||||
}
|
||||
|
||||
Triangle::Triangle(Point& a, Point& b, Point& c)
|
||||
{
|
||||
points_[0] = &a; points_[1] = &b; points_[2] = &c;
|
||||
|
@ -150,7 +156,7 @@ void Triangle::Legalize(Point& opoint, Point& npoint)
|
|||
}
|
||||
}
|
||||
|
||||
int Triangle::Index(const Point* p) const
|
||||
int Triangle::Index(const Point* p)
|
||||
{
|
||||
if (p == points_[0]) {
|
||||
return 0;
|
||||
|
@ -163,7 +169,7 @@ int Triangle::Index(const Point* p) const
|
|||
return -1;
|
||||
}
|
||||
|
||||
int Triangle::EdgeIndex(const Point* p1, const Point* p2) const
|
||||
int Triangle::EdgeIndex(const Point* p1, const Point* p2)
|
||||
{
|
||||
if (points_[0] == p1) {
|
||||
if (points_[1] == p2) {
|
||||
|
@ -259,7 +265,7 @@ Triangle* Triangle::NeighborCCW(const Point& point)
|
|||
return neighbors_[1];
|
||||
}
|
||||
|
||||
bool Triangle::GetConstrainedEdgeCCW(const Point& p) const
|
||||
bool Triangle::GetConstrainedEdgeCCW(const Point& p)
|
||||
{
|
||||
if (&p == points_[0]) {
|
||||
return constrained_edge[2];
|
||||
|
@ -269,7 +275,7 @@ bool Triangle::GetConstrainedEdgeCCW(const Point& p) const
|
|||
return constrained_edge[1];
|
||||
}
|
||||
|
||||
bool Triangle::GetConstrainedEdgeCW(const Point& p) const
|
||||
bool Triangle::GetConstrainedEdgeCW(const Point& p)
|
||||
{
|
||||
if (&p == points_[0]) {
|
||||
return constrained_edge[1];
|
||||
|
@ -301,7 +307,7 @@ void Triangle::SetConstrainedEdgeCW(const Point& p, bool ce)
|
|||
}
|
||||
}
|
||||
|
||||
bool Triangle::GetDelunayEdgeCCW(const Point& p) const
|
||||
bool Triangle::GetDelunayEdgeCCW(const Point& p)
|
||||
{
|
||||
if (&p == points_[0]) {
|
||||
return delaunay_edge[2];
|
||||
|
@ -311,7 +317,7 @@ bool Triangle::GetDelunayEdgeCCW(const Point& p) const
|
|||
return delaunay_edge[1];
|
||||
}
|
||||
|
||||
bool Triangle::GetDelunayEdgeCW(const Point& p) const
|
||||
bool Triangle::GetDelunayEdgeCW(const Point& p)
|
||||
{
|
||||
if (&p == points_[0]) {
|
||||
return delaunay_edge[1];
|
||||
|
@ -356,10 +362,7 @@ Triangle& Triangle::NeighborAcross(const Point& opoint)
|
|||
|
||||
void Triangle::DebugPrint()
|
||||
{
|
||||
using namespace std;
|
||||
cout << points_[0]->x << "," << points_[0]->y << " ";
|
||||
cout << points_[1]->x << "," << points_[1]->y << " ";
|
||||
cout << points_[2]->x << "," << points_[2]->y << endl;
|
||||
std::cout << *points_[0] << " " << *points_[1] << " " << *points_[2] << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||
* https://github.com/jhasse/poly2tri
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
|
@ -33,10 +33,10 @@
|
|||
#ifndef SHAPES_H
|
||||
#define SHAPES_H
|
||||
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <assert.h>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace p2t {
|
||||
|
||||
|
@ -119,6 +119,8 @@ struct Point {
|
|||
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const Point&);
|
||||
|
||||
// Represents a simple polygon's edge
|
||||
struct Edge {
|
||||
|
||||
|
@ -130,13 +132,13 @@ struct Edge {
|
|||
if (p1.y > p2.y) {
|
||||
q = &p1;
|
||||
p = &p2;
|
||||
} else if (p1.y == p2.y) {
|
||||
} else if (std::abs(p1.y - p2.y) < 1e-10) {
|
||||
if (p1.x > p2.x) {
|
||||
q = &p1;
|
||||
p = &p2;
|
||||
} else if (p1.x == p2.x) {
|
||||
} else if (std::abs(p1.x - p2.x) < 1e-10) {
|
||||
// Repeat points
|
||||
assert(false);
|
||||
throw std::runtime_error("Edge::Edge: p1 == p2");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,23 +173,23 @@ void MarkConstrainedEdge(int index);
|
|||
void MarkConstrainedEdge(Edge& edge);
|
||||
void MarkConstrainedEdge(Point* p, Point* q);
|
||||
|
||||
int Index(const Point* p) const;
|
||||
int EdgeIndex(const Point* p1, const Point* p2) const;
|
||||
int Index(const Point* p);
|
||||
int EdgeIndex(const Point* p1, const Point* p2);
|
||||
|
||||
Triangle* NeighborCW(const Point& point);
|
||||
Triangle* NeighborCCW(const Point& point);
|
||||
bool GetConstrainedEdgeCCW(const Point& p) const;
|
||||
bool GetConstrainedEdgeCW(const Point& p) const;
|
||||
bool GetConstrainedEdgeCCW(const Point& p);
|
||||
bool GetConstrainedEdgeCW(const Point& p);
|
||||
void SetConstrainedEdgeCCW(const Point& p, bool ce);
|
||||
void SetConstrainedEdgeCW(const Point& p, bool ce);
|
||||
bool GetDelunayEdgeCCW(const Point& p) const;
|
||||
bool GetDelunayEdgeCW(const Point& p) const;
|
||||
bool GetDelunayEdgeCCW(const Point& p);
|
||||
bool GetDelunayEdgeCW(const Point& p);
|
||||
void SetDelunayEdgeCCW(const Point& p, bool e);
|
||||
void SetDelunayEdgeCW(const Point& p, bool e);
|
||||
|
||||
bool Contains(const Point* p) const;
|
||||
bool Contains(const Edge& e) const;
|
||||
bool Contains(const Point* p, const Point* q) const;
|
||||
bool Contains(const Point* p);
|
||||
bool Contains(const Edge& e);
|
||||
bool Contains(const Point* p, const Point* q);
|
||||
void Legalize(Point& point);
|
||||
void Legalize(Point& opoint, Point& npoint);
|
||||
/**
|
||||
|
@ -198,7 +200,7 @@ void ClearNeighbor(const Triangle *triangle);
|
|||
void ClearNeighbors();
|
||||
void ClearDelunayEdges();
|
||||
|
||||
inline bool IsInterior() const;
|
||||
inline bool IsInterior();
|
||||
inline void IsInterior(bool b);
|
||||
|
||||
Triangle& NeighborAcross(const Point& opoint);
|
||||
|
@ -293,22 +295,22 @@ inline Triangle* Triangle::GetNeighbor(int index)
|
|||
return neighbors_[index];
|
||||
}
|
||||
|
||||
inline bool Triangle::Contains(const Point* p) const
|
||||
inline bool Triangle::Contains(const Point* p)
|
||||
{
|
||||
return p == points_[0] || p == points_[1] || p == points_[2];
|
||||
}
|
||||
|
||||
inline bool Triangle::Contains(const Edge& e) const
|
||||
inline bool Triangle::Contains(const Edge& e)
|
||||
{
|
||||
return Contains(e.p) && Contains(e.q);
|
||||
}
|
||||
|
||||
inline bool Triangle::Contains(const Point* p, const Point* q) const
|
||||
inline bool Triangle::Contains(const Point* p, const Point* q)
|
||||
{
|
||||
return Contains(p) && Contains(q);
|
||||
}
|
||||
|
||||
inline bool Triangle::IsInterior() const
|
||||
inline bool Triangle::IsInterior()
|
||||
{
|
||||
return interior_;
|
||||
}
|
||||
|
@ -320,4 +322,4 @@ inline void Triangle::IsInterior(bool b)
|
|||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||
* https://github.com/jhasse/poly2tri
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
|
@ -34,11 +34,18 @@
|
|||
|
||||
// Otherwise #defines like M_PI are undeclared under Visual Studio
|
||||
#ifndef _USE_MATH_DEFINES
|
||||
#define _USE_MATH_DEFINES
|
||||
#define _USE_MATH_DEFINES
|
||||
#endif /* _USE_MATH_DEFINES */
|
||||
|
||||
#include "shapes.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
#include <math.h>
|
||||
|
||||
// C99 removes M_PI from math.h
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846264338327
|
||||
#endif
|
||||
|
||||
namespace p2t {
|
||||
|
||||
|
@ -121,4 +128,4 @@ bool InScanArea(const Point& pa, const Point& pb, const Point& pc, const Point&
|
|||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||
* https://github.com/jhasse/poly2tri
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
|
@ -35,4 +35,4 @@
|
|||
#include "common/shapes.h"
|
||||
#include "sweep/cdt.h"
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||
* https://github.com/jhasse/poly2tri
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
|
@ -30,6 +30,8 @@
|
|||
*/
|
||||
#include "advancing_front.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace p2t {
|
||||
|
||||
AdvancingFront::AdvancingFront(Node& head, Node& tail)
|
||||
|
@ -105,4 +107,4 @@ AdvancingFront::~AdvancingFront()
|
|||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||
* https://github.com/jhasse/poly2tri
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
|
@ -115,4 +115,4 @@ inline void AdvancingFront::set_search(Node* node)
|
|||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||
* https://github.com/jhasse/poly2tri
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
|
@ -68,4 +68,4 @@ CDT::~CDT()
|
|||
delete sweep_;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||
* https://github.com/jhasse/poly2tri
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
|
@ -102,4 +102,4 @@ public:
|
|||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||
* https://github.com/jhasse/poly2tri
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
|
@ -28,19 +28,21 @@
|
|||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdexcept>
|
||||
#include "sweep.h"
|
||||
#include "sweep_context.h"
|
||||
#include "advancing_front.h"
|
||||
#include "../common/utils.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace p2t {
|
||||
|
||||
// Triangulate simple polygon with holes
|
||||
void Sweep::Triangulate(SweepContext& tcx)
|
||||
{
|
||||
tcx.InitTriangulation();
|
||||
tcx.CreateAdvancingFront(nodes_);
|
||||
tcx.CreateAdvancingFront();
|
||||
// Sweep points; build mesh
|
||||
SweepPoints(tcx);
|
||||
// Clean up
|
||||
|
@ -699,13 +701,6 @@ void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t,
|
|||
Triangle& ot = t->NeighborAcross(p);
|
||||
Point& op = *ot.OppositePoint(*t, p);
|
||||
|
||||
if (&ot == NULL) {
|
||||
// If we want to integrate the fillEdgeEvent do it here
|
||||
// With current implementation we should never get here
|
||||
//throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) {
|
||||
// Lets rotate shared edge one vertex CW
|
||||
RotateTrianglePair(*t, p, ot, op);
|
||||
|
@ -772,13 +767,6 @@ void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle&
|
|||
Triangle& ot = t.NeighborAcross(p);
|
||||
Point& op = *ot.OppositePoint(t, p);
|
||||
|
||||
if (&t.NeighborAcross(p) == NULL) {
|
||||
// If we want to integrate the fillEdgeEvent do it here
|
||||
// With current implementation we should never get here
|
||||
//throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) {
|
||||
// flip with new edge op->eq
|
||||
FlipEdgeEvent(tcx, eq, op, &ot, op);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||
* https://github.com/jhasse/poly2tri
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
|
@ -282,4 +282,4 @@ private:
|
|||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||
* https://github.com/jhasse/poly2tri
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
|
@ -120,10 +120,9 @@ Node& SweepContext::LocateNode(const Point& point)
|
|||
return *front_->LocateNode(point.x);
|
||||
}
|
||||
|
||||
void SweepContext::CreateAdvancingFront(const std::vector<Node*>& nodes)
|
||||
void SweepContext::CreateAdvancingFront()
|
||||
{
|
||||
|
||||
(void) nodes;
|
||||
// Initial triangle
|
||||
Triangle* triangle = new Triangle(*points_[0], *tail_, *head_);
|
||||
|
||||
|
@ -169,8 +168,8 @@ void SweepContext::MeshClean(Triangle& triangle)
|
|||
triangles.push_back(&triangle);
|
||||
|
||||
while(!triangles.empty()){
|
||||
Triangle *t = triangles.back();
|
||||
triangles.pop_back();
|
||||
Triangle *t = triangles.back();
|
||||
triangles.pop_back();
|
||||
|
||||
if (t != NULL && !t->IsInterior()) {
|
||||
t->IsInterior(true);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
||||
* http://code.google.com/p/poly2tri/
|
||||
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||
* https://github.com/jhasse/poly2tri
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
|
@ -70,7 +70,7 @@ Node& LocateNode(const Point& point);
|
|||
|
||||
void RemoveNode(Node* node);
|
||||
|
||||
void CreateAdvancingFront(const std::vector<Node*>& nodes);
|
||||
void CreateAdvancingFront();
|
||||
|
||||
/// Try to map a node to all sides of this triangle that don't have a neighbor
|
||||
void MapTriangleToNodes(Triangle& t);
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include <list>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -66,21 +68,26 @@ void TPPLPoly::Triangle(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3) {
|
|||
points[2] = p3;
|
||||
}
|
||||
|
||||
TPPLPoly::TPPLPoly(const TPPLPoly &src) {
|
||||
TPPLPoly::TPPLPoly(const TPPLPoly &src) : TPPLPoly() {
|
||||
hole = src.hole;
|
||||
numpoints = src.numpoints;
|
||||
points = new TPPLPoint[numpoints];
|
||||
memcpy(points, src.points, numpoints*sizeof(TPPLPoint));
|
||||
}
|
||||
|
||||
TPPLPoly& TPPLPoly::operator=(const TPPLPoly &src) {
|
||||
if(&src != this) {
|
||||
Clear();
|
||||
hole = src.hole;
|
||||
numpoints = src.numpoints;
|
||||
if(numpoints > 0) {
|
||||
points = new TPPLPoint[numpoints];
|
||||
memcpy(points, src.points, numpoints*sizeof(TPPLPoint));
|
||||
}
|
||||
}
|
||||
|
||||
TPPLPoly& TPPLPoly::operator=(const TPPLPoly &src) {
|
||||
Clear();
|
||||
hole = src.hole;
|
||||
numpoints = src.numpoints;
|
||||
|
||||
if(numpoints > 0) {
|
||||
points = new TPPLPoint[numpoints];
|
||||
memcpy(points, src.points, numpoints*sizeof(TPPLPoint));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -105,16 +112,11 @@ void TPPLPoly::SetOrientation(int orientation) {
|
|||
}
|
||||
|
||||
void TPPLPoly::Invert() {
|
||||
long i;
|
||||
TPPLPoint *invpoints;
|
||||
std::reverse(points, points + numpoints);
|
||||
}
|
||||
|
||||
invpoints = new TPPLPoint[numpoints];
|
||||
for(i=0;i<numpoints;i++) {
|
||||
invpoints[i] = points[numpoints-i-1];
|
||||
}
|
||||
TPPLPartition::PartitionVertex::PartitionVertex() : previous(NULL), next(NULL) {
|
||||
|
||||
delete [] points;
|
||||
points = invpoints;
|
||||
}
|
||||
|
||||
TPPLPoint TPPLPartition::Normalize(const TPPLPoint &p) {
|
||||
|
@ -169,10 +171,10 @@ int TPPLPartition::Intersects(TPPLPoint &p11, TPPLPoint &p12, TPPLPoint &p21, TP
|
|||
}
|
||||
|
||||
//removes holes from inpolys by merging them with non-holes
|
||||
int TPPLPartition::RemoveHoles(list<TPPLPoly> *inpolys, list<TPPLPoly> *outpolys) {
|
||||
list<TPPLPoly> polys;
|
||||
list<TPPLPoly>::iterator holeiter,polyiter,iter,iter2;
|
||||
long i,i2,holepointindex,polypointindex = 0;
|
||||
int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
|
||||
TPPLPolyList polys;
|
||||
TPPLPolyList::iterator holeiter,polyiter,iter,iter2;
|
||||
long i,i2,holepointindex,polypointindex;
|
||||
TPPLPoint holepoint,polypoint,bestpolypoint;
|
||||
TPPLPoint linep1,linep2;
|
||||
TPPLPoint v1,v2;
|
||||
|
@ -183,14 +185,14 @@ int TPPLPartition::RemoveHoles(list<TPPLPoly> *inpolys, list<TPPLPoly> *outpolys
|
|||
|
||||
//check for trivial case (no holes)
|
||||
hasholes = false;
|
||||
for(iter = inpolys->begin(); iter!=inpolys->end(); ++iter) {
|
||||
for(iter = inpolys->begin(); iter!=inpolys->end(); iter++) {
|
||||
if(iter->IsHole()) {
|
||||
hasholes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!hasholes) {
|
||||
for(iter = inpolys->begin(); iter!=inpolys->end(); ++iter) {
|
||||
for(iter = inpolys->begin(); iter!=inpolys->end(); iter++) {
|
||||
outpolys->push_back(*iter);
|
||||
}
|
||||
return 1;
|
||||
|
@ -201,7 +203,7 @@ int TPPLPartition::RemoveHoles(list<TPPLPoly> *inpolys, list<TPPLPoly> *outpolys
|
|||
while(1) {
|
||||
//find the hole point with the largest x
|
||||
hasholes = false;
|
||||
for(iter = polys.begin(); iter!=polys.end(); ++iter) {
|
||||
for(iter = polys.begin(); iter!=polys.end(); iter++) {
|
||||
if(!iter->IsHole()) continue;
|
||||
|
||||
if(!hasholes) {
|
||||
|
@ -221,7 +223,7 @@ int TPPLPartition::RemoveHoles(list<TPPLPoly> *inpolys, list<TPPLPoly> *outpolys
|
|||
holepoint = holeiter->GetPoint(holepointindex);
|
||||
|
||||
pointfound = false;
|
||||
for(iter = polys.begin(); iter!=polys.end(); ++iter) {
|
||||
for(iter = polys.begin(); iter!=polys.end(); iter++) {
|
||||
if(iter->IsHole()) continue;
|
||||
for(i=0; i < iter->GetNumPoints(); i++) {
|
||||
if(iter->GetPoint(i).x <= holepoint.x) continue;
|
||||
|
@ -237,7 +239,7 @@ int TPPLPartition::RemoveHoles(list<TPPLPoly> *inpolys, list<TPPLPoly> *outpolys
|
|||
if(v2.x > v1.x) continue;
|
||||
}
|
||||
pointvisible = true;
|
||||
for(iter2 = polys.begin(); iter2!=polys.end(); ++iter2) {
|
||||
for(iter2 = polys.begin(); iter2!=polys.end(); iter2++) {
|
||||
if(iter2->IsHole()) continue;
|
||||
for(i2=0; i2 < iter2->GetNumPoints(); i2++) {
|
||||
linep1 = iter2->GetPoint(i2);
|
||||
|
@ -280,7 +282,7 @@ int TPPLPartition::RemoveHoles(list<TPPLPoly> *inpolys, list<TPPLPoly> *outpolys
|
|||
polys.push_back(newpoly);
|
||||
}
|
||||
|
||||
for(iter = polys.begin(); iter!=polys.end(); ++iter) {
|
||||
for(iter = polys.begin(); iter!=polys.end(); iter++) {
|
||||
outpolys->push_back(*iter);
|
||||
}
|
||||
|
||||
|
@ -335,7 +337,7 @@ bool TPPLPartition::InCone(PartitionVertex *v, TPPLPoint &p) {
|
|||
}
|
||||
|
||||
void TPPLPartition::UpdateVertexReflexity(PartitionVertex *v) {
|
||||
PartitionVertex *v1,*v3;
|
||||
PartitionVertex *v1 = NULL,*v3 = NULL;
|
||||
v1 = v->previous;
|
||||
v3 = v->next;
|
||||
v->isConvex = !IsReflex(v1->p,v->p,v3->p);
|
||||
|
@ -343,7 +345,7 @@ void TPPLPartition::UpdateVertexReflexity(PartitionVertex *v) {
|
|||
|
||||
void TPPLPartition::UpdateVertex(PartitionVertex *v, PartitionVertex *vertices, long numvertices) {
|
||||
long i;
|
||||
PartitionVertex *v1,*v3;
|
||||
PartitionVertex *v1 = NULL,*v3 = NULL;
|
||||
TPPLPoint vec1,vec3;
|
||||
|
||||
v1 = v->previous;
|
||||
|
@ -372,10 +374,12 @@ void TPPLPartition::UpdateVertex(PartitionVertex *v, PartitionVertex *vertices,
|
|||
}
|
||||
|
||||
//triangulation by ear removal
|
||||
int TPPLPartition::Triangulate_EC(TPPLPoly *poly, list<TPPLPoly> *triangles) {
|
||||
int TPPLPartition::Triangulate_EC(TPPLPoly *poly, TPPLPolyList *triangles) {
|
||||
if(!poly->Valid()) return 0;
|
||||
|
||||
long numvertices;
|
||||
PartitionVertex *vertices;
|
||||
PartitionVertex *ear;
|
||||
PartitionVertex *vertices = NULL;
|
||||
PartitionVertex *ear = NULL;
|
||||
TPPLPoly triangle;
|
||||
long i,j;
|
||||
bool earfound;
|
||||
|
@ -446,21 +450,23 @@ int TPPLPartition::Triangulate_EC(TPPLPoly *poly, list<TPPLPoly> *triangles) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
int TPPLPartition::Triangulate_EC(list<TPPLPoly> *inpolys, list<TPPLPoly> *triangles) {
|
||||
list<TPPLPoly> outpolys;
|
||||
list<TPPLPoly>::iterator iter;
|
||||
int TPPLPartition::Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles) {
|
||||
TPPLPolyList outpolys;
|
||||
TPPLPolyList::iterator iter;
|
||||
|
||||
if(!RemoveHoles(inpolys,&outpolys)) return 0;
|
||||
for(iter=outpolys.begin();iter!=outpolys.end();++iter) {
|
||||
for(iter=outpolys.begin();iter!=outpolys.end();iter++) {
|
||||
if(!Triangulate_EC(&(*iter),triangles)) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
||||
list<TPPLPoly> triangles;
|
||||
list<TPPLPoly>::iterator iter1,iter2;
|
||||
TPPLPoly *poly1,*poly2;
|
||||
int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) {
|
||||
if(!poly->Valid()) return 0;
|
||||
|
||||
TPPLPolyList triangles;
|
||||
TPPLPolyList::iterator iter1,iter2;
|
||||
TPPLPoly *poly1 = NULL,*poly2 = NULL;
|
||||
TPPLPoly newpoly;
|
||||
TPPLPoint d1,d2,p1,p2,p3;
|
||||
long i11,i12,i21,i22,i13,i23,j,k;
|
||||
|
@ -486,7 +492,7 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
|||
|
||||
if(!Triangulate_EC(poly,&triangles)) return 0;
|
||||
|
||||
for(iter1 = triangles.begin(); iter1 != triangles.end(); ++iter1) {
|
||||
for(iter1 = triangles.begin(); iter1 != triangles.end(); iter1++) {
|
||||
poly1 = &(*iter1);
|
||||
for(i11=0;i11<poly1->GetNumPoints();i11++) {
|
||||
d1 = poly1->GetPoint(i11);
|
||||
|
@ -494,7 +500,7 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
|||
d2 = poly1->GetPoint(i12);
|
||||
|
||||
isdiagonal = false;
|
||||
for(iter2 = iter1; iter2 != triangles.end(); ++iter2) {
|
||||
for(iter2 = iter1; iter2 != triangles.end(); iter2++) {
|
||||
if(iter1 == iter2) continue;
|
||||
poly2 = &(*iter2);
|
||||
|
||||
|
@ -550,19 +556,19 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
|||
}
|
||||
}
|
||||
|
||||
for(iter1 = triangles.begin(); iter1 != triangles.end(); ++iter1) {
|
||||
for(iter1 = triangles.begin(); iter1 != triangles.end(); iter1++) {
|
||||
parts->push_back(*iter1);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int TPPLPartition::ConvexPartition_HM(list<TPPLPoly> *inpolys, list<TPPLPoly> *parts) {
|
||||
list<TPPLPoly> outpolys;
|
||||
list<TPPLPoly>::iterator iter;
|
||||
int TPPLPartition::ConvexPartition_HM(TPPLPolyList *inpolys, TPPLPolyList *parts) {
|
||||
TPPLPolyList outpolys;
|
||||
TPPLPolyList::iterator iter;
|
||||
|
||||
if(!RemoveHoles(inpolys,&outpolys)) return 0;
|
||||
for(iter=outpolys.begin();iter!=outpolys.end();++iter) {
|
||||
for(iter=outpolys.begin();iter!=outpolys.end();iter++) {
|
||||
if(!ConvexPartition_HM(&(*iter),parts)) return 0;
|
||||
}
|
||||
return 1;
|
||||
|
@ -571,14 +577,16 @@ int TPPLPartition::ConvexPartition_HM(list<TPPLPoly> *inpolys, list<TPPLPoly> *p
|
|||
//minimum-weight polygon triangulation by dynamic programming
|
||||
//O(n^3) time complexity
|
||||
//O(n^2) space complexity
|
||||
int TPPLPartition::Triangulate_OPT(TPPLPoly *poly, list<TPPLPoly> *triangles) {
|
||||
int TPPLPartition::Triangulate_OPT(TPPLPoly *poly, TPPLPolyList *triangles) {
|
||||
if(!poly->Valid()) return 0;
|
||||
|
||||
long i,j,k,gap,n;
|
||||
DPState **dpstates;
|
||||
DPState **dpstates = NULL;
|
||||
TPPLPoint p1,p2,p3,p4;
|
||||
long bestvertex;
|
||||
tppl_float weight,minweight,d1,d2;
|
||||
Diagonal diagonal,newdiagonal;
|
||||
list<Diagonal> diagonals;
|
||||
DiagonalList diagonals;
|
||||
TPPLPoly triangle;
|
||||
int ret = 1;
|
||||
|
||||
|
@ -703,7 +711,7 @@ int TPPLPartition::Triangulate_OPT(TPPLPoly *poly, list<TPPLPoly> *triangles) {
|
|||
|
||||
void TPPLPartition::UpdateState(long a, long b, long w, long i, long j, DPState2 **dpstates) {
|
||||
Diagonal newdiagonal;
|
||||
list<Diagonal> *pairs;
|
||||
DiagonalList *pairs = NULL;
|
||||
long w2;
|
||||
|
||||
w2 = dpstates[a][b].weight;
|
||||
|
@ -725,8 +733,8 @@ void TPPLPartition::UpdateState(long a, long b, long w, long i, long j, DPState2
|
|||
}
|
||||
|
||||
void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) {
|
||||
list<Diagonal> *pairs;
|
||||
list<Diagonal>::iterator iter,lastiter;
|
||||
DiagonalList *pairs = NULL;
|
||||
DiagonalList::iterator iter,lastiter;
|
||||
long top;
|
||||
long w;
|
||||
|
||||
|
@ -742,7 +750,7 @@ void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPS
|
|||
iter = pairs->end();
|
||||
lastiter = pairs->end();
|
||||
while(iter!=pairs->begin()) {
|
||||
--iter;
|
||||
iter--;
|
||||
if(!IsReflex(vertices[iter->index2].p,vertices[j].p,vertices[k].p)) lastiter = iter;
|
||||
else break;
|
||||
}
|
||||
|
@ -756,8 +764,8 @@ void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPS
|
|||
}
|
||||
|
||||
void TPPLPartition::TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) {
|
||||
list<Diagonal> *pairs;
|
||||
list<Diagonal>::iterator iter,lastiter;
|
||||
DiagonalList *pairs = NULL;
|
||||
DiagonalList::iterator iter,lastiter;
|
||||
long top;
|
||||
long w;
|
||||
|
||||
|
@ -778,7 +786,7 @@ void TPPLPartition::TypeB(long i, long j, long k, PartitionVertex *vertices, DPS
|
|||
while(iter!=pairs->end()) {
|
||||
if(!IsReflex(vertices[i].p,vertices[j].p,vertices[iter->index1].p)) {
|
||||
lastiter = iter;
|
||||
++iter;
|
||||
iter++;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
@ -789,19 +797,21 @@ void TPPLPartition::TypeB(long i, long j, long k, PartitionVertex *vertices, DPS
|
|||
UpdateState(i,k,w,j,top,dpstates);
|
||||
}
|
||||
|
||||
int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
||||
int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
|
||||
if(!poly->Valid()) return 0;
|
||||
|
||||
TPPLPoint p1,p2,p3,p4;
|
||||
PartitionVertex *vertices;
|
||||
DPState2 **dpstates;
|
||||
PartitionVertex *vertices = NULL;
|
||||
DPState2 **dpstates = NULL;
|
||||
long i,j,k,n,gap;
|
||||
list<Diagonal> diagonals,diagonals2;
|
||||
DiagonalList diagonals,diagonals2;
|
||||
Diagonal diagonal,newdiagonal;
|
||||
list<Diagonal> *pairs,*pairs2;
|
||||
list<Diagonal>::iterator iter,iter2;
|
||||
DiagonalList *pairs = NULL,*pairs2 = NULL;
|
||||
DiagonalList::iterator iter,iter2;
|
||||
int ret;
|
||||
TPPLPoly newpoly;
|
||||
list<long> indices;
|
||||
list<long>::iterator iiter;
|
||||
vector<long> indices;
|
||||
vector<long>::iterator iiter;
|
||||
bool ijreal,jkreal;
|
||||
|
||||
n = poly->GetNumPoints();
|
||||
|
@ -919,7 +929,7 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
|||
}
|
||||
if(!vertices[diagonal.index1].isConvex) {
|
||||
iter = pairs->end();
|
||||
--iter;
|
||||
iter--;
|
||||
j = iter->index2;
|
||||
newdiagonal.index1 = j;
|
||||
newdiagonal.index2 = diagonal.index2;
|
||||
|
@ -933,7 +943,7 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
|||
break;
|
||||
}
|
||||
iter2 = pairs2->end();
|
||||
--iter2;
|
||||
iter2--;
|
||||
if(iter->index1 != iter2->index1) pairs2->pop_back();
|
||||
else break;
|
||||
}
|
||||
|
@ -1003,7 +1013,7 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
|||
pairs = &(dpstates[diagonal.index1][diagonal.index2].pairs);
|
||||
if(!vertices[diagonal.index1].isConvex) {
|
||||
iter = pairs->end();
|
||||
--iter;
|
||||
iter--;
|
||||
j = iter->index2;
|
||||
if(iter->index1 != iter->index2) ijreal = false;
|
||||
} else {
|
||||
|
@ -1031,10 +1041,10 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
|||
indices.push_back(j);
|
||||
}
|
||||
|
||||
indices.sort();
|
||||
std::sort(indices.begin(), indices.end());
|
||||
newpoly.Init((long)indices.size());
|
||||
k=0;
|
||||
for(iiter = indices.begin();iiter!=indices.end(); ++iiter) {
|
||||
for(iiter = indices.begin();iiter!=indices.end();iiter++) {
|
||||
newpoly[k] = vertices[*iiter].p;
|
||||
k++;
|
||||
}
|
||||
|
@ -1055,18 +1065,19 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
|||
//the algorithm used here is outlined in the book
|
||||
//"Computational Geometry: Algorithms and Applications"
|
||||
//by Mark de Berg, Otfried Cheong, Marc van Kreveld and Mark Overmars
|
||||
int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *monotonePolys) {
|
||||
list<TPPLPoly>::iterator iter;
|
||||
MonotoneVertex *vertices;
|
||||
int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monotonePolys) {
|
||||
TPPLPolyList::iterator iter;
|
||||
MonotoneVertex *vertices = NULL;
|
||||
long i,numvertices,vindex,vindex2,newnumvertices,maxnumvertices;
|
||||
long polystartindex, polyendindex;
|
||||
TPPLPoly *poly;
|
||||
MonotoneVertex *v,*v2,*vprev,*vnext;
|
||||
TPPLPoly *poly = NULL;
|
||||
MonotoneVertex *v = NULL,*v2 = NULL,*vprev = NULL,*vnext = NULL;
|
||||
ScanLineEdge newedge;
|
||||
bool error = false;
|
||||
|
||||
numvertices = 0;
|
||||
for(iter = inpolys->begin(); iter != inpolys->end(); ++iter) {
|
||||
for(iter = inpolys->begin(); iter != inpolys->end(); iter++) {
|
||||
if(!iter->Valid()) return 0;
|
||||
numvertices += iter->GetNumPoints();
|
||||
}
|
||||
|
||||
|
@ -1075,7 +1086,7 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
|||
newnumvertices = numvertices;
|
||||
|
||||
polystartindex = 0;
|
||||
for(iter = inpolys->begin(); iter != inpolys->end(); ++iter) {
|
||||
for(iter = inpolys->begin(); iter != inpolys->end(); iter++) {
|
||||
poly = &(*iter);
|
||||
polyendindex = polystartindex + poly->GetNumPoints()-1;
|
||||
for(i=0;i<poly->GetNumPoints();i++) {
|
||||
|
@ -1130,6 +1141,7 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
|||
set<ScanLineEdge>::iterator *edgeTreeIterators,edgeIter;
|
||||
edgeTreeIterators = new set<ScanLineEdge>::iterator[maxnumvertices];
|
||||
pair<set<ScanLineEdge>::iterator,bool> edgeTreeRet;
|
||||
for(i = 0; i<numvertices; i++) edgeTreeIterators[i] = edgeTree.end();
|
||||
|
||||
//for each vertex
|
||||
for(i=0;i<numvertices;i++) {
|
||||
|
@ -1152,16 +1164,15 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
|||
break;
|
||||
|
||||
case TPPL_VERTEXTYPE_END:
|
||||
if (edgeTreeIterators[v->previous] == edgeTree.end()) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
//if helper(ei-1) is a merge vertex
|
||||
if(vertextypes[helpers[v->previous]]==TPPL_VERTEXTYPE_MERGE) {
|
||||
//Insert the diagonal connecting vi to helper(ei-1) in D.
|
||||
AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous]);
|
||||
vertextypes[newnumvertices-2] = vertextypes[vindex];
|
||||
edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex];
|
||||
helpers[newnumvertices-2] = helpers[vindex];
|
||||
vertextypes[newnumvertices-1] = vertextypes[helpers[v->previous]];
|
||||
edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[v->previous]];
|
||||
helpers[newnumvertices-1] = helpers[helpers[v->previous]];
|
||||
AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous],
|
||||
vertextypes, edgeTreeIterators, &edgeTree, helpers);
|
||||
}
|
||||
//Delete ei-1 from T
|
||||
edgeTree.erase(edgeTreeIterators[v->previous]);
|
||||
|
@ -1176,15 +1187,10 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
|||
error = true;
|
||||
break;
|
||||
}
|
||||
--edgeIter;
|
||||
edgeIter--;
|
||||
//Insert the diagonal connecting vi to helper(ej) in D.
|
||||
AddDiagonal(vertices,&newnumvertices,vindex,helpers[edgeIter->index]);
|
||||
vertextypes[newnumvertices-2] = vertextypes[vindex];
|
||||
edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex];
|
||||
helpers[newnumvertices-2] = helpers[vindex];
|
||||
vertextypes[newnumvertices-1] = vertextypes[helpers[edgeIter->index]];
|
||||
edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[edgeIter->index]];
|
||||
helpers[newnumvertices-1] = helpers[helpers[edgeIter->index]];
|
||||
AddDiagonal(vertices,&newnumvertices,vindex,helpers[edgeIter->index],
|
||||
vertextypes, edgeTreeIterators, &edgeTree, helpers);
|
||||
vindex2 = newnumvertices-2;
|
||||
v2 = &(vertices[vindex2]);
|
||||
//helper(e j)<29>vi
|
||||
|
@ -1199,16 +1205,15 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
|||
break;
|
||||
|
||||
case TPPL_VERTEXTYPE_MERGE:
|
||||
if (edgeTreeIterators[v->previous] == edgeTree.end()) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
//if helper(ei-1) is a merge vertex
|
||||
if(vertextypes[helpers[v->previous]]==TPPL_VERTEXTYPE_MERGE) {
|
||||
//Insert the diagonal connecting vi to helper(ei-1) in D.
|
||||
AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous]);
|
||||
vertextypes[newnumvertices-2] = vertextypes[vindex];
|
||||
edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex];
|
||||
helpers[newnumvertices-2] = helpers[vindex];
|
||||
vertextypes[newnumvertices-1] = vertextypes[helpers[v->previous]];
|
||||
edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[v->previous]];
|
||||
helpers[newnumvertices-1] = helpers[helpers[v->previous]];
|
||||
AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous],
|
||||
vertextypes, edgeTreeIterators, &edgeTree, helpers);
|
||||
vindex2 = newnumvertices-2;
|
||||
v2 = &(vertices[vindex2]);
|
||||
}
|
||||
|
@ -1222,17 +1227,12 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
|||
error = true;
|
||||
break;
|
||||
}
|
||||
--edgeIter;
|
||||
edgeIter--;
|
||||
//if helper(ej) is a merge vertex
|
||||
if(vertextypes[helpers[edgeIter->index]]==TPPL_VERTEXTYPE_MERGE) {
|
||||
//Insert the diagonal connecting vi to helper(e j) in D.
|
||||
AddDiagonal(vertices,&newnumvertices,vindex2,helpers[edgeIter->index]);
|
||||
vertextypes[newnumvertices-2] = vertextypes[vindex2];
|
||||
edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex2];
|
||||
helpers[newnumvertices-2] = helpers[vindex2];
|
||||
vertextypes[newnumvertices-1] = vertextypes[helpers[edgeIter->index]];
|
||||
edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[edgeIter->index]];
|
||||
helpers[newnumvertices-1] = helpers[helpers[edgeIter->index]];
|
||||
AddDiagonal(vertices,&newnumvertices,vindex2,helpers[edgeIter->index],
|
||||
vertextypes, edgeTreeIterators, &edgeTree, helpers);
|
||||
}
|
||||
//helper(e j)<29>vi
|
||||
helpers[edgeIter->index] = vindex2;
|
||||
|
@ -1241,16 +1241,15 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
|||
case TPPL_VERTEXTYPE_REGULAR:
|
||||
//if the interior of P lies to the right of vi
|
||||
if(Below(v->p,vertices[v->previous].p)) {
|
||||
if (edgeTreeIterators[v->previous] == edgeTree.end()) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
//if helper(ei-1) is a merge vertex
|
||||
if(vertextypes[helpers[v->previous]]==TPPL_VERTEXTYPE_MERGE) {
|
||||
//Insert the diagonal connecting vi to helper(ei-1) in D.
|
||||
AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous]);
|
||||
vertextypes[newnumvertices-2] = vertextypes[vindex];
|
||||
edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex];
|
||||
helpers[newnumvertices-2] = helpers[vindex];
|
||||
vertextypes[newnumvertices-1] = vertextypes[helpers[v->previous]];
|
||||
edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[v->previous]];
|
||||
helpers[newnumvertices-1] = helpers[helpers[v->previous]];
|
||||
AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous],
|
||||
vertextypes, edgeTreeIterators, &edgeTree, helpers);
|
||||
vindex2 = newnumvertices-2;
|
||||
v2 = &(vertices[vindex2]);
|
||||
}
|
||||
|
@ -1272,17 +1271,12 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
|||
error = true;
|
||||
break;
|
||||
}
|
||||
--edgeIter;
|
||||
edgeIter--;
|
||||
//if helper(ej) is a merge vertex
|
||||
if(vertextypes[helpers[edgeIter->index]]==TPPL_VERTEXTYPE_MERGE) {
|
||||
//Insert the diagonal connecting vi to helper(e j) in D.
|
||||
AddDiagonal(vertices,&newnumvertices,vindex,helpers[edgeIter->index]);
|
||||
vertextypes[newnumvertices-2] = vertextypes[vindex];
|
||||
edgeTreeIterators[newnumvertices-2] = edgeTreeIterators[vindex];
|
||||
helpers[newnumvertices-2] = helpers[vindex];
|
||||
vertextypes[newnumvertices-1] = vertextypes[helpers[edgeIter->index]];
|
||||
edgeTreeIterators[newnumvertices-1] = edgeTreeIterators[helpers[edgeIter->index]];
|
||||
helpers[newnumvertices-1] = helpers[helpers[edgeIter->index]];
|
||||
AddDiagonal(vertices,&newnumvertices,vindex,helpers[edgeIter->index],
|
||||
vertextypes, edgeTreeIterators, &edgeTree, helpers);
|
||||
}
|
||||
//helper(e j)<29>vi
|
||||
helpers[edgeIter->index] = vindex;
|
||||
|
@ -1342,7 +1336,10 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
|||
}
|
||||
|
||||
//adds a diagonal to the doubly-connected list of vertices
|
||||
void TPPLPartition::AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2) {
|
||||
void TPPLPartition::AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2,
|
||||
char *vertextypes, set<ScanLineEdge>::iterator *edgeTreeIterators,
|
||||
set<ScanLineEdge> *edgeTree, long *helpers)
|
||||
{
|
||||
long newindex1,newindex2;
|
||||
|
||||
newindex1 = *numvertices;
|
||||
|
@ -1364,6 +1361,18 @@ void TPPLPartition::AddDiagonal(MonotoneVertex *vertices, long *numvertices, lon
|
|||
|
||||
vertices[index2].next = newindex1;
|
||||
vertices[newindex1].previous = index2;
|
||||
|
||||
//update all relevant structures
|
||||
vertextypes[newindex1] = vertextypes[index1];
|
||||
edgeTreeIterators[newindex1] = edgeTreeIterators[index1];
|
||||
helpers[newindex1] = helpers[index1];
|
||||
if(edgeTreeIterators[newindex1] != edgeTree->end())
|
||||
edgeTreeIterators[newindex1]->index = newindex1;
|
||||
vertextypes[newindex2] = vertextypes[index2];
|
||||
edgeTreeIterators[newindex2] = edgeTreeIterators[index2];
|
||||
helpers[newindex2] = helpers[index2];
|
||||
if(edgeTreeIterators[newindex2] != edgeTree->end())
|
||||
edgeTreeIterators[newindex2]->index = newindex2;
|
||||
}
|
||||
|
||||
bool TPPLPartition::Below(TPPLPoint &p1, TPPLPoint &p2) {
|
||||
|
@ -1375,7 +1384,7 @@ bool TPPLPartition::Below(TPPLPoint &p1, TPPLPoint &p2) {
|
|||
}
|
||||
|
||||
//sorts in the falling order of y values, if y is equal, x is used instead
|
||||
bool TPPLPartition::VertexSorter::operator() (long index1, long index2) const {
|
||||
bool TPPLPartition::VertexSorter::operator() (long index1, long index2) {
|
||||
if(vertices[index1].p.y > vertices[index2].p.y) return true;
|
||||
else if(vertices[index1].p.y == vertices[index2].p.y) {
|
||||
if(vertices[index1].p.x > vertices[index2].p.x) return true;
|
||||
|
@ -1412,19 +1421,21 @@ bool TPPLPartition::ScanLineEdge::operator < (const ScanLineEdge & other) const
|
|||
|
||||
//triangulates monotone polygon
|
||||
//O(n) time, O(n) space complexity
|
||||
int TPPLPartition::TriangulateMonotone(TPPLPoly *inPoly, list<TPPLPoly> *triangles) {
|
||||
int TPPLPartition::TriangulateMonotone(TPPLPoly *inPoly, TPPLPolyList *triangles) {
|
||||
if(!inPoly->Valid()) return 0;
|
||||
|
||||
long i,i2,j,topindex,bottomindex,leftindex,rightindex,vindex;
|
||||
TPPLPoint *points;
|
||||
TPPLPoint *points = NULL;
|
||||
long numpoints;
|
||||
TPPLPoly triangle;
|
||||
|
||||
numpoints = inPoly->GetNumPoints();
|
||||
points = inPoly->GetPoints();
|
||||
|
||||
//trivial calses
|
||||
if(numpoints < 3) return 0;
|
||||
//trivial case
|
||||
if(numpoints == 3) {
|
||||
triangles->push_back(*inPoly);
|
||||
return 1;
|
||||
}
|
||||
|
||||
topindex = 0; bottomindex=0;
|
||||
|
@ -1544,19 +1555,19 @@ int TPPLPartition::TriangulateMonotone(TPPLPoly *inPoly, list<TPPLPoly> *triangl
|
|||
return 1;
|
||||
}
|
||||
|
||||
int TPPLPartition::Triangulate_MONO(list<TPPLPoly> *inpolys, list<TPPLPoly> *triangles) {
|
||||
list<TPPLPoly> monotone;
|
||||
list<TPPLPoly>::iterator iter;
|
||||
int TPPLPartition::Triangulate_MONO(TPPLPolyList *inpolys, TPPLPolyList *triangles) {
|
||||
TPPLPolyList monotone;
|
||||
TPPLPolyList::iterator iter;
|
||||
|
||||
if(!MonotonePartition(inpolys,&monotone)) return 0;
|
||||
for(iter = monotone.begin(); iter!=monotone.end(); ++iter) {
|
||||
for(iter = monotone.begin(); iter!=monotone.end();iter++) {
|
||||
if(!TriangulateMonotone(&(*iter),triangles)) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int TPPLPartition::Triangulate_MONO(TPPLPoly *poly, list<TPPLPoly> *triangles) {
|
||||
list<TPPLPoly> polys;
|
||||
int TPPLPartition::Triangulate_MONO(TPPLPoly *poly, TPPLPolyList *triangles) {
|
||||
TPPLPolyList polys;
|
||||
polys.push_back(*poly);
|
||||
|
||||
return Triangulate_MONO(&polys, triangles);
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
//THE SOFTWARE.
|
||||
|
||||
#ifndef POLYPARTITION_H
|
||||
#define POLYPARTITION_H
|
||||
|
||||
#include <list>
|
||||
using namespace std;
|
||||
#include <set>
|
||||
|
||||
typedef double tppl_float;
|
||||
|
||||
|
@ -29,315 +31,349 @@ typedef double tppl_float;
|
|||
|
||||
//2D point structure
|
||||
struct TPPLPoint {
|
||||
tppl_float x;
|
||||
tppl_float y;
|
||||
|
||||
TPPLPoint operator + (const TPPLPoint& p) const {
|
||||
TPPLPoint r;
|
||||
r.x = x + p.x;
|
||||
r.y = y + p.y;
|
||||
return r;
|
||||
}
|
||||
|
||||
TPPLPoint operator - (const TPPLPoint& p) const {
|
||||
TPPLPoint r;
|
||||
r.x = x - p.x;
|
||||
r.y = y - p.y;
|
||||
return r;
|
||||
}
|
||||
|
||||
TPPLPoint operator * (const tppl_float f ) const {
|
||||
TPPLPoint r;
|
||||
r.x = x*f;
|
||||
r.y = y*f;
|
||||
return r;
|
||||
}
|
||||
|
||||
TPPLPoint operator / (const tppl_float f ) const {
|
||||
TPPLPoint r;
|
||||
r.x = x/f;
|
||||
r.y = y/f;
|
||||
return r;
|
||||
}
|
||||
|
||||
bool operator==(const TPPLPoint& p) const {
|
||||
if((x == p.x)&&(y==p.y)) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool operator!=(const TPPLPoint& p) const {
|
||||
if((x == p.x)&&(y==p.y)) return false;
|
||||
else return true;
|
||||
}
|
||||
tppl_float x;
|
||||
tppl_float y;
|
||||
// User-specified vertex identifier. Note that this isn't used internally
|
||||
// by the library, but will be faithfully copied around.
|
||||
int id;
|
||||
|
||||
TPPLPoint operator + (const TPPLPoint& p) const {
|
||||
TPPLPoint r;
|
||||
r.x = x + p.x;
|
||||
r.y = y + p.y;
|
||||
return r;
|
||||
}
|
||||
|
||||
TPPLPoint operator - (const TPPLPoint& p) const {
|
||||
TPPLPoint r;
|
||||
r.x = x - p.x;
|
||||
r.y = y - p.y;
|
||||
return r;
|
||||
}
|
||||
|
||||
TPPLPoint operator * (const tppl_float f ) const {
|
||||
TPPLPoint r;
|
||||
r.x = x*f;
|
||||
r.y = y*f;
|
||||
return r;
|
||||
}
|
||||
|
||||
TPPLPoint operator / (const tppl_float f ) const {
|
||||
TPPLPoint r;
|
||||
r.x = x/f;
|
||||
r.y = y/f;
|
||||
return r;
|
||||
}
|
||||
|
||||
bool operator==(const TPPLPoint& p) const {
|
||||
if((x == p.x)&&(y==p.y)) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool operator!=(const TPPLPoint& p) const {
|
||||
if((x == p.x)&&(y==p.y)) return false;
|
||||
else return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//Polygon implemented as an array of points with a 'hole' flag
|
||||
class TPPLPoly {
|
||||
protected:
|
||||
protected:
|
||||
|
||||
TPPLPoint *points;
|
||||
long numpoints;
|
||||
bool hole;
|
||||
|
||||
public:
|
||||
|
||||
//constructors/destructors
|
||||
TPPLPoly();
|
||||
~TPPLPoly();
|
||||
|
||||
TPPLPoly(const TPPLPoly &src);
|
||||
TPPLPoly& operator=(const TPPLPoly &src);
|
||||
|
||||
//getters and setters
|
||||
long GetNumPoints() const {
|
||||
return numpoints;
|
||||
}
|
||||
|
||||
bool IsHole() const {
|
||||
return hole;
|
||||
}
|
||||
|
||||
void SetHole(bool hole) {
|
||||
this->hole = hole;
|
||||
}
|
||||
|
||||
TPPLPoint &GetPoint(long i) {
|
||||
return points[i];
|
||||
}
|
||||
|
||||
const TPPLPoint &GetPoint(long i) const {
|
||||
return points[i];
|
||||
}
|
||||
|
||||
TPPLPoint *points;
|
||||
long numpoints;
|
||||
bool hole;
|
||||
TPPLPoint *GetPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
TPPLPoint& operator[] (int i) {
|
||||
return points[i];
|
||||
}
|
||||
|
||||
public:
|
||||
const TPPLPoint& operator[] (int i) const {
|
||||
return points[i];
|
||||
}
|
||||
|
||||
//clears the polygon points
|
||||
void Clear();
|
||||
|
||||
//inits the polygon with numpoints vertices
|
||||
void Init(long numpoints);
|
||||
|
||||
//creates a triangle with points p1,p2,p3
|
||||
void Triangle(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3);
|
||||
|
||||
//inverts the orfer of vertices
|
||||
void Invert();
|
||||
|
||||
//returns the orientation of the polygon
|
||||
//possible values:
|
||||
// TPPL_CCW : polygon vertices are in counter-clockwise order
|
||||
// TPPL_CW : polygon vertices are in clockwise order
|
||||
// 0 : the polygon has no (measurable) area
|
||||
int GetOrientation() const;
|
||||
|
||||
//sets the polygon orientation
|
||||
//orientation can be
|
||||
// TPPL_CCW : sets vertices in counter-clockwise order
|
||||
// TPPL_CW : sets vertices in clockwise order
|
||||
void SetOrientation(int orientation);
|
||||
|
||||
//constructors/destructors
|
||||
TPPLPoly();
|
||||
~TPPLPoly();
|
||||
|
||||
TPPLPoly(const TPPLPoly &src);
|
||||
TPPLPoly& operator=(const TPPLPoly &src);
|
||||
|
||||
//getters and setters
|
||||
long GetNumPoints() const {
|
||||
return numpoints;
|
||||
}
|
||||
|
||||
bool IsHole() const {
|
||||
return hole;
|
||||
}
|
||||
|
||||
void SetHole(bool hole) {
|
||||
this->hole = hole;
|
||||
}
|
||||
|
||||
TPPLPoint &GetPoint(long i) {
|
||||
return points[i];
|
||||
}
|
||||
|
||||
TPPLPoint *GetPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
TPPLPoint& operator[] (int i) {
|
||||
return points[i];
|
||||
}
|
||||
|
||||
//clears the polygon points
|
||||
void Clear();
|
||||
|
||||
//inits the polygon with numpoints vertices
|
||||
void Init(long numpoints);
|
||||
|
||||
//creates a triangle with points p1,p2,p3
|
||||
void Triangle(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3);
|
||||
|
||||
//inverts the orfer of vertices
|
||||
void Invert();
|
||||
|
||||
//returns the orientation of the polygon
|
||||
//possible values:
|
||||
// TPPL_CCW : polygon vertices are in counter-clockwise order
|
||||
// TPPL_CW : polygon vertices are in clockwise order
|
||||
// 0 : the polygon has no (measurable) area
|
||||
int GetOrientation() const;
|
||||
|
||||
//sets the polygon orientation
|
||||
//orientation can be
|
||||
// TPPL_CCW : sets vertices in counter-clockwise order
|
||||
// TPPL_CW : sets vertices in clockwise order
|
||||
void SetOrientation(int orientation);
|
||||
//checks whether a polygon is valid or not
|
||||
inline bool Valid() const { return this->numpoints >= 3; }
|
||||
};
|
||||
|
||||
#ifdef TPPL_ALLOCATOR
|
||||
typedef std::list<TPPLPoly, TPPL_ALLOCATOR(TPPLPoly)> TPPLPolyList;
|
||||
#else
|
||||
typedef std::list<TPPLPoly> TPPLPolyList;
|
||||
#endif
|
||||
|
||||
class TPPLPartition {
|
||||
protected:
|
||||
struct PartitionVertex {
|
||||
bool isActive;
|
||||
bool isConvex;
|
||||
bool isEar;
|
||||
protected:
|
||||
struct PartitionVertex {
|
||||
bool isActive;
|
||||
bool isConvex;
|
||||
bool isEar;
|
||||
|
||||
TPPLPoint p;
|
||||
tppl_float angle;
|
||||
PartitionVertex *previous;
|
||||
PartitionVertex *next;
|
||||
|
||||
PartitionVertex();
|
||||
};
|
||||
|
||||
struct MonotoneVertex {
|
||||
TPPLPoint p;
|
||||
long previous;
|
||||
long next;
|
||||
};
|
||||
|
||||
class VertexSorter{
|
||||
MonotoneVertex *vertices;
|
||||
public:
|
||||
VertexSorter(MonotoneVertex *v) : vertices(v) {}
|
||||
bool operator() (long index1, long index2);
|
||||
};
|
||||
|
||||
struct Diagonal {
|
||||
long index1;
|
||||
long index2;
|
||||
};
|
||||
|
||||
TPPLPoint p;
|
||||
tppl_float angle;
|
||||
PartitionVertex *previous;
|
||||
PartitionVertex *next;
|
||||
};
|
||||
|
||||
struct MonotoneVertex {
|
||||
TPPLPoint p;
|
||||
long previous;
|
||||
long next;
|
||||
};
|
||||
|
||||
class VertexSorter{
|
||||
MonotoneVertex *vertices;
|
||||
public:
|
||||
VertexSorter(MonotoneVertex *v) : vertices(v) {}
|
||||
bool operator() (long index1, long index2) const;
|
||||
};
|
||||
|
||||
struct Diagonal {
|
||||
long index1;
|
||||
long index2;
|
||||
};
|
||||
|
||||
//dynamic programming state for minimum-weight triangulation
|
||||
struct DPState {
|
||||
bool visible;
|
||||
tppl_float weight;
|
||||
long bestvertex;
|
||||
};
|
||||
|
||||
//dynamic programming state for convex partitioning
|
||||
struct DPState2 {
|
||||
bool visible;
|
||||
long weight;
|
||||
list<Diagonal> pairs;
|
||||
};
|
||||
|
||||
//edge that intersects the scanline
|
||||
struct ScanLineEdge {
|
||||
long index;
|
||||
TPPLPoint p1;
|
||||
TPPLPoint p2;
|
||||
|
||||
//determines if the edge is to the left of another edge
|
||||
bool operator< (const ScanLineEdge & other) const;
|
||||
|
||||
bool IsConvex(const TPPLPoint& p1, const TPPLPoint& p2, const TPPLPoint& p3) const;
|
||||
};
|
||||
|
||||
//standard helper functions
|
||||
bool IsConvex(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3);
|
||||
bool IsReflex(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3);
|
||||
bool IsInside(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3, TPPLPoint &p);
|
||||
|
||||
bool InCone(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3, TPPLPoint &p);
|
||||
bool InCone(PartitionVertex *v, TPPLPoint &p);
|
||||
|
||||
int Intersects(TPPLPoint &p11, TPPLPoint &p12, TPPLPoint &p21, TPPLPoint &p22);
|
||||
|
||||
TPPLPoint Normalize(const TPPLPoint &p);
|
||||
tppl_float Distance(const TPPLPoint &p1, const TPPLPoint &p2);
|
||||
|
||||
//helper functions for Triangulate_EC
|
||||
void UpdateVertexReflexity(PartitionVertex *v);
|
||||
void UpdateVertex(PartitionVertex *v,PartitionVertex *vertices, long numvertices);
|
||||
|
||||
//helper functions for ConvexPartition_OPT
|
||||
void UpdateState(long a, long b, long w, long i, long j, DPState2 **dpstates);
|
||||
void TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates);
|
||||
void TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates);
|
||||
|
||||
//helper functions for MonotonePartition
|
||||
bool Below(TPPLPoint &p1, TPPLPoint &p2);
|
||||
void AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2);
|
||||
|
||||
//triangulates a monotone polygon, used in Triangulate_MONO
|
||||
int TriangulateMonotone(TPPLPoly *inPoly, list<TPPLPoly> *triangles);
|
||||
|
||||
public:
|
||||
|
||||
//simple heuristic procedure for removing holes from a list of polygons
|
||||
//works by creating a diagonal from the rightmost hole vertex to some visible vertex
|
||||
//time complexity: O(h*(n^2)), h is the number of holes, n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// inpolys : a list of polygons that can contain holes
|
||||
// vertices of all non-hole polys have to be in counter-clockwise order
|
||||
// vertices of all hole polys have to be in clockwise order
|
||||
// outpolys : a list of polygons without holes
|
||||
//returns 1 on success, 0 on failure
|
||||
int RemoveHoles(list<TPPLPoly> *inpolys, list<TPPLPoly> *outpolys);
|
||||
|
||||
//triangulates a polygon by ear clipping
|
||||
//time complexity O(n^2), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// poly : an input polygon to be triangulated
|
||||
// vertices have to be in counter-clockwise order
|
||||
// triangles : a list of triangles (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int Triangulate_EC(TPPLPoly *poly, list<TPPLPoly> *triangles);
|
||||
|
||||
//triangulates a list of polygons that may contain holes by ear clipping algorithm
|
||||
//first calls RemoveHoles to get rid of the holes, and then Triangulate_EC for each resulting polygon
|
||||
//time complexity: O(h*(n^2)), h is the number of holes, n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// inpolys : a list of polygons to be triangulated (can contain holes)
|
||||
// vertices of all non-hole polys have to be in counter-clockwise order
|
||||
// vertices of all hole polys have to be in clockwise order
|
||||
// triangles : a list of triangles (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int Triangulate_EC(list<TPPLPoly> *inpolys, list<TPPLPoly> *triangles);
|
||||
|
||||
//creates an optimal polygon triangulation in terms of minimal edge length
|
||||
//time complexity: O(n^3), n is the number of vertices
|
||||
//space complexity: O(n^2)
|
||||
//params:
|
||||
// poly : an input polygon to be triangulated
|
||||
// vertices have to be in counter-clockwise order
|
||||
// triangles : a list of triangles (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int Triangulate_OPT(TPPLPoly *poly, list<TPPLPoly> *triangles);
|
||||
|
||||
//triangulates a polygons by firstly partitioning it into monotone polygons
|
||||
//time complexity: O(n*log(n)), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// poly : an input polygon to be triangulated
|
||||
// vertices have to be in counter-clockwise order
|
||||
// triangles : a list of triangles (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int Triangulate_MONO(TPPLPoly *poly, list<TPPLPoly> *triangles);
|
||||
|
||||
//triangulates a list of polygons by firstly partitioning them into monotone polygons
|
||||
//time complexity: O(n*log(n)), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// inpolys : a list of polygons to be triangulated (can contain holes)
|
||||
// vertices of all non-hole polys have to be in counter-clockwise order
|
||||
// vertices of all hole polys have to be in clockwise order
|
||||
// triangles : a list of triangles (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int Triangulate_MONO(list<TPPLPoly> *inpolys, list<TPPLPoly> *triangles);
|
||||
|
||||
//creates a monotone partition of a list of polygons that can contain holes
|
||||
//time complexity: O(n*log(n)), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// inpolys : a list of polygons to be triangulated (can contain holes)
|
||||
// vertices of all non-hole polys have to be in counter-clockwise order
|
||||
// vertices of all hole polys have to be in clockwise order
|
||||
// monotonePolys : a list of monotone polygons (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *monotonePolys);
|
||||
|
||||
//partitions a polygon into convex polygons by using Hertel-Mehlhorn algorithm
|
||||
//the algorithm gives at most four times the number of parts as the optimal algorithm
|
||||
//however, in practice it works much better than that and often gives optimal partition
|
||||
//uses triangulation obtained by ear clipping as intermediate result
|
||||
//time complexity O(n^2), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// poly : an input polygon to be partitioned
|
||||
// vertices have to be in counter-clockwise order
|
||||
// parts : resulting list of convex polygons
|
||||
//returns 1 on success, 0 on failure
|
||||
int ConvexPartition_HM(TPPLPoly *poly, list<TPPLPoly> *parts);
|
||||
|
||||
//partitions a list of polygons into convex parts by using Hertel-Mehlhorn algorithm
|
||||
//the algorithm gives at most four times the number of parts as the optimal algorithm
|
||||
//however, in practice it works much better than that and often gives optimal partition
|
||||
//uses triangulation obtained by ear clipping as intermediate result
|
||||
//time complexity O(n^2), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// inpolys : an input list of polygons to be partitioned
|
||||
// vertices of all non-hole polys have to be in counter-clockwise order
|
||||
// vertices of all hole polys have to be in clockwise order
|
||||
// parts : resulting list of convex polygons
|
||||
//returns 1 on success, 0 on failure
|
||||
int ConvexPartition_HM(list<TPPLPoly> *inpolys, list<TPPLPoly> *parts);
|
||||
|
||||
//optimal convex partitioning (in terms of number of resulting convex polygons)
|
||||
//using the Keil-Snoeyink algorithm
|
||||
//M. Keil, J. Snoeyink, "On the time bound for convex decomposition of simple polygons", 1998
|
||||
//time complexity O(n^3), n is the number of vertices
|
||||
//space complexity: O(n^3)
|
||||
// poly : an input polygon to be partitioned
|
||||
// vertices have to be in counter-clockwise order
|
||||
// parts : resulting list of convex polygons
|
||||
//returns 1 on success, 0 on failure
|
||||
int ConvexPartition_OPT(TPPLPoly *poly, list<TPPLPoly> *parts);
|
||||
#ifdef TPPL_ALLOCATOR
|
||||
typedef std::list<Diagonal, TPPL_ALLOCATOR(Diagonal)> DiagonalList;
|
||||
#else
|
||||
typedef std::list<Diagonal> DiagonalList;
|
||||
#endif
|
||||
|
||||
//dynamic programming state for minimum-weight triangulation
|
||||
struct DPState {
|
||||
bool visible;
|
||||
tppl_float weight;
|
||||
long bestvertex;
|
||||
};
|
||||
|
||||
//dynamic programming state for convex partitioning
|
||||
struct DPState2 {
|
||||
bool visible;
|
||||
long weight;
|
||||
DiagonalList pairs;
|
||||
};
|
||||
|
||||
//edge that intersects the scanline
|
||||
struct ScanLineEdge {
|
||||
mutable long index;
|
||||
TPPLPoint p1;
|
||||
TPPLPoint p2;
|
||||
|
||||
//determines if the edge is to the left of another edge
|
||||
bool operator< (const ScanLineEdge & other) const;
|
||||
|
||||
bool IsConvex(const TPPLPoint& p1, const TPPLPoint& p2, const TPPLPoint& p3) const;
|
||||
};
|
||||
|
||||
//standard helper functions
|
||||
bool IsConvex(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3);
|
||||
bool IsReflex(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3);
|
||||
bool IsInside(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3, TPPLPoint &p);
|
||||
|
||||
bool InCone(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3, TPPLPoint &p);
|
||||
bool InCone(PartitionVertex *v, TPPLPoint &p);
|
||||
|
||||
int Intersects(TPPLPoint &p11, TPPLPoint &p12, TPPLPoint &p21, TPPLPoint &p22);
|
||||
|
||||
TPPLPoint Normalize(const TPPLPoint &p);
|
||||
tppl_float Distance(const TPPLPoint &p1, const TPPLPoint &p2);
|
||||
|
||||
//helper functions for Triangulate_EC
|
||||
void UpdateVertexReflexity(PartitionVertex *v);
|
||||
void UpdateVertex(PartitionVertex *v,PartitionVertex *vertices, long numvertices);
|
||||
|
||||
//helper functions for ConvexPartition_OPT
|
||||
void UpdateState(long a, long b, long w, long i, long j, DPState2 **dpstates);
|
||||
void TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates);
|
||||
void TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates);
|
||||
|
||||
//helper functions for MonotonePartition
|
||||
bool Below(TPPLPoint &p1, TPPLPoint &p2);
|
||||
void AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2,
|
||||
char *vertextypes, std::set<ScanLineEdge>::iterator *edgeTreeIterators,
|
||||
std::set<ScanLineEdge> *edgeTree, long *helpers);
|
||||
|
||||
//triangulates a monotone polygon, used in Triangulate_MONO
|
||||
int TriangulateMonotone(TPPLPoly *inPoly, TPPLPolyList *triangles);
|
||||
|
||||
public:
|
||||
|
||||
//simple heuristic procedure for removing holes from a list of polygons
|
||||
//works by creating a diagonal from the rightmost hole vertex to some visible vertex
|
||||
//time complexity: O(h*(n^2)), h is the number of holes, n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// inpolys : a list of polygons that can contain holes
|
||||
// vertices of all non-hole polys have to be in counter-clockwise order
|
||||
// vertices of all hole polys have to be in clockwise order
|
||||
// outpolys : a list of polygons without holes
|
||||
//returns 1 on success, 0 on failure
|
||||
int RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys);
|
||||
|
||||
//triangulates a polygon by ear clipping
|
||||
//time complexity O(n^2), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// poly : an input polygon to be triangulated
|
||||
// vertices have to be in counter-clockwise order
|
||||
// triangles : a list of triangles (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int Triangulate_EC(TPPLPoly *poly, TPPLPolyList *triangles);
|
||||
|
||||
//triangulates a list of polygons that may contain holes by ear clipping algorithm
|
||||
//first calls RemoveHoles to get rid of the holes, and then Triangulate_EC for each resulting polygon
|
||||
//time complexity: O(h*(n^2)), h is the number of holes, n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// inpolys : a list of polygons to be triangulated (can contain holes)
|
||||
// vertices of all non-hole polys have to be in counter-clockwise order
|
||||
// vertices of all hole polys have to be in clockwise order
|
||||
// triangles : a list of triangles (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles);
|
||||
|
||||
//creates an optimal polygon triangulation in terms of minimal edge length
|
||||
//time complexity: O(n^3), n is the number of vertices
|
||||
//space complexity: O(n^2)
|
||||
//params:
|
||||
// poly : an input polygon to be triangulated
|
||||
// vertices have to be in counter-clockwise order
|
||||
// triangles : a list of triangles (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int Triangulate_OPT(TPPLPoly *poly, TPPLPolyList *triangles);
|
||||
|
||||
//triangulates a polygons by firstly partitioning it into monotone polygons
|
||||
//time complexity: O(n*log(n)), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// poly : an input polygon to be triangulated
|
||||
// vertices have to be in counter-clockwise order
|
||||
// triangles : a list of triangles (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int Triangulate_MONO(TPPLPoly *poly, TPPLPolyList *triangles);
|
||||
|
||||
//triangulates a list of polygons by firstly partitioning them into monotone polygons
|
||||
//time complexity: O(n*log(n)), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// inpolys : a list of polygons to be triangulated (can contain holes)
|
||||
// vertices of all non-hole polys have to be in counter-clockwise order
|
||||
// vertices of all hole polys have to be in clockwise order
|
||||
// triangles : a list of triangles (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int Triangulate_MONO(TPPLPolyList *inpolys, TPPLPolyList *triangles);
|
||||
|
||||
//creates a monotone partition of a list of polygons that can contain holes
|
||||
//time complexity: O(n*log(n)), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// inpolys : a list of polygons to be triangulated (can contain holes)
|
||||
// vertices of all non-hole polys have to be in counter-clockwise order
|
||||
// vertices of all hole polys have to be in clockwise order
|
||||
// monotonePolys : a list of monotone polygons (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monotonePolys);
|
||||
|
||||
//partitions a polygon into convex polygons by using Hertel-Mehlhorn algorithm
|
||||
//the algorithm gives at most four times the number of parts as the optimal algorithm
|
||||
//however, in practice it works much better than that and often gives optimal partition
|
||||
//uses triangulation obtained by ear clipping as intermediate result
|
||||
//time complexity O(n^2), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// poly : an input polygon to be partitioned
|
||||
// vertices have to be in counter-clockwise order
|
||||
// parts : resulting list of convex polygons
|
||||
//returns 1 on success, 0 on failure
|
||||
int ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts);
|
||||
|
||||
//partitions a list of polygons into convex parts by using Hertel-Mehlhorn algorithm
|
||||
//the algorithm gives at most four times the number of parts as the optimal algorithm
|
||||
//however, in practice it works much better than that and often gives optimal partition
|
||||
//uses triangulation obtained by ear clipping as intermediate result
|
||||
//time complexity O(n^2), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// inpolys : an input list of polygons to be partitioned
|
||||
// vertices of all non-hole polys have to be in counter-clockwise order
|
||||
// vertices of all hole polys have to be in clockwise order
|
||||
// parts : resulting list of convex polygons
|
||||
//returns 1 on success, 0 on failure
|
||||
int ConvexPartition_HM(TPPLPolyList *inpolys, TPPLPolyList *parts);
|
||||
|
||||
//optimal convex partitioning (in terms of number of resulting convex polygons)
|
||||
//using the Keil-Snoeyink algorithm
|
||||
//M. Keil, J. Snoeyink, "On the time bound for convex decomposition of simple polygons", 1998
|
||||
//time complexity O(n^3), n is the number of vertices
|
||||
//space complexity: O(n^3)
|
||||
// poly : an input polygon to be partitioned
|
||||
// vertices have to be in counter-clockwise order
|
||||
// parts : resulting list of convex polygons
|
||||
//returns 1 on success, 0 on failure
|
||||
int ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -386,58 +386,6 @@ void GLVolume::render() const
|
|||
::glPopMatrix();
|
||||
}
|
||||
|
||||
void GLVolume::render_using_layer_height() const
|
||||
{
|
||||
if (!is_active)
|
||||
return;
|
||||
|
||||
GLint current_program_id;
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id);
|
||||
|
||||
if ((layer_height_texture_data.shader_id > 0) && (layer_height_texture_data.shader_id != current_program_id))
|
||||
glUseProgram(layer_height_texture_data.shader_id);
|
||||
|
||||
GLint z_to_texture_row_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_to_texture_row") : -1;
|
||||
GLint z_texture_row_to_normalized_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_texture_row_to_normalized") : -1;
|
||||
GLint z_cursor_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor") : -1;
|
||||
GLint z_cursor_band_width_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor_band_width") : -1;
|
||||
GLint world_matrix_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "volume_world_matrix") : -1;
|
||||
|
||||
if (z_to_texture_row_id >= 0)
|
||||
glUniform1f(z_to_texture_row_id, (GLfloat)layer_height_texture_z_to_row_id());
|
||||
|
||||
if (z_texture_row_to_normalized_id >= 0)
|
||||
glUniform1f(z_texture_row_to_normalized_id, (GLfloat)(1.0f / layer_height_texture_height()));
|
||||
|
||||
if (z_cursor_id >= 0)
|
||||
glUniform1f(z_cursor_id, (GLfloat)(layer_height_texture_data.print_object->model_object()->bounding_box().max(2) * layer_height_texture_data.z_cursor_relative));
|
||||
|
||||
if (z_cursor_band_width_id >= 0)
|
||||
glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width);
|
||||
|
||||
if (world_matrix_id >= 0)
|
||||
::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast<float>().data());
|
||||
|
||||
GLsizei w = (GLsizei)layer_height_texture_width();
|
||||
GLsizei h = (GLsizei)layer_height_texture_height();
|
||||
GLsizei half_w = w / 2;
|
||||
GLsizei half_h = h / 2;
|
||||
|
||||
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glBindTexture(GL_TEXTURE_2D, layer_height_texture_data.texture_id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level0());
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1());
|
||||
|
||||
render();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
if ((current_program_id > 0) && (layer_height_texture_data.shader_id != current_program_id))
|
||||
glUseProgram(current_program_id);
|
||||
}
|
||||
|
||||
void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) const
|
||||
{
|
||||
if (!is_active)
|
||||
|
@ -446,16 +394,6 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c
|
|||
if (!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id)
|
||||
return;
|
||||
|
||||
if (layer_height_texture_data.can_use())
|
||||
{
|
||||
::glDisableClientState(GL_VERTEX_ARRAY);
|
||||
::glDisableClientState(GL_NORMAL_ARRAY);
|
||||
render_using_layer_height();
|
||||
::glEnableClientState(GL_VERTEX_ARRAY);
|
||||
::glEnableClientState(GL_NORMAL_ARRAY);
|
||||
return;
|
||||
}
|
||||
|
||||
GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first));
|
||||
GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first));
|
||||
if (n_triangles + n_quads == 0)
|
||||
|
@ -558,44 +496,6 @@ void GLVolume::render_legacy() const
|
|||
::glPopMatrix();
|
||||
}
|
||||
|
||||
double GLVolume::layer_height_texture_z_to_row_id() const
|
||||
{
|
||||
return (this->layer_height_texture.get() == nullptr) ? 0.0 : double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * this->layer_height_texture_data.print_object->model_object()->bounding_box().max(2));
|
||||
}
|
||||
|
||||
void GLVolume::generate_layer_height_texture(const PrintObject *print_object, bool force)
|
||||
{
|
||||
LayersTexture *tex = this->layer_height_texture.get();
|
||||
if (tex == nullptr)
|
||||
// No layer_height_texture is assigned to this GLVolume, therefore the layer height texture cannot be filled.
|
||||
return;
|
||||
|
||||
// Always try to update the layer height profile.
|
||||
bool update = print_object->update_layer_height_profile(const_cast<ModelObject*>(print_object->model_object())->layer_height_profile) || force;
|
||||
// Update if the layer height profile was changed, or when the texture is not valid.
|
||||
if (! update && ! tex->data.empty() && tex->cells > 0)
|
||||
// Texture is valid, don't update.
|
||||
return;
|
||||
|
||||
if (tex->data.empty()) {
|
||||
tex->width = 1024;
|
||||
tex->height = 1024;
|
||||
tex->levels = 2;
|
||||
tex->data.assign(tex->width * tex->height * 5, 0);
|
||||
}
|
||||
|
||||
SlicingParameters slicing_params = print_object->slicing_parameters();
|
||||
bool level_of_detail_2nd_level = true;
|
||||
tex->cells = Slic3r::generate_layer_height_texture(
|
||||
slicing_params,
|
||||
Slic3r::generate_object_layers(slicing_params, print_object->model_object()->layer_height_profile),
|
||||
tex->data.data(), tex->height, tex->width, level_of_detail_2nd_level);
|
||||
}
|
||||
|
||||
// 512x512 bitmaps are supported everywhere, but that may not be sufficent for super large print volumes.
|
||||
#define LAYER_HEIGHT_TEXTURE_WIDTH 1024
|
||||
#define LAYER_HEIGHT_TEXTURE_HEIGHT 1024
|
||||
|
||||
std::vector<int> GLVolumeCollection::load_object(
|
||||
const ModelObject *model_object,
|
||||
int obj_idx,
|
||||
|
@ -603,19 +503,15 @@ std::vector<int> GLVolumeCollection::load_object(
|
|||
const std::string &color_by,
|
||||
bool use_VBOs)
|
||||
{
|
||||
// Object will share a single common layer height texture between all printable volumes.
|
||||
std::shared_ptr<LayersTexture> layer_height_texture = std::make_shared<LayersTexture>();
|
||||
std::vector<int> volumes_idx;
|
||||
for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx)
|
||||
for (int instance_idx : instance_idxs)
|
||||
volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, layer_height_texture, obj_idx, volume_idx, instance_idx, color_by, use_VBOs));
|
||||
volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by, use_VBOs));
|
||||
return volumes_idx;
|
||||
}
|
||||
|
||||
int GLVolumeCollection::load_object_volume(
|
||||
const ModelObject *model_object,
|
||||
// Layer height texture is shared between all printable volumes of a single ModelObject.
|
||||
std::shared_ptr<LayersTexture> &layer_height_texture,
|
||||
int obj_idx,
|
||||
int volume_idx,
|
||||
int instance_idx,
|
||||
|
@ -645,9 +541,7 @@ int GLVolumeCollection::load_object_volume(
|
|||
color[2] = 1.0f;
|
||||
}
|
||||
color[3] = model_volume->is_model_part() ? 1.f : 0.5f; */
|
||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
|
||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
this->volumes.emplace_back(new GLVolume(color));
|
||||
GLVolume &v = *this->volumes.back();
|
||||
v.set_color_from_model_volume(model_volume);
|
||||
|
@ -666,7 +560,6 @@ int GLVolumeCollection::load_object_volume(
|
|||
v.set_convex_hull(&model_volume->get_convex_hull(), false);
|
||||
if (extruder_id != -1)
|
||||
v.extruder_id = extruder_id;
|
||||
v.layer_height_texture = layer_height_texture;
|
||||
}
|
||||
v.is_modifier = ! model_volume->is_model_part();
|
||||
v.shader_outside_printer_detection_enabled = model_volume->is_model_part();
|
||||
|
@ -792,20 +685,21 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
|||
return int(this->volumes.size() - 1);
|
||||
}
|
||||
|
||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
typedef std::pair<GLVolume*, double> GLVolumeWithZ;
|
||||
typedef std::vector<GLVolumeWithZ> GLVolumesWithZList;
|
||||
GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type)
|
||||
static GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, std::function<bool(const GLVolume&)> filter_func)
|
||||
{
|
||||
GLVolumesWithZList list;
|
||||
list.reserve(volumes.size());
|
||||
|
||||
for (GLVolume* volume : volumes)
|
||||
{
|
||||
bool is_transparent = (volume->render_color[3] < 1.0f);
|
||||
if (((type == GLVolumeCollection::Opaque) && !is_transparent) ||
|
||||
((type == GLVolumeCollection::Transparent) && is_transparent) ||
|
||||
(type == GLVolumeCollection::All))
|
||||
list.push_back(std::make_pair(volume, 0.0));
|
||||
if ((((type == GLVolumeCollection::Opaque) && !is_transparent) ||
|
||||
((type == GLVolumeCollection::Transparent) && is_transparent) ||
|
||||
(type == GLVolumeCollection::All)) &&
|
||||
(! filter_func || filter_func(*volume)))
|
||||
list.emplace_back(std::make_pair(volume, 0.0));
|
||||
}
|
||||
|
||||
if ((type == GLVolumeCollection::Transparent) && (list.size() > 1))
|
||||
|
@ -826,19 +720,14 @@ GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollec
|
|||
return list;
|
||||
}
|
||||
|
||||
void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool disable_cullface) const
|
||||
#else
|
||||
void GLVolumeCollection::render_VBOs() const
|
||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool disable_cullface, std::function<bool(const GLVolume&)> filter_func) const
|
||||
{
|
||||
::glEnable(GL_BLEND);
|
||||
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
::glCullFace(GL_BACK);
|
||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
if (disable_cullface)
|
||||
::glDisable(GL_CULL_FACE);
|
||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
|
||||
::glEnableClientState(GL_VERTEX_ARRAY);
|
||||
::glEnableClientState(GL_NORMAL_ARRAY);
|
||||
|
@ -861,28 +750,11 @@ void GLVolumeCollection::render_VBOs() const
|
|||
if (z_range_id != -1)
|
||||
::glUniform2fv(z_range_id, 1, (const GLfloat*)z_range);
|
||||
|
||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type);
|
||||
for (GLVolumeWithZ& volume : to_render)
|
||||
{
|
||||
if (volume.first->layer_height_texture_data.can_use())
|
||||
volume.first->generate_layer_height_texture(volume.first->layer_height_texture_data.print_object, false);
|
||||
else
|
||||
volume.first->set_render_color();
|
||||
|
||||
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, filter_func);
|
||||
for (GLVolumeWithZ& volume : to_render) {
|
||||
volume.first->set_render_color();
|
||||
volume.first->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id);
|
||||
}
|
||||
#else
|
||||
for (GLVolume *volume : this->volumes)
|
||||
{
|
||||
if (volume->layer_height_texture_data.can_use())
|
||||
volume->generate_layer_height_texture(volume->layer_height_texture_data.print_object, false);
|
||||
else
|
||||
volume->set_render_color();
|
||||
|
||||
volume->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id);
|
||||
}
|
||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
|
||||
::glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
@ -890,54 +762,36 @@ void GLVolumeCollection::render_VBOs() const
|
|||
::glDisableClientState(GL_VERTEX_ARRAY);
|
||||
::glDisableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
if (disable_cullface)
|
||||
::glEnable(GL_CULL_FACE);
|
||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
|
||||
::glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface) const
|
||||
#else
|
||||
void GLVolumeCollection::render_legacy() const
|
||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glCullFace(GL_BACK);
|
||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
if (disable_cullface)
|
||||
::glDisable(GL_CULL_FACE);
|
||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type);
|
||||
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, std::function<bool(const GLVolume&)>());
|
||||
for (GLVolumeWithZ& volume : to_render)
|
||||
{
|
||||
volume.first->set_render_color();
|
||||
volume.first->render_legacy();
|
||||
}
|
||||
#else
|
||||
for (GLVolume *volume : this->volumes)
|
||||
{
|
||||
volume->set_render_color();
|
||||
volume->render_legacy();
|
||||
}
|
||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
if (disable_cullface)
|
||||
::glEnable(GL_CULL_FACE);
|
||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
@ -1790,7 +1644,6 @@ void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height
|
|||
|
||||
GUI::GLCanvas3DManager _3DScene::s_canvas_mgr;
|
||||
|
||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
GLModel::GLModel()
|
||||
: m_useVBOs(false)
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
|
@ -2089,7 +1942,6 @@ bool GLCurvedArrow::on_init(bool useVBOs)
|
|||
m_volume.finalize_geometry(m_useVBOs);
|
||||
return true;
|
||||
}
|
||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs)
|
||||
|
@ -2107,7 +1959,7 @@ bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs)
|
|||
{
|
||||
model = Model::read_from_file(filename);
|
||||
}
|
||||
catch (std::exception &e)
|
||||
catch (std::exception & /* ex */)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -2126,7 +1978,7 @@ bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs)
|
|||
else
|
||||
m_volume.indexed_vertex_array.load_mesh_flat_shading(mesh);
|
||||
|
||||
float color[4] = { 0.235f, 0.235, 0.235f, 1.0f };
|
||||
float color[4] = { 0.235f, 0.235f, 0.235f, 1.0f };
|
||||
set_color(color, 4);
|
||||
|
||||
m_volume.bounding_box = m_volume.indexed_vertex_array.bounding_box();
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include "libslic3r/Model.hpp"
|
||||
#include "slic3r/GUI/GLCanvas3DManager.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Print;
|
||||
|
@ -199,50 +201,7 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
class LayersTexture
|
||||
{
|
||||
public:
|
||||
LayersTexture() : width(0), height(0), levels(0), cells(0) {}
|
||||
|
||||
// Texture data
|
||||
std::vector<char> data;
|
||||
// Width of the texture, top level.
|
||||
size_t width;
|
||||
// Height of the texture, top level.
|
||||
size_t height;
|
||||
// For how many levels of detail is the data allocated?
|
||||
size_t levels;
|
||||
// Number of texture cells allocated for the height texture.
|
||||
size_t cells;
|
||||
};
|
||||
|
||||
class GLVolume {
|
||||
struct LayerHeightTextureData
|
||||
{
|
||||
// ID of the layer height texture
|
||||
unsigned int texture_id;
|
||||
// ID of the shader used to render with the layer height texture
|
||||
unsigned int shader_id;
|
||||
// The print object to update when generating the layer height texture
|
||||
const PrintObject* print_object;
|
||||
|
||||
float z_cursor_relative;
|
||||
float edit_band_width;
|
||||
|
||||
LayerHeightTextureData() { reset(); }
|
||||
|
||||
void reset()
|
||||
{
|
||||
texture_id = 0;
|
||||
shader_id = 0;
|
||||
print_object = nullptr;
|
||||
z_cursor_relative = 0.0f;
|
||||
edit_band_width = 0.0f;
|
||||
}
|
||||
|
||||
bool can_use() const { return (texture_id > 0) && (shader_id > 0) && (print_object != nullptr); }
|
||||
};
|
||||
|
||||
public:
|
||||
static const float SELECTED_COLOR[4];
|
||||
static const float HOVER_COLOR[4];
|
||||
|
@ -406,7 +365,7 @@ public:
|
|||
int volume_idx() const { return this->composite_id.volume_id; }
|
||||
int instance_idx() const { return this->composite_id.instance_id; }
|
||||
|
||||
Transform3d world_matrix() const;
|
||||
Transform3d world_matrix() const;
|
||||
|
||||
const BoundingBoxf3& transformed_bounding_box() const;
|
||||
const BoundingBoxf3& transformed_convex_hull_bounding_box() const;
|
||||
|
@ -416,56 +375,19 @@ public:
|
|||
|
||||
void set_range(coordf_t low, coordf_t high);
|
||||
void render() const;
|
||||
void render_using_layer_height() const;
|
||||
void render_VBOs(int color_id, int detection_id, int worldmatrix_id) const;
|
||||
void render_legacy() const;
|
||||
|
||||
void finalize_geometry(bool use_VBOs) { this->indexed_vertex_array.finalize_geometry(use_VBOs); }
|
||||
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
|
||||
|
||||
/************************************************ Layer height texture ****************************************************/
|
||||
std::shared_ptr<LayersTexture> layer_height_texture;
|
||||
// Data to render this volume using the layer height texture
|
||||
LayerHeightTextureData layer_height_texture_data;
|
||||
|
||||
bool has_layer_height_texture() const
|
||||
{ return this->layer_height_texture.get() != nullptr; }
|
||||
size_t layer_height_texture_width() const
|
||||
{ return (this->layer_height_texture.get() == nullptr) ? 0 : this->layer_height_texture->width; }
|
||||
size_t layer_height_texture_height() const
|
||||
{ return (this->layer_height_texture.get() == nullptr) ? 0 : this->layer_height_texture->height; }
|
||||
size_t layer_height_texture_cells() const
|
||||
{ return (this->layer_height_texture.get() == nullptr) ? 0 : this->layer_height_texture->cells; }
|
||||
void* layer_height_texture_data_ptr_level0() const {
|
||||
return (layer_height_texture.get() == nullptr) ? 0 :
|
||||
(void*)layer_height_texture->data.data();
|
||||
}
|
||||
void* layer_height_texture_data_ptr_level1() const {
|
||||
return (layer_height_texture.get() == nullptr) ? 0 :
|
||||
(void*)(layer_height_texture->data.data() + layer_height_texture->width * layer_height_texture->height * 4);
|
||||
}
|
||||
double layer_height_texture_z_to_row_id() const;
|
||||
void generate_layer_height_texture(const PrintObject *print_object, bool force);
|
||||
|
||||
void set_layer_height_texture_data(unsigned int texture_id, unsigned int shader_id, const PrintObject* print_object, float z_cursor_relative, float edit_band_width)
|
||||
{
|
||||
layer_height_texture_data.texture_id = texture_id;
|
||||
layer_height_texture_data.shader_id = shader_id;
|
||||
layer_height_texture_data.print_object = print_object;
|
||||
layer_height_texture_data.z_cursor_relative = z_cursor_relative;
|
||||
layer_height_texture_data.edit_band_width = edit_band_width;
|
||||
}
|
||||
|
||||
void reset_layer_height_texture_data() { layer_height_texture_data.reset(); }
|
||||
|
||||
void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; }
|
||||
void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; }
|
||||
};
|
||||
|
||||
typedef std::vector<GLVolume*> GLVolumePtrs;
|
||||
|
||||
class GLVolumeCollection
|
||||
{
|
||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
public:
|
||||
enum ERenderType : unsigned char
|
||||
{
|
||||
|
@ -475,7 +397,6 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
// min and max vertex of the print box volume
|
||||
float print_box_min[3];
|
||||
float print_box_max[3];
|
||||
|
@ -498,7 +419,6 @@ public:
|
|||
|
||||
int load_object_volume(
|
||||
const ModelObject *model_object,
|
||||
std::shared_ptr<LayersTexture> &layer_height_texture,
|
||||
int obj_idx,
|
||||
int volume_idx,
|
||||
int instance_idx,
|
||||
|
@ -520,13 +440,8 @@ public:
|
|||
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width);
|
||||
|
||||
// Render the volumes by OpenGL.
|
||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
void render_VBOs(ERenderType type, bool disable_cullface) const;
|
||||
void render_VBOs(ERenderType type, bool disable_cullface, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
|
||||
void render_legacy(ERenderType type, bool disable_cullface) const;
|
||||
#else
|
||||
void render_VBOs() const;
|
||||
void render_legacy() const;
|
||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
||||
|
||||
// Finalize the initialization of the geometry & indices,
|
||||
// upload the geometry and indices to OpenGL VBO objects
|
||||
|
@ -563,7 +478,6 @@ private:
|
|||
GLVolumeCollection& operator=(const GLVolumeCollection &);
|
||||
};
|
||||
|
||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
class GLModel
|
||||
{
|
||||
protected:
|
||||
|
@ -630,7 +544,6 @@ public:
|
|||
protected:
|
||||
virtual bool on_init(bool useVBOs);
|
||||
};
|
||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
class GLBed : public GLModel
|
||||
|
|
|
@ -115,7 +115,7 @@ AboutDialog::AboutDialog()
|
|||
vsizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3);
|
||||
|
||||
// this->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this);
|
||||
logo->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this);
|
||||
// logo->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this);
|
||||
|
||||
SetSizer(main_sizer);
|
||||
main_sizer->SetSizeHints(this);
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -89,8 +89,8 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, cons
|
|||
|
||||
bool default_variant = true; // Mark the first variant as default in the GUI
|
||||
for (const auto &variant : model.variants) {
|
||||
const auto label = wxString::Format("%s %s %s %s", variant.name, _(L("mm")), _(L("nozzle")),
|
||||
(default_variant ? _(L("(default)")) : wxString()));
|
||||
const auto label = wxString::Format("%s %s %s %s", variant.name, _(L("mm")), _(L("nozzle")),
|
||||
(default_variant ? "(" + _(L("default")) + ")" : wxString()));
|
||||
default_variant = false;
|
||||
auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name);
|
||||
const size_t idx = cboxes.size();
|
||||
|
@ -803,7 +803,7 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||
// Public
|
||||
|
||||
ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) :
|
||||
wxDialog(parent, wxID_ANY, name(), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
|
||||
wxDialog(parent, wxID_ANY, _(name().ToStdString()), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
|
||||
p(new priv(this))
|
||||
{
|
||||
p->run_reason = reason;
|
||||
|
@ -899,15 +899,17 @@ bool ConfigWizard::run(PresetBundle *preset_bundle, const PresetUpdater *updater
|
|||
}
|
||||
|
||||
|
||||
const wxString& ConfigWizard::name()
|
||||
const wxString& ConfigWizard::name(const bool from_menu/* = false*/)
|
||||
{
|
||||
// A different naming convention is used for the Wizard on Windows vs. OSX & GTK.
|
||||
#if WIN32
|
||||
static const wxString config_wizard_name = L("Configuration Wizard");
|
||||
static const wxString config_wizard_name = L("Configuration Wizard");
|
||||
static const wxString config_wizard_name_menu = L("Configuration &Wizard");
|
||||
#else
|
||||
static const wxString config_wizard_name = L("Configuration Assistant");
|
||||
static const wxString config_wizard_name = L("Configuration Assistant");
|
||||
static const wxString config_wizard_name_menu = L("Configuration &Assistant");
|
||||
#endif
|
||||
return config_wizard_name;
|
||||
return from_menu ? config_wizard_name_menu : config_wizard_name;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
// Run the Wizard. Return whether it was completed.
|
||||
bool run(PresetBundle *preset_bundle, const PresetUpdater *updater);
|
||||
|
||||
static const wxString& name();
|
||||
static const wxString& name(const bool from_menu = false);
|
||||
private:
|
||||
struct priv;
|
||||
std::unique_ptr<priv> p;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||
|
||||
|
@ -29,11 +34,17 @@ class GLShader;
|
|||
class ExPolygon;
|
||||
class BackgroundSlicingProcess;
|
||||
class GCodePreviewData;
|
||||
struct SlicingParameters;
|
||||
enum LayerHeightEditActionType : unsigned int;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
class GLGizmoBase;
|
||||
|
||||
#if ENABLE_RETINA_GL
|
||||
class RetinaHelper;
|
||||
#endif
|
||||
|
||||
class GeometryBuffer
|
||||
{
|
||||
std::vector<float> m_vertices;
|
||||
|
@ -53,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
|
||||
|
@ -87,6 +102,9 @@ public:
|
|||
|
||||
float get_bottom() const;
|
||||
void set_bottom(float bottom);
|
||||
|
||||
float get_width() const { return m_right - m_left; }
|
||||
float get_height() const { return m_top - m_bottom; }
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
|
||||
|
@ -101,7 +119,6 @@ wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
|
|||
wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_MODEL_UPDATE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent);
|
||||
|
@ -171,7 +188,7 @@ class GLCanvas3D
|
|||
std::string get_type_as_string() const;
|
||||
|
||||
float get_theta() const { return m_theta; }
|
||||
void set_theta(float theta);
|
||||
void set_theta(float theta, bool apply_limit);
|
||||
|
||||
const Vec3d& get_target() const { return m_target; }
|
||||
void set_target(const Vec3d& target, GLCanvas3D& canvas);
|
||||
|
@ -205,6 +222,8 @@ class GLCanvas3D
|
|||
mutable GLBed m_model;
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
|
||||
mutable float m_scale_factor;
|
||||
|
||||
public:
|
||||
Bed();
|
||||
|
||||
|
@ -220,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:
|
||||
|
@ -293,12 +312,45 @@ class GLCanvas3D
|
|||
};
|
||||
|
||||
private:
|
||||
bool m_use_legacy_opengl;
|
||||
bool m_enabled;
|
||||
Shader m_shader;
|
||||
unsigned int m_z_texture_id;
|
||||
mutable GLTexture m_tooltip_texture;
|
||||
mutable GLTexture m_reset_texture;
|
||||
static const float THICKNESS_BAR_WIDTH;
|
||||
static const float THICKNESS_RESET_BUTTON_HEIGHT;
|
||||
|
||||
bool m_use_legacy_opengl;
|
||||
bool m_enabled;
|
||||
Shader m_shader;
|
||||
unsigned int m_z_texture_id;
|
||||
mutable GLTexture m_tooltip_texture;
|
||||
mutable GLTexture m_reset_texture;
|
||||
// Not owned by LayersEditing.
|
||||
const DynamicPrintConfig *m_config;
|
||||
// ModelObject for the currently selected object (Model::objects[last_object_id]).
|
||||
const ModelObject *m_model_object;
|
||||
// Maximum z of the currently selected object (Model::objects[last_object_id]).
|
||||
float m_object_max_z;
|
||||
// Owned by LayersEditing.
|
||||
SlicingParameters *m_slicing_parameters;
|
||||
std::vector<coordf_t> m_layer_height_profile;
|
||||
bool m_layer_height_profile_modified;
|
||||
|
||||
class LayersTexture
|
||||
{
|
||||
public:
|
||||
LayersTexture() : width(0), height(0), levels(0), cells(0), valid(false) {}
|
||||
|
||||
// Texture data
|
||||
std::vector<char> data;
|
||||
// Width of the texture, top level.
|
||||
size_t width;
|
||||
// Height of the texture, top level.
|
||||
size_t height;
|
||||
// For how many levels of detail is the data allocated?
|
||||
size_t levels;
|
||||
// Number of texture cells allocated for the height texture.
|
||||
size_t cells;
|
||||
// Does it need to be refreshed?
|
||||
bool valid;
|
||||
};
|
||||
LayersTexture m_layers_texture;
|
||||
|
||||
public:
|
||||
EState state;
|
||||
|
@ -306,12 +358,14 @@ class GLCanvas3D
|
|||
float strength;
|
||||
int last_object_id;
|
||||
float last_z;
|
||||
unsigned int last_action;
|
||||
LayerHeightEditActionType last_action;
|
||||
|
||||
LayersEditing();
|
||||
~LayersEditing();
|
||||
|
||||
bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename);
|
||||
void set_config(const DynamicPrintConfig* config);
|
||||
void select_object(const Model &model, int object_id);
|
||||
|
||||
bool is_allowed() const;
|
||||
void set_use_legacy_opengl(bool use_legacy_opengl);
|
||||
|
@ -319,11 +373,12 @@ class GLCanvas3D
|
|||
bool is_enabled() const;
|
||||
void set_enabled(bool enabled);
|
||||
|
||||
unsigned int get_z_texture_id() const;
|
||||
void render_overlay(const GLCanvas3D& canvas) const;
|
||||
void render_volumes(const GLCanvas3D& canvas, const GLVolumeCollection& volumes) const;
|
||||
|
||||
void render(const GLCanvas3D& canvas, const PrintObject& print_object, const GLVolume& volume) const;
|
||||
|
||||
int get_shader_program_id() const;
|
||||
void adjust_layer_height_profile();
|
||||
void accept_changes(GLCanvas3D& canvas);
|
||||
void reset_layer_height_profile(GLCanvas3D& canvas);
|
||||
|
||||
static float get_cursor_z_relative(const GLCanvas3D& canvas);
|
||||
static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y);
|
||||
|
@ -333,12 +388,19 @@ class GLCanvas3D
|
|||
static Rect get_bar_rect_viewport(const GLCanvas3D& canvas);
|
||||
static Rect get_reset_rect_viewport(const GLCanvas3D& canvas);
|
||||
|
||||
float object_max_z() const { return m_object_max_z; }
|
||||
|
||||
private:
|
||||
bool _is_initialized() const;
|
||||
void generate_layer_height_texture();
|
||||
void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const;
|
||||
void _render_reset_texture(const Rect& reset_rect) const;
|
||||
void _render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const;
|
||||
void _render_profile(const PrintObject& print_object, const Rect& bar_rect) const;
|
||||
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
|
||||
|
@ -492,10 +554,10 @@ public:
|
|||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
GLUquadricObj* m_quadric;
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
mutable GLArrow m_arrow;
|
||||
mutable GLCurvedArrow m_curved_arrow;
|
||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
|
||||
mutable float m_scale_factor;
|
||||
|
||||
public:
|
||||
Selection();
|
||||
|
@ -504,9 +566,7 @@ public:
|
|||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
|
||||
void set_volumes(GLVolumePtrs* volumes);
|
||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
bool init(bool useVBOs);
|
||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
|
||||
Model* get_model() const { return m_model; }
|
||||
void set_model(Model* model);
|
||||
|
@ -580,13 +640,11 @@ 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
|
||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
void render_sidebar_hints(const std::string& sidebar_field) const;
|
||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
|
||||
bool requires_local_axes() const;
|
||||
|
||||
|
@ -604,7 +662,6 @@ public:
|
|||
void _render_selected_volumes() const;
|
||||
void _render_synchronized_volumes() const;
|
||||
void _render_bounding_box(const BoundingBoxf3& box, float* color) const;
|
||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
void _render_sidebar_position_hints(const std::string& sidebar_field) const;
|
||||
void _render_sidebar_rotation_hints(const std::string& sidebar_field) const;
|
||||
void _render_sidebar_scale_hints(const std::string& sidebar_field) const;
|
||||
|
@ -613,12 +670,17 @@ public:
|
|||
void _render_sidebar_rotation_hint(Axis axis) const;
|
||||
void _render_sidebar_scale_hint(Axis axis) const;
|
||||
void _render_sidebar_size_hint(Axis axis, double length) const;
|
||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
void _synchronize_unselected_instances(bool including_z = false);
|
||||
enum SyncRotationType {
|
||||
// Do not synchronize rotation. Either not rotating at all, or rotating by world Z axis.
|
||||
SYNC_ROTATION_NONE = 0,
|
||||
// Synchronize fully. Used from "place on bed" feature.
|
||||
SYNC_ROTATION_FULL = 1,
|
||||
// Synchronize after rotation by an axis not parallel with Z.
|
||||
SYNC_ROTATION_GENERAL = 2,
|
||||
};
|
||||
void _synchronize_unselected_instances(SyncRotationType sync_rotation_type);
|
||||
void _synchronize_unselected_volumes();
|
||||
#if ENABLE_ENSURE_ON_BED_WHILE_SCALING
|
||||
void _ensure_on_bed();
|
||||
#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING
|
||||
};
|
||||
|
||||
class ClippingPlane
|
||||
|
@ -649,10 +711,6 @@ public:
|
|||
private:
|
||||
class Gizmos
|
||||
{
|
||||
static const float OverlayIconsScale;
|
||||
static const float OverlayBorder;
|
||||
static const float OverlayGapY;
|
||||
|
||||
public:
|
||||
enum EType : unsigned char
|
||||
{
|
||||
|
@ -673,6 +731,10 @@ private:
|
|||
BackgroundTexture m_background_texture;
|
||||
EType m_current;
|
||||
|
||||
float m_overlay_icons_scale;
|
||||
float m_overlay_border;
|
||||
float m_overlay_gap_y;
|
||||
|
||||
public:
|
||||
Gizmos();
|
||||
~Gizmos();
|
||||
|
@ -682,6 +744,8 @@ private:
|
|||
bool is_enabled() const;
|
||||
void set_enabled(bool enable);
|
||||
|
||||
void set_overlay_scale(float scale);
|
||||
|
||||
std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection);
|
||||
void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection);
|
||||
void update_on_off_state(const Selection& selection);
|
||||
|
@ -749,7 +813,7 @@ private:
|
|||
struct Triangles
|
||||
{
|
||||
Pointf3s object;
|
||||
Pointf3s suppports;
|
||||
Pointf3s supports;
|
||||
};
|
||||
typedef std::map<unsigned int, Triangles> ObjectIdToTrianglesMap;
|
||||
double z;
|
||||
|
@ -771,7 +835,7 @@ private:
|
|||
public:
|
||||
WarningTexture();
|
||||
|
||||
bool generate(const std::string& msg);
|
||||
bool generate(const std::string& msg, const GLCanvas3D& canvas);
|
||||
|
||||
void render(const GLCanvas3D& canvas) const;
|
||||
};
|
||||
|
@ -801,6 +865,9 @@ private:
|
|||
|
||||
wxGLCanvas* m_canvas;
|
||||
wxGLContext* m_context;
|
||||
#if ENABLE_RETINA_GL
|
||||
std::unique_ptr<RetinaHelper> m_retina_helper;
|
||||
#endif
|
||||
bool m_in_render;
|
||||
LegendTexture m_legend_texture;
|
||||
WarningTexture m_warning_texture;
|
||||
|
@ -821,7 +888,7 @@ private:
|
|||
|
||||
mutable GLVolumeCollection m_volumes;
|
||||
Selection m_selection;
|
||||
DynamicPrintConfig* m_config;
|
||||
const DynamicPrintConfig* m_config;
|
||||
Model* m_model;
|
||||
BackgroundSlicingProcess *m_process;
|
||||
|
||||
|
@ -860,9 +927,7 @@ public:
|
|||
GLCanvas3D(wxGLCanvas* canvas);
|
||||
~GLCanvas3D();
|
||||
|
||||
#if ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
void set_context(wxGLContext* context) { m_context = context; }
|
||||
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
|
||||
wxGLCanvas* get_wxglcanvas() { return m_canvas; }
|
||||
|
||||
|
@ -871,17 +936,13 @@ public:
|
|||
bool init(bool useVBOs, bool use_legacy_opengl);
|
||||
void post_event(wxEvent &&event);
|
||||
|
||||
#if !ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
bool set_current();
|
||||
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
|
||||
void set_as_dirty();
|
||||
|
||||
unsigned int get_volumes_count() const;
|
||||
void reset_volumes();
|
||||
int check_volumes_outside_state() const;
|
||||
|
||||
void set_config(DynamicPrintConfig* config);
|
||||
void set_config(const DynamicPrintConfig* config);
|
||||
void set_process(BackgroundSlicingProcess* process);
|
||||
void set_model(Model* model);
|
||||
|
||||
|
@ -1005,6 +1066,8 @@ public:
|
|||
|
||||
void handle_sidebar_focus_event(const std::string& opt_key, bool focus_on);
|
||||
|
||||
void update_ui_from_settings();
|
||||
|
||||
private:
|
||||
bool _is_shown_on_screen() const;
|
||||
#if !ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
|
@ -1013,9 +1076,7 @@ private:
|
|||
|
||||
bool _init_toolbar();
|
||||
|
||||
#if ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
bool _set_current();
|
||||
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
void _resize(unsigned int w, unsigned int h);
|
||||
|
||||
BoundingBoxf3 _max_bounding_box() const;
|
||||
|
@ -1023,7 +1084,6 @@ private:
|
|||
void _zoom_to_bounding_box(const BoundingBoxf3& bbox);
|
||||
float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const;
|
||||
|
||||
void _mark_volumes_for_layer_height() const;
|
||||
void _refresh_if_shown_on_screen();
|
||||
|
||||
void _camera_tranform() const;
|
||||
|
@ -1038,7 +1098,6 @@ private:
|
|||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
void _render_warning_texture() const;
|
||||
void _render_legend_texture() const;
|
||||
void _render_layer_editing_overlay() const;
|
||||
void _render_volumes(bool fake_colors) const;
|
||||
void _render_current_gizmo() const;
|
||||
void _render_gizmos_overlay() const;
|
||||
|
@ -1048,14 +1107,11 @@ private:
|
|||
void _render_camera_target() const;
|
||||
#endif // ENABLE_SHOW_CAMERA_TARGET
|
||||
void _render_sla_slices() const;
|
||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
void _render_selection_sidebar_hints() const;
|
||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
||||
|
||||
void _update_volumes_hover_state() const;
|
||||
void _update_gizmos_data();
|
||||
|
||||
float _get_layers_editing_cursor_z_relative() const;
|
||||
void _perform_layer_editing_action(wxMouseEvent* evt = nullptr);
|
||||
|
||||
// Convert the screen space coordinate to an object space coordinate.
|
||||
|
|
|
@ -113,16 +113,13 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten
|
|||
GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::MS_Unknown;
|
||||
|
||||
GLCanvas3DManager::GLCanvas3DManager()
|
||||
#if ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
: m_context(nullptr)
|
||||
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
, m_gl_initialized(false)
|
||||
, m_use_legacy_opengl(false)
|
||||
, m_use_VBOs(false)
|
||||
{
|
||||
}
|
||||
|
||||
#if ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
GLCanvas3DManager::~GLCanvas3DManager()
|
||||
{
|
||||
if (m_context != nullptr)
|
||||
|
@ -131,7 +128,6 @@ GLCanvas3DManager::~GLCanvas3DManager()
|
|||
m_context = nullptr;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
|
||||
bool GLCanvas3DManager::add(wxGLCanvas* canvas)
|
||||
{
|
||||
|
@ -147,7 +143,6 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas)
|
|||
|
||||
canvas3D->bind_event_handlers();
|
||||
|
||||
#if ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
if (m_context == nullptr)
|
||||
{
|
||||
m_context = new wxGLContext(canvas);
|
||||
|
@ -156,7 +151,6 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas)
|
|||
}
|
||||
|
||||
canvas3D->set_context(m_context);
|
||||
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
|
||||
m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D));
|
||||
|
||||
|
|
|
@ -51,9 +51,7 @@ class GLCanvas3DManager
|
|||
typedef std::map<wxGLCanvas*, GLCanvas3D*> CanvasesMap;
|
||||
|
||||
CanvasesMap m_canvases;
|
||||
#if ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
wxGLContext* m_context;
|
||||
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
GLInfo m_gl_info;
|
||||
bool m_gl_initialized;
|
||||
bool m_use_legacy_opengl;
|
||||
|
@ -62,9 +60,7 @@ class GLCanvas3DManager
|
|||
|
||||
public:
|
||||
GLCanvas3DManager();
|
||||
#if ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
~GLCanvas3DManager();
|
||||
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
|
||||
|
||||
bool add(wxGLCanvas* canvas);
|
||||
bool remove(wxGLCanvas* canvas);
|
||||
|
|
|
@ -409,9 +409,6 @@ void GLGizmoRotate::on_render(const GLCanvas3D::Selection& selection) const
|
|||
return;
|
||||
|
||||
const BoundingBoxf3& box = selection.get_bounding_box();
|
||||
#if !ENABLE_WORLD_ROTATIONS
|
||||
bool single_selection = selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume();
|
||||
#endif // !ENABLE_WORLD_ROTATIONS
|
||||
|
||||
std::string axis;
|
||||
switch (m_axis)
|
||||
|
@ -421,13 +418,9 @@ void GLGizmoRotate::on_render(const GLCanvas3D::Selection& selection) const
|
|||
case Z: { axis = "Z"; break; }
|
||||
}
|
||||
|
||||
#if ENABLE_WORLD_ROTATIONS
|
||||
if (!m_dragging && (m_hover_id == 0))
|
||||
set_tooltip(axis);
|
||||
else if (m_dragging)
|
||||
#else
|
||||
if ((single_selection && (m_hover_id == 0)) || m_dragging)
|
||||
#endif // ENABLE_WORLD_ROTATIONS
|
||||
set_tooltip(axis + ": " + format((float)Geometry::rad2deg(m_angle), 4) + "\u00B0");
|
||||
else
|
||||
{
|
||||
|
@ -571,11 +564,7 @@ void GLGizmoRotate::render_angle() const
|
|||
|
||||
void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const
|
||||
{
|
||||
#if ENABLE_WORLD_ROTATIONS
|
||||
double grabber_radius = (double)m_radius * (1.0 + (double)GrabberOffset);
|
||||
#else
|
||||
double grabber_radius = (double)m_radius * (1.0 + (double)GrabberOffset) + 2.0 * (double)m_axis * (double)m_grabbers[0].get_half_size((float)box.max_size());
|
||||
#endif // ENABLE_WORLD_ROTATIONS
|
||||
m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0);
|
||||
m_grabbers[0].angles(2) = m_angle;
|
||||
|
||||
|
@ -1439,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();
|
||||
}
|
||||
|
@ -1457,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)
|
||||
|
@ -1489,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));
|
||||
|
@ -1508,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 (object_changed && is_plane_update_necessary())
|
||||
update_planes();
|
||||
}
|
||||
|
||||
void GLGizmoFlatten::update_planes()
|
||||
|
@ -1526,10 +1520,9 @@ void GLGizmoFlatten::update_planes()
|
|||
vol_ch.transform(vol->get_matrix());
|
||||
ch.merge(vol_ch);
|
||||
}
|
||||
|
||||
ch = ch.convex_hull_3d();
|
||||
m_planes.clear();
|
||||
const Transform3d& inst_matrix = m_model_object->instances.front()->get_matrix();
|
||||
const Transform3d& inst_matrix = m_model_object->instances.front()->get_matrix(true);
|
||||
|
||||
// Following constants are used for discarding too small polygons.
|
||||
const float minimal_area = 5.f; // in square mm (world coordinates)
|
||||
|
@ -1586,15 +1579,17 @@ void GLGizmoFlatten::update_planes()
|
|||
m_planes.pop_back();
|
||||
}
|
||||
|
||||
// Let's prepare transformation of the normal vector from mesh to instance coordinates.
|
||||
Geometry::Transformation t(inst_matrix);
|
||||
Vec3d scaling = t.get_scaling_factor();
|
||||
t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2)));
|
||||
|
||||
// Now we'll go through all the polygons, transform the points into xy plane to process them:
|
||||
for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) {
|
||||
Pointf3s& polygon = m_planes[polygon_id].vertices;
|
||||
const Vec3d& normal = m_planes[polygon_id].normal;
|
||||
|
||||
// let's transform the normal accodring to the instance matrix:
|
||||
Geometry::Transformation t(inst_matrix);
|
||||
Vec3d scaling = t.get_scaling_factor();
|
||||
t.set_scaling_factor(Vec3d(1./(scaling(0)*scaling(0)), 1./(scaling(0)*scaling(0)), 1./(scaling(0)*scaling(0))));
|
||||
// transform the normal according to the instance matrix:
|
||||
Vec3d normal_transformed = t.get_matrix() * normal;
|
||||
|
||||
// We are going to rotate about z and y to flatten the plane
|
||||
|
@ -1696,10 +1691,6 @@ void GLGizmoFlatten::update_planes()
|
|||
|
||||
// Transform back to 3D (and also back to mesh coordinates)
|
||||
polygon = transform(polygon, inst_matrix.inverse() * m.inverse());
|
||||
|
||||
// make sure the points are in correct order:
|
||||
if ( ((inst_matrix.inverse() * m.inverse()) * Vec3d(0., 0., 1.)).dot(normal) > 0.)
|
||||
std::reverse(polygon.begin(),polygon.end());
|
||||
}
|
||||
|
||||
// We'll sort the planes by area and only keep the 254 largest ones (because of the picking pass limitations):
|
||||
|
@ -1714,6 +1705,9 @@ void GLGizmoFlatten::update_planes()
|
|||
m_volumes_types.push_back(vol->type());
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1722,11 +1716,12 @@ 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.
|
||||
if (! m_model_object->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale))
|
||||
if (! m_model_object->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale)
|
||||
|| ! m_model_object->instances.front()->get_mirror().isApprox(m_first_instance_mirror))
|
||||
return true;
|
||||
|
||||
for (unsigned int i=0; i < m_model_object->volumes.size(); ++i)
|
||||
|
@ -2466,11 +2461,13 @@ void GLGizmoCut::on_render_input_window(float x, float y, const GLCanvas3D::Sele
|
|||
m_imgui->checkbox(_(L("Keep lower part")), m_keep_lower);
|
||||
m_imgui->checkbox(_(L("Rotate lower part upwards")), m_rotate_lower);
|
||||
|
||||
m_imgui->disabled_begin(!m_keep_upper && !m_keep_lower);
|
||||
const bool cut_clicked = m_imgui->button(_(L("Perform cut")));
|
||||
m_imgui->disabled_end();
|
||||
|
||||
m_imgui->end();
|
||||
|
||||
if (cut_clicked) {
|
||||
if (cut_clicked && (m_keep_upper || m_keep_lower)) {
|
||||
perform_cut(selection);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -405,8 +405,10 @@ private:
|
|||
std::vector<Transform3d> m_volumes_matrices;
|
||||
std::vector<ModelVolume::Type> m_volumes_types;
|
||||
Vec3d m_first_instance_scale;
|
||||
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;
|
||||
|
||||
|
|
|
@ -245,8 +245,6 @@ void show_info(wxWindow* parent, const wxString& message, const wxString& title)
|
|||
|
||||
void warning_catcher(wxWindow* parent, const wxString& message)
|
||||
{
|
||||
if (message == "GLUquadricObjPtr | " + _(L("Attempt to free unreferenced scalar")) )
|
||||
return;
|
||||
wxMessageDialog msg(parent, message, _(L("Warning")), wxOK | wxICON_WARNING);
|
||||
msg.ShowModal();
|
||||
}
|
||||
|
@ -348,50 +346,6 @@ bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height
|
|||
return true;
|
||||
}
|
||||
|
||||
void save_window_size(wxTopLevelWindow *window, const std::string &name)
|
||||
{
|
||||
const wxSize size = window->GetSize();
|
||||
const wxPoint pos = window->GetPosition();
|
||||
const auto maximized = window->IsMaximized() ? "1" : "0";
|
||||
|
||||
get_app_config()->set((boost::format("window_%1%_size") % name).str(), (boost::format("%1%;%2%") % size.GetWidth() % size.GetHeight()).str());
|
||||
get_app_config()->set((boost::format("window_%1%_maximized") % name).str(), maximized);
|
||||
}
|
||||
|
||||
void restore_window_size(wxTopLevelWindow *window, const std::string &name)
|
||||
{
|
||||
// XXX: This still doesn't behave nicely in some situations (mostly on Linux).
|
||||
// The problem is that it's hard to obtain window position with respect to screen geometry reliably
|
||||
// from wxWidgets. Sometimes wxWidgets claim a window is located on a different screen than on which
|
||||
// it's actually visible. I suspect this has something to do with window initialization (maybe we
|
||||
// restore window geometry too early), but haven't yet found a workaround.
|
||||
|
||||
const auto display_idx = wxDisplay::GetFromWindow(window);
|
||||
if (display_idx == wxNOT_FOUND) { return; }
|
||||
|
||||
const auto display = wxDisplay(display_idx).GetClientArea();
|
||||
std::vector<std::string> pair;
|
||||
|
||||
try {
|
||||
const auto key_size = (boost::format("window_%1%_size") % name).str();
|
||||
if (get_app_config()->has(key_size)) {
|
||||
if (unescape_strings_cstyle(get_app_config()->get(key_size), pair) && pair.size() == 2) {
|
||||
auto width = boost::lexical_cast<int>(pair[0]);
|
||||
auto height = boost::lexical_cast<int>(pair[1]);
|
||||
|
||||
window->SetSize(width, height);
|
||||
}
|
||||
}
|
||||
} catch(const boost::bad_lexical_cast &) {}
|
||||
|
||||
// Maximizing should be the last thing to do.
|
||||
// This ensure the size and position are sane when the user un-maximizes the window.
|
||||
const auto key_maximized = (boost::format("window_%1%_maximized") % name).str();
|
||||
if (get_app_config()->get(key_maximized) == "1") {
|
||||
window->Maximize(true);
|
||||
}
|
||||
}
|
||||
|
||||
void about()
|
||||
{
|
||||
AboutDialog dlg;
|
||||
|
|
|
@ -70,11 +70,6 @@ boost::filesystem::path into_path(const wxString &str);
|
|||
// Returns the dimensions of the screen on which the main frame is displayed
|
||||
bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height);
|
||||
|
||||
// Save window size and maximized status into AppConfig
|
||||
void save_window_size(wxTopLevelWindow *window, const std::string &name);
|
||||
// Restore the above
|
||||
void restore_window_size(wxTopLevelWindow *window, const std::string &name);
|
||||
|
||||
// Display an About dialog
|
||||
extern void about();
|
||||
// Ask the destop to open the datadir using the default file explorer.
|
||||
|
|
|
@ -143,56 +143,33 @@ bool GUI_App::OnInit()
|
|||
init_fonts();
|
||||
|
||||
// application frame
|
||||
std::cerr << "Creating main frame..." << std::endl;
|
||||
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
||||
wxImage::AddHandler(new wxPNGHandler());
|
||||
mainframe = new MainFrame();
|
||||
sidebar().obj_list()->init_objects(); // propagate model objects to object list
|
||||
// update_mode(); // do that later
|
||||
// update_mode(); // !!! do that later
|
||||
SetTopWindow(mainframe);
|
||||
|
||||
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
||||
|
||||
CallAfter([this]() {
|
||||
// temporary workaround for the correct behavior of the Scrolled sidebar panel
|
||||
auto& panel = sidebar();
|
||||
if (panel.obj_list()->GetMinHeight() > 200) {
|
||||
wxWindowUpdateLocker noUpdates_sidebar(&panel);
|
||||
panel.obj_list()->SetMinSize(wxSize(-1, 200));
|
||||
// panel.Layout();
|
||||
}
|
||||
update_mode(); // update view mode after fix of the object_list size
|
||||
// to correct later layouts
|
||||
});
|
||||
|
||||
// This makes CallAfter() work
|
||||
Bind(wxEVT_IDLE, [this](wxIdleEvent& event)
|
||||
{
|
||||
std::function<void()> cur_cb{ nullptr };
|
||||
// try to get the mutex. If we can't, just skip this idle event and get the next one.
|
||||
if (!callback_register.try_lock()) return;
|
||||
// pop callback
|
||||
if (m_cb.size() != 0) {
|
||||
cur_cb = m_cb.top();
|
||||
m_cb.pop();
|
||||
}
|
||||
// unlock mutex
|
||||
this->callback_register.unlock();
|
||||
|
||||
try { // call the function if it's not nullptr;
|
||||
if (cur_cb != nullptr) cur_cb();
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
std::cerr << "Exception thrown: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
if (app_config->dirty())
|
||||
app_config->save();
|
||||
|
||||
#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
// ! Temporary workaround for the correct behavior of the Scrolled sidebar panel
|
||||
// Do this "manipulations" only once ( after (re)create of the application )
|
||||
if (plater_ && sidebar().obj_list()->GetMinHeight() > 200)
|
||||
{
|
||||
wxWindowUpdateLocker noUpdates_sidebar(&sidebar());
|
||||
sidebar().obj_list()->SetMinSize(wxSize(-1, 200));
|
||||
|
||||
// !!! to correct later layouts
|
||||
update_mode(); // update view mode after fix of the object_list size
|
||||
}
|
||||
|
||||
if (this->plater() != nullptr)
|
||||
this->obj_manipul()->update_if_dirty();
|
||||
#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
});
|
||||
|
||||
// On OS X the UI tends to freeze in weird ways if modal dialogs(config wizard, update notifications, ...)
|
||||
|
@ -214,6 +191,8 @@ bool GUI_App::OnInit()
|
|||
preset_updater->slic3r_update_notify();
|
||||
}
|
||||
preset_updater->sync(preset_bundle);
|
||||
|
||||
load_current_presets();
|
||||
});
|
||||
|
||||
|
||||
|
@ -291,12 +270,14 @@ void GUI_App::set_label_clr_sys(const wxColour& clr) {
|
|||
|
||||
void GUI_App::recreate_GUI()
|
||||
{
|
||||
std::cerr << "recreate_GUI" << std::endl;
|
||||
// to make sure nobody accesses data from the soon-to-be-destroyed widgets:
|
||||
tabs_list.clear();
|
||||
plater_ = nullptr;
|
||||
|
||||
MainFrame* topwindow = dynamic_cast<MainFrame*>(GetTopWindow());
|
||||
MainFrame* topwindow = mainframe;
|
||||
mainframe = new MainFrame();
|
||||
sidebar().obj_list()->init_objects(); // propagate model objects to object list
|
||||
// update_mode(); // do that later
|
||||
|
||||
if (topwindow) {
|
||||
SetTopWindow(mainframe);
|
||||
topwindow->Destroy();
|
||||
|
@ -304,18 +285,8 @@ void GUI_App::recreate_GUI()
|
|||
|
||||
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
||||
|
||||
CallAfter([this]() {
|
||||
// temporary workaround for the correct behavior of the Scrolled sidebar panel
|
||||
auto& panel = sidebar();
|
||||
if (panel.obj_list()->GetMinHeight() > 200) {
|
||||
wxWindowUpdateLocker noUpdates_sidebar(&panel);
|
||||
panel.obj_list()->SetMinSize(wxSize(-1, 200));
|
||||
// panel.Layout();
|
||||
}
|
||||
update_mode(); // update view mode after fix of the object_list size
|
||||
// to correct later layouts
|
||||
});
|
||||
|
||||
load_current_presets();
|
||||
|
||||
mainframe->Show(true);
|
||||
|
||||
// On OSX the UI was not initialized correctly if the wizard was called
|
||||
|
@ -370,6 +341,33 @@ void GUI_App::update_ui_from_settings()
|
|||
mainframe->update_ui_from_settings();
|
||||
}
|
||||
|
||||
void GUI_App::persist_window_geometry(wxTopLevelWindow *window)
|
||||
{
|
||||
const std::string name = into_u8(window->GetName());
|
||||
|
||||
window->Bind(wxEVT_CLOSE_WINDOW, [=](wxCloseEvent &event) {
|
||||
window_pos_save(window, name);
|
||||
event.Skip();
|
||||
});
|
||||
|
||||
window_pos_restore(window, name);
|
||||
#ifdef _WIN32
|
||||
// On windows, the wxEVT_SHOW is not received if the window is created maximized
|
||||
// cf. https://groups.google.com/forum/#!topic/wx-users/c7ntMt6piRI
|
||||
// so we sanitize the position right away
|
||||
window_pos_sanitize(window);
|
||||
#else
|
||||
// On other platforms on the other hand it's needed to wait before the window is actually on screen
|
||||
// and some initial round of events is complete otherwise position / display index is not reported correctly.
|
||||
window->Bind(wxEVT_SHOW, [=](wxShowEvent &event) {
|
||||
CallAfter([=]() {
|
||||
window_pos_sanitize(window);
|
||||
});
|
||||
event.Skip();
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void GUI_App::load_project(wxWindow *parent, wxString& input_file)
|
||||
{
|
||||
input_file.Clear();
|
||||
|
@ -394,55 +392,6 @@ void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files)
|
|||
dialog.GetPaths(input_files);
|
||||
}
|
||||
|
||||
void GUI_App::CallAfter(std::function<void()> cb)
|
||||
{
|
||||
// set mutex
|
||||
callback_register.lock();
|
||||
// push function onto stack
|
||||
m_cb.emplace(cb);
|
||||
// unset mutex
|
||||
callback_register.unlock();
|
||||
}
|
||||
|
||||
void GUI_App::window_pos_save(wxTopLevelWindow* window, const std::string &name)
|
||||
{
|
||||
if (name.empty()) { return; }
|
||||
const auto config_key = (boost::format("window_%1%") % name).str();
|
||||
|
||||
WindowMetrics metrics = WindowMetrics::from_window(window);
|
||||
app_config->set(config_key, metrics.serialize());
|
||||
app_config->save();
|
||||
}
|
||||
|
||||
void GUI_App::window_pos_restore(wxTopLevelWindow* window, const std::string &name)
|
||||
{
|
||||
if (name.empty()) { return; }
|
||||
const auto config_key = (boost::format("window_%1%") % name).str();
|
||||
|
||||
if (! app_config->has(config_key)) { return; }
|
||||
|
||||
auto metrics = WindowMetrics::deserialize(app_config->get(config_key));
|
||||
if (! metrics) { return; }
|
||||
|
||||
window->SetSize(metrics->get_rect());
|
||||
window->Maximize(metrics->get_maximized());
|
||||
}
|
||||
|
||||
void GUI_App::window_pos_sanitize(wxTopLevelWindow* window)
|
||||
{
|
||||
const auto display_idx = wxDisplay::GetFromWindow(window);
|
||||
if (display_idx == wxNOT_FOUND) { return; }
|
||||
|
||||
const auto display = wxDisplay(display_idx).GetClientArea();
|
||||
|
||||
auto metrics = WindowMetrics::from_window(window);
|
||||
|
||||
metrics.sanitize_for_display(display);
|
||||
if (window->GetScreenRect() != metrics.get_rect()) {
|
||||
window->SetSize(metrics.get_rect());
|
||||
}
|
||||
}
|
||||
|
||||
// select language from the list of installed languages
|
||||
bool GUI_App::select_language( wxArrayString & names,
|
||||
wxArrayLong & identifiers)
|
||||
|
@ -582,10 +531,12 @@ void GUI_App::update_mode()
|
|||
|
||||
const ConfigOptionMode mode = wxGetApp().get_mode();
|
||||
|
||||
obj_list()->get_sizer()->Show(mode == comExpert);
|
||||
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();
|
||||
|
||||
sidebar().update_mode_sizer(mode);
|
||||
|
||||
|
@ -602,15 +553,15 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
|||
auto local_menu = new wxMenu();
|
||||
wxWindowID config_id_base = wxWindow::NewControlId((int)ConfigMenuCnt);
|
||||
|
||||
const auto config_wizard_name = _(ConfigWizard::name().wx_str());
|
||||
const auto config_wizard_name = _(ConfigWizard::name(true).wx_str());
|
||||
const auto config_wizard_tooltip = wxString::Format(_(L("Run %s")), config_wizard_name);
|
||||
// Cmd+, is standard on OS X - what about other operating systems?
|
||||
local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip);
|
||||
local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("Configuration Snapshots")) + dots, _(L("Inspect / activate configuration snapshots")));
|
||||
local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration Snapshot")), _(L("Capture a configuration snapshot")));
|
||||
local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("&Configuration Snapshots")) + dots, _(L("Inspect / activate configuration snapshots")));
|
||||
local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration &Snapshot")), _(L("Capture a configuration snapshot")));
|
||||
// local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates")));
|
||||
local_menu->AppendSeparator();
|
||||
local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("Preferences")) + dots + "\tCtrl+P", _(L("Application preferences")));
|
||||
local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("&Preferences")) + dots + "\tCtrl+P", _(L("Application preferences")));
|
||||
local_menu->AppendSeparator();
|
||||
auto mode_menu = new wxMenu();
|
||||
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _(L("Simple")), _(L("Simple View Mode")));
|
||||
|
@ -619,9 +570,9 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
|||
mode_menu->Check(config_id_base + ConfigMenuModeSimple + get_mode(), true);
|
||||
local_menu->AppendSubMenu(mode_menu, _(L("Mode")), _(L("Slic3r View Mode")));
|
||||
local_menu->AppendSeparator();
|
||||
local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application Language")));
|
||||
local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application &Language")));
|
||||
local_menu->AppendSeparator();
|
||||
local_menu->Append(config_id_base + ConfigMenuFlashFirmware, _(L("Flash printer firmware")), _(L("Upload a firmware image into an Arduino based printer")));
|
||||
local_menu->Append(config_id_base + ConfigMenuFlashFirmware, _(L("Flash printer &firmware")), _(L("Upload a firmware image into an Arduino based printer")));
|
||||
// TODO: for when we're able to flash dictionaries
|
||||
// local_menu->Append(config_id_base + FirmwareMenuDict, _(L("Flash language file")), _(L("Upload a language dictionary file into a Prusa printer")));
|
||||
|
||||
|
@ -722,13 +673,6 @@ bool GUI_App::checked_tab(Tab* tab)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void GUI_App::delete_tab_from_list(Tab* tab)
|
||||
{
|
||||
std::vector<Tab *>::iterator itr = find(tabs_list.begin(), tabs_list.end(), tab);
|
||||
if (itr != tabs_list.end())
|
||||
tabs_list.erase(itr);
|
||||
}
|
||||
|
||||
// Update UI / Tabs to reflect changes in the currently loaded presets
|
||||
void GUI_App::load_current_presets()
|
||||
{
|
||||
|
@ -789,6 +733,48 @@ wxNotebook* GUI_App::tab_panel() const
|
|||
return mainframe->m_tabpanel;
|
||||
}
|
||||
|
||||
void GUI_App::window_pos_save(wxTopLevelWindow* window, const std::string &name)
|
||||
{
|
||||
if (name.empty()) { return; }
|
||||
const auto config_key = (boost::format("window_%1%") % name).str();
|
||||
|
||||
WindowMetrics metrics = WindowMetrics::from_window(window);
|
||||
app_config->set(config_key, metrics.serialize());
|
||||
app_config->save();
|
||||
}
|
||||
|
||||
void GUI_App::window_pos_restore(wxTopLevelWindow* window, const std::string &name)
|
||||
{
|
||||
if (name.empty()) { return; }
|
||||
const auto config_key = (boost::format("window_%1%") % name).str();
|
||||
|
||||
if (! app_config->has(config_key)) { return; }
|
||||
|
||||
auto metrics = WindowMetrics::deserialize(app_config->get(config_key));
|
||||
if (! metrics) { return; }
|
||||
|
||||
window->SetSize(metrics->get_rect());
|
||||
window->Maximize(metrics->get_maximized());
|
||||
}
|
||||
|
||||
void GUI_App::window_pos_sanitize(wxTopLevelWindow* window)
|
||||
{
|
||||
unsigned display_idx = wxDisplay::GetFromWindow(window);
|
||||
wxRect display;
|
||||
if (display_idx == wxNOT_FOUND) {
|
||||
display = wxDisplay(0u).GetClientArea();
|
||||
window->Move(display.GetTopLeft());
|
||||
} else {
|
||||
display = wxDisplay(display_idx).GetClientArea();
|
||||
}
|
||||
|
||||
auto metrics = WindowMetrics::from_window(window);
|
||||
metrics.sanitize_for_display(display);
|
||||
if (window->GetScreenRect() != metrics.get_rect()) {
|
||||
window->SetSize(metrics.get_rect());
|
||||
}
|
||||
}
|
||||
|
||||
// static method accepting a wxWindow object as first parameter
|
||||
// void warning_catcher{
|
||||
// my($self, $message_dialog) = @_;
|
||||
|
|
|
@ -73,11 +73,6 @@ class GUI_App : public wxApp
|
|||
{
|
||||
bool app_conf_exists{ false };
|
||||
|
||||
// Lock to guard the callback stack
|
||||
std::mutex callback_register;
|
||||
// callbacks registered to run during idle event.
|
||||
std::stack<std::function<void()>> m_cb{};
|
||||
|
||||
wxColour m_color_label_modified;
|
||||
wxColour m_color_label_sys;
|
||||
wxColour m_color_label_default;
|
||||
|
@ -121,12 +116,9 @@ public:
|
|||
// wxMessageDialog* message_dialog,
|
||||
const std::string& err);
|
||||
// void notify(/*message*/);
|
||||
void update_ui_from_settings();
|
||||
void CallAfter(std::function<void()> cb);
|
||||
|
||||
void window_pos_save(wxTopLevelWindow* window, const std::string &name);
|
||||
void window_pos_restore(wxTopLevelWindow* window, const std::string &name);
|
||||
void window_pos_sanitize(wxTopLevelWindow* window);
|
||||
void persist_window_geometry(wxTopLevelWindow *window);
|
||||
void update_ui_from_settings();
|
||||
|
||||
bool select_language(wxArrayString & names, wxArrayLong & identifiers);
|
||||
bool load_language();
|
||||
|
@ -141,7 +133,6 @@ public:
|
|||
void add_config_menu(wxMenuBar *menu);
|
||||
bool check_unsaved_changes();
|
||||
bool checked_tab(Tab* tab);
|
||||
void delete_tab_from_list(Tab* tab);
|
||||
void load_current_presets();
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
@ -172,6 +163,10 @@ public:
|
|||
|
||||
PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); }
|
||||
|
||||
private:
|
||||
void window_pos_save(wxTopLevelWindow* window, const std::string &name);
|
||||
void window_pos_restore(wxTopLevelWindow* window, const std::string &name);
|
||||
void window_pos_sanitize(wxTopLevelWindow* window);
|
||||
};
|
||||
DECLARE_APP(GUI_App)
|
||||
|
||||
|
|
|
@ -21,6 +21,23 @@ namespace GUI
|
|||
|
||||
wxDEFINE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
|
||||
|
||||
// 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)
|
||||
|
@ -51,6 +68,7 @@ ObjectList::ObjectList(wxWindow* parent) :
|
|||
create_object_popupmenu(&m_menu_object);
|
||||
create_part_popupmenu(&m_menu_part);
|
||||
create_sla_object_popupmenu(&m_menu_sla_object);
|
||||
create_instance_popupmenu(&m_menu_instance);
|
||||
|
||||
// describe control behavior
|
||||
Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxEvent& event) {
|
||||
|
@ -97,7 +115,7 @@ void ObjectList::create_objects_ctrl()
|
|||
// temporary workaround for the correct behavior of the Scrolled sidebar panel:
|
||||
// 1. set a height of the list to some big value
|
||||
// 2. change it to the normal min value (200) after first whole App updating/layouting
|
||||
SetMinSize(wxSize(-1, 1500)); // #ys_FIXME
|
||||
SetMinSize(wxSize(-1, 3000)); // #ys_FIXME
|
||||
|
||||
m_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_sizer->Add(this, 1, wxGROW | wxLEFT, 20);
|
||||
|
@ -400,15 +418,28 @@ void ObjectList::OnContextMenu(wxDataViewEvent&)
|
|||
|
||||
void ObjectList::show_context_menu()
|
||||
{
|
||||
if (multiple_selection() && selected_instances_of_same_object())
|
||||
{
|
||||
wxGetApp().plater()->PopupMenu(&m_menu_instance);
|
||||
|
||||
wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) {
|
||||
evt.Enable(can_split_instances()); }, m_menu_item_split_instances->GetId());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto item = GetSelection();
|
||||
if (item)
|
||||
{
|
||||
if (!(m_objects_model->GetItemType(item) & (itObject | itVolume)))
|
||||
const ItemType type = m_objects_model->GetItemType(item);
|
||||
if (!(type & (itObject | itVolume | itInstance)))
|
||||
return;
|
||||
wxMenu* menu = m_objects_model->GetParent(item) != wxDataViewItem(0) ? &m_menu_part :
|
||||
|
||||
wxMenu* menu = type & itInstance ? &m_menu_instance :
|
||||
m_objects_model->GetParent(item) != wxDataViewItem(0) ? &m_menu_part :
|
||||
wxGetApp().plater()->printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object;
|
||||
|
||||
append_menu_item_settings(menu);
|
||||
if (!(type & itInstance))
|
||||
append_menu_item_settings(menu);
|
||||
|
||||
wxGetApp().plater()->PopupMenu(menu);
|
||||
|
||||
|
@ -443,15 +474,35 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event)
|
|||
{
|
||||
const wxDataViewItem item(event.GetItem());
|
||||
|
||||
// only allow drags for item, not containers
|
||||
if (multiple_selection() || GetSelection()!=item ||
|
||||
m_objects_model->GetParent(item) == wxDataViewItem(0) ||
|
||||
m_objects_model->GetItemType(item) != itVolume ) {
|
||||
const bool mult_sel = multiple_selection();
|
||||
|
||||
if (mult_sel && !selected_instances_of_same_object() ||
|
||||
!mult_sel && (GetSelection() != item ||
|
||||
m_objects_model->GetParent(item) == wxDataViewItem(0) ) ) {
|
||||
event.Veto();
|
||||
return;
|
||||
}
|
||||
|
||||
const ItemType& type = m_objects_model->GetItemType(item);
|
||||
if (!(type & (itVolume | itInstance))) {
|
||||
event.Veto();
|
||||
return;
|
||||
}
|
||||
|
||||
m_dragged_data.init(m_objects_model->GetObjectIdByItem(item), m_objects_model->GetVolumeIdByItem(item));
|
||||
if (mult_sel)
|
||||
{
|
||||
m_dragged_data.init(m_objects_model->GetObjectIdByItem(item),type);
|
||||
std::set<int>& sub_obj_idxs = m_dragged_data.inst_idxs();
|
||||
wxDataViewItemArray sels;
|
||||
GetSelections(sels);
|
||||
for (auto sel : sels )
|
||||
sub_obj_idxs.insert(m_objects_model->GetInstanceIdByItem(sel));
|
||||
}
|
||||
else
|
||||
m_dragged_data.init(m_objects_model->GetObjectIdByItem(item),
|
||||
type&itVolume ? m_objects_model->GetVolumeIdByItem(item) :
|
||||
m_objects_model->GetInstanceIdByItem(item),
|
||||
type);
|
||||
|
||||
/* Under MSW or OSX, DnD moves an item to the place of another selected item
|
||||
* But under GTK, DnD moves an item between another two items.
|
||||
|
@ -470,31 +521,41 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event)
|
|||
event.SetDragFlags(wxDrag_DefaultMove); // allows both copy and move;
|
||||
}
|
||||
|
||||
bool ObjectList::can_drop(const wxDataViewItem& item) const
|
||||
{
|
||||
return m_dragged_data.type() == itInstance && !item.IsOk() ||
|
||||
m_dragged_data.type() == itVolume && item.IsOk() &&
|
||||
m_objects_model->GetItemType(item) == itVolume &&
|
||||
m_dragged_data.obj_idx() == m_objects_model->GetObjectIdByItem(item);
|
||||
}
|
||||
|
||||
void ObjectList::OnDropPossible(wxDataViewEvent &event)
|
||||
{
|
||||
wxDataViewItem item(event.GetItem());
|
||||
const wxDataViewItem& item = event.GetItem();
|
||||
|
||||
// only allow drags for item or background, not containers
|
||||
if (!item.IsOk() ||
|
||||
m_objects_model->GetParent(item) == wxDataViewItem(0) ||
|
||||
m_objects_model->GetItemType(item) != itVolume ||
|
||||
m_dragged_data.obj_idx() != m_objects_model->GetObjectIdByItem(item))
|
||||
if (!can_drop(item))
|
||||
event.Veto();
|
||||
}
|
||||
|
||||
void ObjectList::OnDrop(wxDataViewEvent &event)
|
||||
{
|
||||
wxDataViewItem item(event.GetItem());
|
||||
const wxDataViewItem& item = event.GetItem();
|
||||
|
||||
if (!item.IsOk() || m_objects_model->GetParent(item) == wxDataViewItem(0) ||
|
||||
m_objects_model->GetItemType(item) != itVolume ||
|
||||
m_dragged_data.obj_idx() != m_objects_model->GetObjectIdByItem(item)) {
|
||||
if (!can_drop(item))
|
||||
{
|
||||
event.Veto();
|
||||
m_dragged_data.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
const int from_volume_id = m_dragged_data.vol_idx();
|
||||
if (m_dragged_data.type() == itInstance)
|
||||
{
|
||||
instances_to_separated_object(m_dragged_data.obj_idx(), m_dragged_data.inst_idxs());
|
||||
m_dragged_data.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
const int from_volume_id = m_dragged_data.sub_obj_idx();
|
||||
int to_volume_id = m_objects_model->GetVolumeIdByItem(item);
|
||||
|
||||
// It looks like a fixed in current version of the wxWidgets
|
||||
|
@ -506,7 +567,7 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
|
|||
// if (to_volume_id > from_volume_id) to_volume_id--;
|
||||
// #endif // __WXGTK__
|
||||
|
||||
auto& volumes = (*m_objects)[/*m_selected_object_id*/m_dragged_data.obj_idx()]->volumes;
|
||||
auto& volumes = (*m_objects)[m_dragged_data.obj_idx()]->volumes;
|
||||
auto delta = to_volume_id < from_volume_id ? -1 : 1;
|
||||
int cnt = 0;
|
||||
for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id += delta, cnt++)
|
||||
|
@ -516,7 +577,7 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
|
|||
m_objects_model->GetParent(item)));
|
||||
|
||||
m_parts_changed = true;
|
||||
parts_changed(/*m_selected_object_id*/m_dragged_data.obj_idx());
|
||||
parts_changed(m_dragged_data.obj_idx());
|
||||
|
||||
m_dragged_data.clear();
|
||||
}
|
||||
|
@ -524,9 +585,9 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
|
|||
|
||||
// Context Menu
|
||||
|
||||
std::vector<std::string> get_options(const bool is_part, const bool is_sla)
|
||||
std::vector<std::string> ObjectList::get_options(const bool is_part)
|
||||
{
|
||||
if (is_sla) {
|
||||
if (wxGetApp().plater()->printer_technology() == ptSLA) {
|
||||
SLAPrintObjectConfig full_sla_config;
|
||||
auto options = full_sla_config.keys();
|
||||
options.erase(find(options.begin(), options.end(), "layer_height"));
|
||||
|
@ -542,17 +603,36 @@ std::vector<std::string> get_options(const bool is_part, const bool is_sla)
|
|||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
std::vector<std::string> get_options(const bool is_part)
|
||||
|
||||
const std::vector<std::string>& ObjectList::get_options_for_bundle(const wxString& bundle_name)
|
||||
{
|
||||
return get_options(is_part, wxGetApp().plater()->printer_technology() == ptSLA);
|
||||
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;
|
||||
}
|
||||
#if 0
|
||||
// if "Quick menu" is selected
|
||||
FreqSettingsBundle& bundle_quick = wxGetApp().plater()->printer_technology() == ptSLA ?
|
||||
m_freq_settings_sla: m_freq_settings_fff;
|
||||
|
||||
for (auto& it : bundle_quick)
|
||||
{
|
||||
if ( bundle_name == wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)) )
|
||||
return it.second;
|
||||
}
|
||||
#endif
|
||||
|
||||
static std::vector<std::string> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
// category -> vector ( option ; label )
|
||||
typedef std::map< std::string, std::vector< std::pair<std::string, std::string> > > settings_menu_hierarchy;
|
||||
void get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part, const bool is_sla)
|
||||
void ObjectList::get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part)
|
||||
{
|
||||
auto options = get_options(is_part, is_sla);
|
||||
auto options = get_options(is_part);
|
||||
|
||||
auto extruders_cnt = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA ? 1 :
|
||||
wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
||||
|
@ -577,11 +657,6 @@ void get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part
|
|||
}
|
||||
}
|
||||
|
||||
void get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part)
|
||||
{
|
||||
get_options_menu(settings_menu, is_part, wxGetApp().plater()->printer_technology() == ptSLA);
|
||||
}
|
||||
|
||||
void ObjectList::get_settings_choice(const wxString& category_name)
|
||||
{
|
||||
wxArrayString names;
|
||||
|
@ -615,10 +690,59 @@ void ObjectList::get_settings_choice(const wxString& category_name)
|
|||
if (wxGetSelectedChoices(selections, _(L("Select showing settings")), category_name, names) == -1)
|
||||
return;
|
||||
|
||||
const int selection_cnt = selections.size();
|
||||
#if 0
|
||||
if (selection_cnt > 0)
|
||||
{
|
||||
// Add selected items to the "Quick menu"
|
||||
FreqSettingsBundle& freq_settings = wxGetApp().plater()->printer_technology() == ptSLA ?
|
||||
m_freq_settings_sla : m_freq_settings_fff;
|
||||
bool changed_existing = false;
|
||||
|
||||
std::vector<std::string> tmp_freq_cat = {};
|
||||
|
||||
for (auto& cat : freq_settings)
|
||||
{
|
||||
if (_(cat.first) == category_name)
|
||||
{
|
||||
std::vector<std::string>& freq_settings_category = cat.second;
|
||||
freq_settings_category.clear();
|
||||
freq_settings_category.reserve(selection_cnt);
|
||||
for (auto sel : selections)
|
||||
freq_settings_category.push_back((*settings_list)[sel].first);
|
||||
|
||||
changed_existing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed_existing)
|
||||
{
|
||||
// Create new "Quick menu" item
|
||||
for (auto& cat : settings_menu)
|
||||
{
|
||||
if (_(cat.first) == category_name)
|
||||
{
|
||||
freq_settings[cat.first] = std::vector<std::string> {};
|
||||
|
||||
std::vector<std::string>& freq_settings_category = freq_settings.find(cat.first)->second;
|
||||
freq_settings_category.reserve(selection_cnt);
|
||||
for (auto sel : selections)
|
||||
freq_settings_category.push_back((*settings_list)[sel].first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector <std::string> selected_options;
|
||||
selected_options.reserve(selection_cnt);
|
||||
for (auto sel : selections)
|
||||
selected_options.push_back((*settings_list)[sel].first);
|
||||
|
||||
const DynamicPrintConfig& from_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
|
||||
for (auto& setting : (*settings_list))
|
||||
{
|
||||
auto& opt_key = setting.first;
|
||||
|
@ -627,12 +751,52 @@ void ObjectList::get_settings_choice(const wxString& category_name)
|
|||
m_config->erase(opt_key);
|
||||
|
||||
if (find(opt_keys.begin(), opt_keys.end(), opt_key) == opt_keys.end() &&
|
||||
find(selected_options.begin(), selected_options.end(), opt_key) != selected_options.end())
|
||||
m_config->set_key_value(opt_key, m_default_config->option(opt_key)->clone());
|
||||
find(selected_options.begin(), selected_options.end(), opt_key) != selected_options.end()) {
|
||||
const ConfigOption* option = from_config.option(opt_key);
|
||||
if (!option) {
|
||||
// if current option doesn't exist in prints.get_edited_preset(),
|
||||
// get it from m_default_config
|
||||
if (m_default_config) delete m_default_config;
|
||||
m_default_config = DynamicPrintConfig::new_from_defaults_keys(get_options(false));
|
||||
option = m_default_config->option(opt_key);
|
||||
}
|
||||
m_config->set_key_value(opt_key, option->clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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();
|
||||
|
||||
const DynamicPrintConfig& from_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
for (auto& opt_key : options)
|
||||
{
|
||||
if (find(opt_keys.begin(), opt_keys.end(), opt_key) == opt_keys.end()) {
|
||||
const ConfigOption* option = from_config.option(opt_key);
|
||||
if (!option) {
|
||||
// if current option doesn't exist in prints.get_edited_preset(),
|
||||
// get it from m_default_config
|
||||
if (m_default_config) delete m_default_config;
|
||||
m_default_config = DynamicPrintConfig::new_from_defaults_keys(get_options(false));
|
||||
option = m_default_config->option(opt_key);
|
||||
}
|
||||
m_config->set_key_value(opt_key, option->clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Add settings item for object
|
||||
update_settings_item();
|
||||
}
|
||||
|
||||
void ObjectList::update_settings_item()
|
||||
{
|
||||
auto item = GetSelection();
|
||||
if (item) {
|
||||
if (m_objects_model->GetItemType(item) == itInstance)
|
||||
|
@ -644,7 +808,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();
|
||||
}
|
||||
}
|
||||
|
@ -652,9 +816,11 @@ void ObjectList::get_settings_choice(const wxString& category_name)
|
|||
void ObjectList::append_menu_item_add_generic(wxMenuItem* menu, const int type) {
|
||||
auto sub_menu = new wxMenu;
|
||||
|
||||
if (wxGetApp().get_mode() == comExpert) {
|
||||
append_menu_item(sub_menu, wxID_ANY, _(L("Load")) + " " + dots, "",
|
||||
[this, type](wxCommandEvent&) { load_subobject(type); }, "", menu->GetMenu());
|
||||
sub_menu->AppendSeparator();
|
||||
}
|
||||
|
||||
std::vector<std::string> menu_items = { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") };
|
||||
for (auto& item : menu_items) {
|
||||
|
@ -680,21 +846,25 @@ void ObjectList::append_menu_items_add_volume(wxMenu* menu)
|
|||
menu->Destroy(settings_id);
|
||||
}
|
||||
|
||||
if (wxGetApp().get_mode() == comSimple)
|
||||
const ConfigOptionMode mode = wxGetApp().get_mode();
|
||||
|
||||
if (mode < comExpert)
|
||||
{
|
||||
append_menu_item(menu, wxID_ANY, _(L("Add part")), "",
|
||||
[this](wxCommandEvent&) { load_subobject(ModelVolume::MODEL_PART); }, *m_bmp_vector[ModelVolume::MODEL_PART]);
|
||||
}
|
||||
if (mode == comSimple) {
|
||||
append_menu_item(menu, wxID_ANY, _(L("Add support enforcer")), "",
|
||||
[this](wxCommandEvent&) { load_generic_subobject(_(L("Box")).ToUTF8().data(), ModelVolume::SUPPORT_ENFORCER); },
|
||||
[this](wxCommandEvent&) { load_generic_subobject(_(L("Box")).ToUTF8().data(), ModelVolume::SUPPORT_ENFORCER); },
|
||||
*m_bmp_vector[ModelVolume::SUPPORT_ENFORCER]);
|
||||
append_menu_item(menu, wxID_ANY, _(L("Add support blocker")), "",
|
||||
[this](wxCommandEvent&) { load_generic_subobject(_(L("Box")).ToUTF8().data(), ModelVolume::SUPPORT_BLOCKER); },
|
||||
[this](wxCommandEvent&) { load_generic_subobject(_(L("Box")).ToUTF8().data(), ModelVolume::SUPPORT_BLOCKER); },
|
||||
*m_bmp_vector[ModelVolume::SUPPORT_BLOCKER]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (int type = 0; type < menu_object_types_items.size(); type++)
|
||||
|
||||
for (int type = mode == comExpert ? 0 : 1 ; type < menu_object_types_items.size(); type++)
|
||||
{
|
||||
auto& item = menu_object_types_items[type];
|
||||
|
||||
|
@ -712,23 +882,70 @@ 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;
|
||||
|
||||
auto menu_item = new wxMenuItem(menu, wxID_ANY, _(L("Add settings")));
|
||||
menu_item->SetBitmap(m_bmp_cog);
|
||||
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);
|
||||
}
|
||||
#if 0
|
||||
for (auto& it : m_freq_settings_fff)
|
||||
{
|
||||
settings_id = menu->FindItem(wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)));
|
||||
if (settings_id != wxNOT_FOUND)
|
||||
menu->Destroy(settings_id);
|
||||
}
|
||||
for (auto& it : m_freq_settings_sla)
|
||||
{
|
||||
settings_id = menu->FindItem(wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)));
|
||||
if (settings_id != wxNOT_FOUND)
|
||||
menu->Destroy(settings_id);
|
||||
}
|
||||
#endif
|
||||
menu->DestroySeparators(); // delete old separators
|
||||
|
||||
const auto sel_vol = get_selected_model_volume();
|
||||
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);
|
||||
|
@ -741,20 +958,27 @@ wxMenuItem* ObjectList::append_menu_item_change_type(wxMenu* menu)
|
|||
|
||||
}
|
||||
|
||||
wxMenuItem* ObjectList::append_menu_item_instance_to_object(wxMenu* menu)
|
||||
{
|
||||
return append_menu_item(menu, wxID_ANY, _(L("Set as a Separated Object")), "",
|
||||
[this](wxCommandEvent&) { split_instances(); }, "", menu);
|
||||
}
|
||||
|
||||
void ObjectList::create_object_popupmenu(wxMenu *menu)
|
||||
{
|
||||
append_menu_items_add_volume(menu);
|
||||
|
||||
// Split object to parts
|
||||
menu->AppendSeparator();
|
||||
m_menu_item_split = append_menu_item_split(menu);
|
||||
|
||||
// Settings
|
||||
menu->AppendSeparator();
|
||||
|
||||
// rest of a object_menu will be added later in:
|
||||
// - append_menu_items_add_volume() -> for "Add (volumes)"
|
||||
// - append_menu_item_settings() -> for "Add (settings)"
|
||||
}
|
||||
|
||||
void ObjectList::create_sla_object_popupmenu(wxMenu *menu)
|
||||
{
|
||||
// rest of a object_sla_menu will be added later in:
|
||||
// - append_menu_item_settings() -> for "Add (settings)"
|
||||
}
|
||||
|
||||
void ObjectList::create_part_popupmenu(wxMenu *menu)
|
||||
|
@ -765,8 +989,13 @@ 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)
|
||||
{
|
||||
m_menu_item_split_instances = append_menu_item_instance_to_object(menu);
|
||||
}
|
||||
|
||||
wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu)
|
||||
|
@ -786,6 +1015,39 @@ wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu)
|
|||
return menu;
|
||||
}
|
||||
|
||||
void ObjectList::create_freq_settings_popupmenu(wxMenu *menu)
|
||||
{
|
||||
// Add default settings bundles
|
||||
const FreqSettingsBundle& bundle = wxGetApp().plater()->printer_technology() == ptFFF ?
|
||||
FREQ_SETTINGS_BUNDLE_FFF : FREQ_SETTINGS_BUNDLE_SLA;
|
||||
|
||||
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);
|
||||
}
|
||||
#if 0
|
||||
// Add "Quick" settings bundles
|
||||
const FreqSettingsBundle& bundle_quick = wxGetApp().plater()->printer_technology() == ptFFF ?
|
||||
m_freq_settings_fff : m_freq_settings_sla;
|
||||
|
||||
for (auto& it : bundle_quick) {
|
||||
if (it.first.empty() || it.first == "Extruders" && extruders_cnt == 1)
|
||||
continue;
|
||||
|
||||
append_menu_item(menu, wxID_ANY, wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)), "",
|
||||
[menu, this](wxCommandEvent& event) { get_freq_settings_choice(menu->GetLabel(event.GetId())); },
|
||||
CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ObjectList::update_opt_keys(t_config_option_keys& opt_keys)
|
||||
{
|
||||
auto full_current_opts = get_options(false);
|
||||
|
@ -845,7 +1107,9 @@ void ObjectList::load_part( ModelObject* model_object,
|
|||
delta = model_object->origin_translation - object->origin_translation;
|
||||
}
|
||||
for (auto volume : object->volumes) {
|
||||
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
volume->center_geometry();
|
||||
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
volume->translate(delta);
|
||||
auto new_volume = model_object->add_volume(*volume);
|
||||
new_volume->set_type(static_cast<ModelVolume::Type>(type));
|
||||
|
@ -898,7 +1162,9 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const int
|
|||
#if !ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
new_volume->set_offset(Vec3d(0.0, 0.0, (*m_objects)[obj_idx]->origin_translation(2) - mesh.stl.stats.min(2)));
|
||||
#endif // !ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
new_volume->center_geometry();
|
||||
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
#if ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||
|
@ -1105,11 +1371,33 @@ bool ObjectList::is_splittable()
|
|||
if (!get_volume_by_item(item, volume) || !volume)
|
||||
return false;
|
||||
|
||||
TriangleMeshPtrs meshptrs = volume->mesh.split();
|
||||
bool splittable = meshptrs.size() > 1;
|
||||
for (TriangleMesh* m : meshptrs) { delete m; }
|
||||
int splittable = volume->is_splittable();
|
||||
if (splittable == -1) {
|
||||
splittable = (int)volume->mesh.has_multiple_patches();
|
||||
volume->set_splittable(splittable);
|
||||
}
|
||||
return splittable != 0;
|
||||
}
|
||||
|
||||
return splittable;
|
||||
bool ObjectList::selected_instances_of_same_object()
|
||||
{
|
||||
wxDataViewItemArray sels;
|
||||
GetSelections(sels);
|
||||
|
||||
const int obj_idx = m_objects_model->GetObjectIdByItem(sels.front());
|
||||
|
||||
for (auto item : sels) {
|
||||
if (! (m_objects_model->GetItemType(item) & itInstance) ||
|
||||
obj_idx != m_objects_model->GetObjectIdByItem(item))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ObjectList::can_split_instances()
|
||||
{
|
||||
const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||
return selection.is_multiple_full_instance() || selection.is_single_full_instance();
|
||||
}
|
||||
|
||||
void ObjectList::part_settings_changed()
|
||||
|
@ -1185,9 +1473,6 @@ void ObjectList::part_selection_changed()
|
|||
m_config = &(*m_objects)[obj_idx_]->config;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_default_config) delete m_default_config;
|
||||
m_default_config = DynamicPrintConfig::new_from_defaults_keys(get_options(is_part));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1415,7 +1700,7 @@ bool ObjectList::multiple_selection() const
|
|||
|
||||
void ObjectList::update_selections()
|
||||
{
|
||||
auto& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||
const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||
wxDataViewItemArray sels;
|
||||
|
||||
// We doesn't update selection if SettingsItem for the current object/part is selected
|
||||
|
@ -1501,7 +1786,7 @@ void ObjectList::update_selections()
|
|||
|
||||
void ObjectList::update_selections_on_canvas()
|
||||
{
|
||||
auto& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||
GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||
|
||||
const int sel_cnt = GetSelectedItemsCount();
|
||||
if (sel_cnt == 0) {
|
||||
|
@ -1725,6 +2010,48 @@ void ObjectList::update_settings_items()
|
|||
UnselectAll();
|
||||
}
|
||||
|
||||
void ObjectList::update_object_menu()
|
||||
{
|
||||
append_menu_items_add_volume(&m_menu_object);
|
||||
}
|
||||
|
||||
void ObjectList::instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idxs)
|
||||
{
|
||||
// create new object from selected instance
|
||||
ModelObject* model_object = (*m_objects)[obj_idx]->get_model()->add_object(*(*m_objects)[obj_idx]);
|
||||
for (int inst_idx = model_object->instances.size() - 1; inst_idx >= 0; inst_idx--)
|
||||
{
|
||||
if (find(inst_idxs.begin(), inst_idxs.end(), inst_idx) != inst_idxs.end())
|
||||
continue;
|
||||
model_object->delete_instance(inst_idx);
|
||||
}
|
||||
|
||||
// Add new object to the object_list
|
||||
add_object_to_list(m_objects->size() - 1);
|
||||
|
||||
for (std::set<int>::const_reverse_iterator it = inst_idxs.rbegin(); it != inst_idxs.rend(); ++it)
|
||||
{
|
||||
// delete selected instance from the object
|
||||
del_subobject_from_object(obj_idx, *it, itInstance);
|
||||
delete_instance_from_list(obj_idx, *it);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectList::split_instances()
|
||||
{
|
||||
const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||
const int obj_idx = selection.get_object_idx();
|
||||
if (obj_idx == -1)
|
||||
return;
|
||||
|
||||
const int inst_idx = selection.get_instance_idx();
|
||||
const std::set<int> inst_idxs = inst_idx < 0 ?
|
||||
selection.get_instance_idxs() :
|
||||
std::set<int>{ inst_idx };
|
||||
|
||||
instances_to_separated_object(obj_idx, inst_idxs);
|
||||
}
|
||||
|
||||
void ObjectList::ItemValueChanged(wxDataViewEvent &event)
|
||||
{
|
||||
if (event.GetColumn() == 0)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
class wxBoxSizer;
|
||||
class wxMenuItem;
|
||||
class PrusaObjectDataViewModel;
|
||||
class PrusaMenu;
|
||||
|
||||
namespace Slic3r {
|
||||
class ConfigOptionsGroup;
|
||||
|
@ -24,6 +25,11 @@ class ModelVolume;
|
|||
// FIXME: broken build on mac os because of this is missing:
|
||||
typedef std::vector<std::string> t_config_option_keys;
|
||||
|
||||
typedef std::map<std::string, std::vector<std::string>> FreqSettingsBundle;
|
||||
|
||||
// category -> vector ( option ; label )
|
||||
typedef std::map< std::string, std::vector< std::pair<std::string, std::string> > > settings_menu_hierarchy;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
wxDECLARE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
|
||||
|
@ -56,22 +62,38 @@ class ObjectList : public wxDataViewCtrl
|
|||
|
||||
struct dragged_item_data
|
||||
{
|
||||
void init(const int obj_idx, const int vol_idx) {
|
||||
void init(const int obj_idx, const int subobj_idx, const ItemType type) {
|
||||
m_obj_idx = obj_idx;
|
||||
m_vol_idx = vol_idx;
|
||||
m_type = type;
|
||||
if (m_type&itVolume)
|
||||
m_vol_idx = subobj_idx;
|
||||
else
|
||||
m_inst_idxs.insert(subobj_idx);
|
||||
}
|
||||
|
||||
void init(const int obj_idx, const ItemType type) {
|
||||
m_obj_idx = obj_idx;
|
||||
m_type = type;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
m_obj_idx = -1;
|
||||
m_vol_idx = -1;
|
||||
m_vol_idx = -1;
|
||||
m_inst_idxs.clear();
|
||||
m_type = itUndef;
|
||||
}
|
||||
|
||||
int obj_idx() const { return m_obj_idx; }
|
||||
int vol_idx() const { return m_vol_idx; }
|
||||
int sub_obj_idx() const { return m_vol_idx; }
|
||||
ItemType type() const { return m_type; }
|
||||
std::set<int>& inst_idxs() { return m_inst_idxs; }
|
||||
|
||||
private:
|
||||
int m_obj_idx = -1;
|
||||
int m_vol_idx = -1;
|
||||
std::set<int> m_inst_idxs{};
|
||||
ItemType m_type = itUndef;
|
||||
|
||||
} m_dragged_data;
|
||||
|
||||
wxBoxSizer *m_sizer {nullptr};
|
||||
|
@ -88,12 +110,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;
|
||||
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 };
|
||||
|
||||
std::vector<wxBitmap*> m_bmp_vector;
|
||||
|
||||
|
@ -110,6 +134,11 @@ class ObjectList : public wxDataViewCtrl
|
|||
|
||||
int m_selected_row = 0;
|
||||
|
||||
#if 0
|
||||
FreqSettingsBundle m_freq_settings_fff;
|
||||
FreqSettingsBundle m_freq_settings_sla;
|
||||
#endif
|
||||
|
||||
public:
|
||||
ObjectList(wxWindow* parent);
|
||||
~ObjectList();
|
||||
|
@ -143,15 +172,21 @@ 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* append_menu_item_split(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_settings(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_change_type(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu);
|
||||
void create_object_popupmenu(wxMenu *menu);
|
||||
void create_sla_object_popupmenu(wxMenu*menu);
|
||||
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);
|
||||
|
||||
|
@ -166,6 +201,8 @@ public:
|
|||
void split();
|
||||
bool get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume);
|
||||
bool is_splittable();
|
||||
bool selected_instances_of_same_object();
|
||||
bool can_split_instances();
|
||||
|
||||
wxPoint get_mouse_position_in_control();
|
||||
wxBoxSizer* get_sizer() {return m_sizer;}
|
||||
|
@ -221,6 +258,10 @@ public:
|
|||
void last_volume_is_deleted(const int obj_idx);
|
||||
bool has_multi_part_objects();
|
||||
void update_settings_items();
|
||||
void update_object_menu();
|
||||
|
||||
void instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idx);
|
||||
void split_instances();
|
||||
|
||||
private:
|
||||
void OnChar(wxKeyEvent& event);
|
||||
|
@ -229,9 +270,14 @@ private:
|
|||
void OnBeginDrag(wxDataViewEvent &event);
|
||||
void OnDropPossible(wxDataViewEvent &event);
|
||||
void OnDrop(wxDataViewEvent &event);
|
||||
bool can_drop(const wxDataViewItem& item) const ;
|
||||
|
||||
void ItemValueChanged(wxDataViewEvent &event);
|
||||
void OnEditingDone(wxDataViewEvent &event);
|
||||
|
||||
std::vector<std::string> get_options(const bool is_part);
|
||||
const std::vector<std::string>& get_options_for_bundle(const wxString& bundle_name);
|
||||
void get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -39,19 +39,13 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||
change_rotation_value(new_value);
|
||||
else if (param == "scale")
|
||||
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
{
|
||||
change_scale_value(new_value);
|
||||
update_settings_value(wxGetApp().plater()->canvas3D()->get_selection());
|
||||
}
|
||||
#else
|
||||
change_scale_value(new_value);
|
||||
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
else if (param == "size")
|
||||
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
{
|
||||
change_size_value(new_value);
|
||||
update_settings_value(wxGetApp().plater()->canvas3D()->get_selection());
|
||||
}
|
||||
#else
|
||||
change_size_value(new_value);
|
||||
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
|
@ -249,9 +243,9 @@ void ObjectManipulation::UpdateAndShow(const bool show)
|
|||
|
||||
void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection)
|
||||
{
|
||||
m_new_move_label_string = L("Position:");
|
||||
m_new_rotate_label_string = L("Rotation:");
|
||||
m_new_scale_label_string = L("Scale factors:");
|
||||
m_new_move_label_string = L("Position");
|
||||
m_new_rotate_label_string = L("Rotation");
|
||||
m_new_scale_label_string = L("Scale factors");
|
||||
if (selection.is_single_full_instance())
|
||||
{
|
||||
// all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one
|
||||
|
@ -267,7 +261,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele
|
|||
bool changed_box = false;
|
||||
if (!m_cache.instance.matches_object(obj_idx))
|
||||
{
|
||||
m_cache.instance.set(obj_idx, instance_idx, (*wxGetApp().model_objects())[obj_idx]->raw_mesh().bounding_box().size());
|
||||
m_cache.instance.set(obj_idx, instance_idx, (*wxGetApp().model_objects())[obj_idx]->raw_mesh_bounding_box().size());
|
||||
changed_box = true;
|
||||
}
|
||||
if (changed_box || !m_cache.instance.matches_instance(instance_idx) || !m_cache.scale.isApprox(100.0 * m_new_scale))
|
||||
|
@ -278,7 +272,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele
|
|||
m_new_size = Vec3d::Zero();
|
||||
#else
|
||||
if ((0 <= obj_idx) && (obj_idx < (int)wxGetApp().model_objects()->size()))
|
||||
m_new_size = volume->get_instance_transformation().get_matrix(true, true) * (*wxGetApp().model_objects())[obj_idx]->raw_mesh().bounding_box().size();
|
||||
m_new_size = volume->get_instance_transformation().get_matrix(true, true) * (*wxGetApp().model_objects())[obj_idx]->raw_mesh_bounding_box().size();
|
||||
else
|
||||
// this should never happen
|
||||
m_new_size = Vec3d::Zero();
|
||||
|
@ -297,8 +291,8 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele
|
|||
m_new_rotation = Vec3d::Zero();
|
||||
m_new_scale = Vec3d(1.0, 1.0, 1.0);
|
||||
m_new_size = box.size();
|
||||
m_new_rotate_label_string = L("Rotate:");
|
||||
m_new_scale_label_string = L("Scale:");
|
||||
m_new_rotate_label_string = L("Rotate");
|
||||
m_new_scale_label_string = L("Scale");
|
||||
m_new_enabled = true;
|
||||
}
|
||||
else if (selection.is_single_modifier() || selection.is_single_volume())
|
||||
|
@ -318,40 +312,39 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele
|
|||
else if (wxGetApp().obj_list()->multiple_selection())
|
||||
{
|
||||
reset_settings_value();
|
||||
m_new_move_label_string = L("Translate:");
|
||||
m_new_rotate_label_string = L("Rotate:");
|
||||
m_new_scale_label_string = L("Scale:");
|
||||
m_new_move_label_string = L("Translate");
|
||||
m_new_rotate_label_string = L("Rotate");
|
||||
m_new_scale_label_string = L("Scale");
|
||||
m_new_size = selection.get_bounding_box().size();
|
||||
m_new_enabled = true;
|
||||
}
|
||||
else
|
||||
reset_settings_value();
|
||||
|
||||
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
update_if_dirty();
|
||||
#else
|
||||
m_dirty = true;
|
||||
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
}
|
||||
|
||||
void ObjectManipulation::update_if_dirty()
|
||||
{
|
||||
if (!m_dirty)
|
||||
return;
|
||||
|
||||
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
if (m_cache.move_label_string != _(m_new_move_label_string))
|
||||
if (m_cache.move_label_string != _(m_new_move_label_string)+ ":")
|
||||
{
|
||||
m_cache.move_label_string = _(m_new_move_label_string);
|
||||
m_cache.move_label_string = _(m_new_move_label_string)+ ":";
|
||||
m_move_Label->SetLabel(m_cache.move_label_string);
|
||||
}
|
||||
|
||||
if (m_cache.rotate_label_string != _(m_new_rotate_label_string))
|
||||
if (m_cache.rotate_label_string != _(m_new_rotate_label_string)+ ":")
|
||||
{
|
||||
m_cache.rotate_label_string = _(m_new_rotate_label_string);
|
||||
m_cache.rotate_label_string = _(m_new_rotate_label_string)+ ":";
|
||||
m_rotate_Label->SetLabel(m_cache.rotate_label_string);
|
||||
}
|
||||
|
||||
if (m_cache.scale_label_string != _(m_new_scale_label_string))
|
||||
if (m_cache.scale_label_string != _(m_new_scale_label_string)+ ":")
|
||||
{
|
||||
m_cache.scale_label_string = _(m_new_scale_label_string);
|
||||
m_cache.scale_label_string = _(m_new_scale_label_string)+ ":";
|
||||
m_scale_Label->SetLabel(m_cache.scale_label_string);
|
||||
}
|
||||
|
||||
|
@ -414,9 +407,6 @@ void ObjectManipulation::update_if_dirty()
|
|||
else
|
||||
m_og->disable();
|
||||
#else
|
||||
if (! m_dirty)
|
||||
return;
|
||||
|
||||
m_move_Label->SetLabel(_(m_new_move_label_string));
|
||||
m_rotate_Label->SetLabel(_(m_new_rotate_label_string));
|
||||
m_scale_Label->SetLabel(_(m_new_scale_label_string));
|
||||
|
@ -446,9 +436,9 @@ void ObjectManipulation::update_if_dirty()
|
|||
m_og->enable();
|
||||
else
|
||||
m_og->disable();
|
||||
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
|
||||
m_dirty = false;
|
||||
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
}
|
||||
|
||||
void ObjectManipulation::reset_settings_value()
|
||||
|
@ -461,9 +451,7 @@ void ObjectManipulation::reset_settings_value()
|
|||
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
m_cache.instance.reset();
|
||||
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
m_dirty = true;
|
||||
#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
}
|
||||
|
||||
void ObjectManipulation::change_position_value(const Vec3d& position)
|
||||
|
@ -554,18 +542,16 @@ void ObjectManipulation::change_size_value(const Vec3d& size)
|
|||
{
|
||||
const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||
|
||||
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
Vec3d ref_size = m_cache.size;
|
||||
#else
|
||||
Vec3d ref_size = m_cache_size;
|
||||
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
if (selection.is_single_full_instance())
|
||||
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
if (selection.is_single_volume() || selection.is_single_modifier())
|
||||
{
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
ref_size = volume->bounding_box.size();
|
||||
}
|
||||
else if (selection.is_single_full_instance())
|
||||
ref_size = m_cache.instance.box_size;
|
||||
|
||||
#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
Vec3d scale = 100.0 * Vec3d(size(0) / ref_size(0), size(1) / ref_size(1), size(2) / ref_size(2));
|
||||
Vec3d scaling_factor = scale;
|
||||
|
||||
|
@ -596,6 +582,12 @@ void ObjectManipulation::change_size_value(const Vec3d& size)
|
|||
|
||||
m_cache.size = size;
|
||||
#else
|
||||
if (selection.is_single_full_instance())
|
||||
{
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
ref_size = volume->bounding_box.size();
|
||||
}
|
||||
|
||||
change_scale_value(100.0 * Vec3d(size(0) / ref_size(0), size(1) / ref_size(1), size(2) / ref_size(2)));
|
||||
#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
}
|
||||
|
|
|
@ -62,10 +62,8 @@ class ObjectManipulation : public OG_Settings
|
|||
wxStaticText* m_scale_Label = nullptr;
|
||||
wxStaticText* m_rotate_Label = nullptr;
|
||||
|
||||
#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
// Needs to be updated from OnIdle?
|
||||
bool m_dirty = false;
|
||||
#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION
|
||||
// Cached labels for the delayed update, not localized!
|
||||
std::string m_new_move_label_string;
|
||||
std::string m_new_rotate_label_string;
|
||||
|
@ -75,7 +73,7 @@ class ObjectManipulation : public OG_Settings
|
|||
Vec3d m_new_scale;
|
||||
Vec3d m_new_size;
|
||||
bool m_new_enabled;
|
||||
bool m_uniform_scale {false};
|
||||
bool m_uniform_scale {true};
|
||||
PrusaLockButton* m_lock_bnt{ nullptr };
|
||||
|
||||
public:
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
View3D::View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
|
||||
View3D::View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
|
||||
: m_canvas_widget(nullptr)
|
||||
, m_canvas(nullptr)
|
||||
#if !ENABLE_IMGUI
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -120,19 +120,28 @@ boost::optional<WindowMetrics> WindowMetrics::deserialize(const std::string &str
|
|||
void WindowMetrics::sanitize_for_display(const wxRect &screen_rect)
|
||||
{
|
||||
rect = rect.Intersect(screen_rect);
|
||||
|
||||
// Prevent the window from going too far towards the right and/or bottom edge
|
||||
// It's hardcoded here that the threshold is 80% of the screen size
|
||||
rect.x = std::min(rect.x, screen_rect.x + 4*screen_rect.width/5);
|
||||
rect.y = std::min(rect.y, screen_rect.y + 4*screen_rect.height/5);
|
||||
}
|
||||
|
||||
std::string WindowMetrics::serialize()
|
||||
std::string WindowMetrics::serialize() const
|
||||
{
|
||||
return (boost::format("%1%; %2%; %3%; %4%; %5%")
|
||||
% rect.GetX()
|
||||
% rect.GetY()
|
||||
% rect.GetWidth()
|
||||
% rect.GetHeight()
|
||||
% rect.x
|
||||
% rect.y
|
||||
% rect.width
|
||||
% rect.height
|
||||
% static_cast<int>(maximized)
|
||||
).str();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream &os, const WindowMetrics& metrics)
|
||||
{
|
||||
return os << '(' << metrics.serialize() << ')';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
|
@ -149,9 +150,11 @@ public:
|
|||
bool get_maximized() const { return maximized; }
|
||||
|
||||
void sanitize_for_display(const wxRect &screen_rect);
|
||||
std::string serialize();
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream &os, const WindowMetrics& metrics);
|
||||
|
||||
|
||||
}}
|
||||
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
#define _(s) Slic3r::GUI::I18N::translate((s))
|
||||
#endif /* _ */
|
||||
|
||||
#ifndef _CTX
|
||||
#define _CTX(s, ctx) Slic3r::GUI::I18N::translate((s), (ctx))
|
||||
#endif /* _ */
|
||||
|
||||
#ifndef L
|
||||
// !!! If you needed to translate some wxString,
|
||||
// !!! please use _(L(string))
|
||||
|
@ -21,6 +25,7 @@
|
|||
#define slic3r_GUI_I18N_hpp_
|
||||
|
||||
#include <wx/intl.h>
|
||||
#include <wx/version.h>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
|
@ -29,7 +34,20 @@ namespace I18N {
|
|||
inline wxString translate(const wchar_t *s) { return wxGetTranslation(s); }
|
||||
inline wxString translate(const std::string &s) { return wxGetTranslation(wxString(s.c_str(), wxConvUTF8)); }
|
||||
inline wxString translate(const std::wstring &s) { return wxGetTranslation(s.c_str()); }
|
||||
}
|
||||
|
||||
#if wxCHECK_VERSION(3, 1, 1)
|
||||
#define _wxGetTranslation_ctx(S, CTX) wxGetTranslation((S), wxEmptyString, (CTX))
|
||||
#else
|
||||
#define _wxGetTranslation_ctx(S, CTX) ((void)(CTX), wxGetTranslation((S)))
|
||||
#endif
|
||||
|
||||
inline wxString translate(const char *s, const char* ctx) { return _wxGetTranslation_ctx(wxString(s, wxConvUTF8), ctx); }
|
||||
inline wxString translate(const wchar_t *s, const char* ctx) { return _wxGetTranslation_ctx(s, ctx); }
|
||||
inline wxString translate(const std::string &s, const char* ctx) { return _wxGetTranslation_ctx(wxString(s.c_str(), wxConvUTF8), ctx); }
|
||||
inline wxString translate(const std::wstring &s, const char* ctx) { return _wxGetTranslation_ctx(s.c_str(), ctx); }
|
||||
|
||||
#undef _wxGetTranslation_ctx
|
||||
}
|
||||
|
||||
// Return translated std::string as a wxString
|
||||
wxString L_str(const std::string &str);
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
@ -12,6 +14,8 @@
|
|||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include <imgui/imgui_internal.h>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "GUI.hpp"
|
||||
|
@ -22,7 +26,9 @@ namespace GUI {
|
|||
|
||||
ImGuiWrapper::ImGuiWrapper()
|
||||
: m_font_texture(0)
|
||||
, m_style_scaling(1.0)
|
||||
, m_mouse_buttons(0)
|
||||
, m_disabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -36,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;
|
||||
}
|
||||
|
@ -59,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();
|
||||
|
@ -90,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)
|
||||
|
@ -154,6 +161,26 @@ void ImGuiWrapper::text(const wxString &label)
|
|||
ImGui::Text(label_utf8.c_str(), NULL);
|
||||
}
|
||||
|
||||
void ImGuiWrapper::disabled_begin(bool disabled)
|
||||
{
|
||||
wxCHECK_RET(!m_disabled, "ImGUI: Unbalanced disabled_begin() call");
|
||||
|
||||
if (disabled) {
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
|
||||
m_disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiWrapper::disabled_end()
|
||||
{
|
||||
if (m_disabled) {
|
||||
ImGui::PopItemFlag();
|
||||
ImGui::PopStyleVar();
|
||||
m_disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::want_mouse() const
|
||||
{
|
||||
return ImGui::GetIO().WantCaptureMouse;
|
||||
|
@ -175,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,8 +21,9 @@ class ImGuiWrapper
|
|||
|
||||
FontsMap m_fonts;
|
||||
unsigned m_font_texture;
|
||||
|
||||
float m_style_scaling;
|
||||
unsigned m_mouse_buttons;
|
||||
bool m_disabled;
|
||||
|
||||
public:
|
||||
ImGuiWrapper();
|
||||
|
@ -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();
|
||||
|
@ -50,11 +52,15 @@ public:
|
|||
bool checkbox(const wxString &label, bool &value);
|
||||
void text(const wxString &label);
|
||||
|
||||
void disabled_begin(bool disabled);
|
||||
void disabled_end();
|
||||
|
||||
bool want_mouse() const;
|
||||
bool want_keyboard() const;
|
||||
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);
|
||||
|
|
|
@ -101,7 +101,7 @@ void KBShortcutsDialog::fill_shortcuts()
|
|||
main_shortcuts.push_back(Shortcut(ctrl+"O" ,L("Open project STL/OBJ/AMF/3MF with config, delete bed")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"I" ,L("Import STL/OBJ/AMF/3MF without config, keep bed")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"L" ,L("Load Config from .ini/amf/3mf/gcode")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"G" ,L("Export Gcode")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"G" ,L("Export G-code")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"S" ,L("Save project (3MF)")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+alt+"L" ,L("Load Config from .ini/amf/3mf/gcode and merge")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"R" ,L("(Re)slice")));
|
||||
|
@ -110,8 +110,8 @@ void KBShortcutsDialog::fill_shortcuts()
|
|||
main_shortcuts.push_back(Shortcut(ctrl+"1" ,L("Select Plater Tab")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+alt+"U" ,L("Quick slice and Save as")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"2" ,L("Select Print Settings Tab")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"3" ,L("Select Filament Setting Tab")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"4" ,L("Select Printer Setting Tab")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"3" ,L("Select Filament Settings Tab")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"4" ,L("Select Printer Settings Tab")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"5" ,L("Switch to 3D")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"6" ,L("Switch to Preview")));
|
||||
main_shortcuts.push_back(Shortcut(ctrl+"P" ,L("Preferences")));
|
||||
|
@ -131,7 +131,7 @@ void KBShortcutsDialog::fill_shortcuts()
|
|||
plater_shortcuts.push_back(Shortcut("A", L("Arrange")));
|
||||
plater_shortcuts.push_back(Shortcut(ctrl+"A", L("Select All objects")));
|
||||
plater_shortcuts.push_back(Shortcut("Del", L("Delete selected")));
|
||||
plater_shortcuts.push_back(Shortcut(ctrl+"Del", L("Delete all")));
|
||||
plater_shortcuts.push_back(Shortcut(ctrl+"Del", L("Delete All")));
|
||||
plater_shortcuts.push_back(Shortcut("M", L("Gizmo move")));
|
||||
plater_shortcuts.push_back(Shortcut("S", L("Gizmo scale")));
|
||||
plater_shortcuts.push_back(Shortcut("R", L("Gizmo rotate")));
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace Slic3r {
|
|||
namespace GUI {
|
||||
|
||||
MainFrame::MainFrame() :
|
||||
wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE),
|
||||
wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"),
|
||||
m_printhost_queue_dlg(new PrintHostQueueDialog(this))
|
||||
{
|
||||
// Load the icon either from the exe, or from the ico file.
|
||||
|
@ -48,7 +48,7 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
|
|||
// initialize status bar
|
||||
m_statusbar = new ProgressStatusBar(this);
|
||||
m_statusbar->embed(this);
|
||||
m_statusbar->set_status_text(_(L("Version ")) +
|
||||
m_statusbar->set_status_text(_(L("Version")) + " " +
|
||||
SLIC3R_VERSION +
|
||||
_(L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases")));
|
||||
|
||||
|
@ -70,8 +70,14 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
|
|||
sizer->SetSizeHints(this);
|
||||
SetSizer(sizer);
|
||||
Fit();
|
||||
#ifdef __APPLE__
|
||||
// Using SetMinSize() on Mac messes up the window position in some cases
|
||||
// cf. https://groups.google.com/forum/#!topic/wx-users/yUKPBBfXWO0
|
||||
SetSize(wxSize(760, 490));
|
||||
#else
|
||||
SetMinSize(wxSize(760, 490));
|
||||
SetSize(GetMinSize());
|
||||
#endif
|
||||
Layout();
|
||||
|
||||
// declare events
|
||||
|
@ -80,8 +86,7 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
|
|||
event.Veto();
|
||||
return;
|
||||
}
|
||||
// save window size
|
||||
wxGetApp().window_pos_save(this, "mainframe");
|
||||
|
||||
// Save the slic3r.ini.Usually the ini file is saved from "on idle" callback,
|
||||
// but in rare cases it may not have been called yet.
|
||||
wxGetApp().app_config->save();
|
||||
|
@ -89,24 +94,22 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
|
|||
// m_plater->print = undef;
|
||||
_3DScene::remove_all_canvases();
|
||||
// Slic3r::GUI::deregister_on_request_update_callback();
|
||||
|
||||
// set to null tabs and a platter
|
||||
// to avoid any manipulations with them from App->wxEVT_IDLE after of the mainframe closing
|
||||
wxGetApp().tabs_list.clear();
|
||||
wxGetApp().plater_ = nullptr;
|
||||
|
||||
// propagate event
|
||||
event.Skip();
|
||||
});
|
||||
|
||||
// NB: Restoring the window position is done in a two-phase manner here,
|
||||
// first the saved position is restored as-is and validation is done after the window is shown
|
||||
// and initial round of events is complete, because on some platforms that is the only way
|
||||
// to get an accurate window position & size.
|
||||
wxGetApp().window_pos_restore(this, "mainframe");
|
||||
Bind(wxEVT_SHOW, [this](wxShowEvent&) {
|
||||
CallAfter([this]() {
|
||||
wxGetApp().window_pos_sanitize(this);
|
||||
});
|
||||
});
|
||||
wxGetApp().persist_window_geometry(this);
|
||||
|
||||
update_ui_from_settings();
|
||||
update_ui_from_settings(); // FIXME (?)
|
||||
}
|
||||
|
||||
|
||||
void MainFrame::init_tabpanel()
|
||||
{
|
||||
m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL);
|
||||
|
@ -197,6 +200,12 @@ bool MainFrame::can_export_gcode() const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool MainFrame::can_slice() const
|
||||
{
|
||||
bool bg_proc = wxGetApp().app_config->get("background_processing") == "1";
|
||||
return (m_plater != nullptr) ? !m_plater->model().objects.empty() && !bg_proc : false;
|
||||
}
|
||||
|
||||
bool MainFrame::can_change_view() const
|
||||
{
|
||||
int page_id = m_tabpanel->GetSelection();
|
||||
|
@ -227,60 +236,60 @@ void MainFrame::init_menubar()
|
|||
// File menu
|
||||
wxMenu* fileMenu = new wxMenu;
|
||||
{
|
||||
wxMenuItem* item_open = append_menu_item(fileMenu, wxID_ANY, _(L("Open…\tCtrl+O")), _(L("Open a project file")),
|
||||
wxMenuItem* item_open = append_menu_item(fileMenu, wxID_ANY, _(L("&Open")) + dots + "\tCtrl+O", _(L("Open a project file")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "brick_add.png");
|
||||
wxMenuItem* item_save = append_menu_item(fileMenu, wxID_ANY, _(L("Save\tCtrl+S")), _(L("Save current project file")),
|
||||
wxMenuItem* item_save = append_menu_item(fileMenu, wxID_ANY, _(L("&Save")) + "\tCtrl+S", _(L("Save current project file")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename())); }, "disk.png");
|
||||
wxMenuItem* item_save_as = append_menu_item(fileMenu, wxID_ANY, _(L("Save as…\tCtrl+Alt+S")), _(L("Save current project file as")),
|
||||
wxMenuItem* item_save_as = append_menu_item(fileMenu, wxID_ANY, _(L("Save &as")) + dots + "\tCtrl+Alt+S", _(L("Save current project file as")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, "disk.png");
|
||||
|
||||
fileMenu->AppendSeparator();
|
||||
|
||||
wxMenu* import_menu = new wxMenu();
|
||||
wxMenuItem* item_import_model = append_menu_item(import_menu, wxID_ANY, _(L("Import STL/OBJ/AMF/3MF…\tCtrl+I")), _(L("Load a model")),
|
||||
wxMenuItem* item_import_model = append_menu_item(import_menu, wxID_ANY, _(L("Import STL/OBJ/AM&F/3MF")) + dots + "\tCtrl+I", _(L("Load a model")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, "brick_add.png");
|
||||
import_menu->AppendSeparator();
|
||||
append_menu_item(import_menu, wxID_ANY, _(L("Import Config…\tCtrl+L")), _(L("Load exported configuration file")),
|
||||
append_menu_item(import_menu, wxID_ANY, _(L("Import &Config")) + dots + "\tCtrl+L", _(L("Load exported configuration file")),
|
||||
[this](wxCommandEvent&) { load_config_file(); }, "plugin_add.png");
|
||||
append_menu_item(import_menu, wxID_ANY, _(L("Import Config from project…\tCtrl+Alt+L")), _(L("Load configuration from project file")),
|
||||
append_menu_item(import_menu, wxID_ANY, _(L("Import Config from &project")) + dots +"\tCtrl+Alt+L", _(L("Load configuration from project file")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->extract_config_from_project(); }, "plugin_add.png");
|
||||
import_menu->AppendSeparator();
|
||||
append_menu_item(import_menu, wxID_ANY, _(L("Import Config Bundle…")), _(L("Load presets from a bundle")),
|
||||
append_menu_item(import_menu, wxID_ANY, _(L("Import Config &Bundle")) + dots, _(L("Load presets from a bundle")),
|
||||
[this](wxCommandEvent&) { load_configbundle(); }, "lorry_add.png");
|
||||
append_submenu(fileMenu, import_menu, wxID_ANY, _(L("Import")), _(L("")));
|
||||
append_submenu(fileMenu, import_menu, wxID_ANY, _(L("&Import")), "");
|
||||
|
||||
wxMenu* export_menu = new wxMenu();
|
||||
wxMenuItem* item_export_gcode = append_menu_item(export_menu, wxID_ANY, _(L("Export G-code…\tCtrl+G")), _(L("Export current plate as G-code")),
|
||||
wxMenuItem* item_export_gcode = append_menu_item(export_menu, wxID_ANY, _(L("Export &G-code")) + dots +"\tCtrl+G", _(L("Export current plate as G-code")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(); }, "cog_go.png");
|
||||
export_menu->AppendSeparator();
|
||||
wxMenuItem* item_export_stl = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as STL…")), _(L("Export current plate as STL")),
|
||||
wxMenuItem* item_export_stl = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &STL")) + dots, _(L("Export current plate as STL")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, "brick_go.png");
|
||||
wxMenuItem* item_export_amf = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as AMF…")), _(L("Export current plate as AMF")),
|
||||
wxMenuItem* item_export_amf = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &AMF")) + dots, _(L("Export current plate as AMF")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, "brick_go.png");
|
||||
export_menu->AppendSeparator();
|
||||
append_menu_item(export_menu, wxID_ANY, _(L("Export Config…\tCtrl+E")), _(L("Export current configuration to file")),
|
||||
append_menu_item(export_menu, wxID_ANY, _(L("Export &Config")) +dots +"\tCtrl+E", _(L("Export current configuration to file")),
|
||||
[this](wxCommandEvent&) { export_config(); }, "plugin_go.png");
|
||||
append_menu_item(export_menu, wxID_ANY, _(L("Export Config Bundle…")), _(L("Export all presets to file")),
|
||||
append_menu_item(export_menu, wxID_ANY, _(L("Export Config &Bundle")) + dots, _(L("Export all presets to file")),
|
||||
[this](wxCommandEvent&) { export_configbundle(); }, "lorry_go.png");
|
||||
append_submenu(fileMenu, export_menu, wxID_ANY, _(L("Export")), _(L("")));
|
||||
append_submenu(fileMenu, export_menu, wxID_ANY, _(L("&Export")), "");
|
||||
|
||||
fileMenu->AppendSeparator();
|
||||
|
||||
#if 0
|
||||
m_menu_item_repeat = nullptr;
|
||||
append_menu_item(fileMenu, wxID_ANY, _(L("Quick Slice…\tCtrl+U")), _(L("Slice a file into a G-code")),
|
||||
append_menu_item(fileMenu, wxID_ANY, _(L("Quick Slice")) +dots+ "\tCtrl+U", _(L("Slice a file into a G-code")),
|
||||
[this](wxCommandEvent&) {
|
||||
wxTheApp->CallAfter([this]() {
|
||||
quick_slice();
|
||||
m_menu_item_repeat->Enable(is_last_input_file());
|
||||
}); }, "cog_go.png");
|
||||
append_menu_item(fileMenu, wxID_ANY, _(L("Quick Slice and Save As…\tCtrl+Alt+U")), _(L("Slice a file into a G-code, save as")),
|
||||
append_menu_item(fileMenu, wxID_ANY, _(L("Quick Slice and Save As")) +dots +"\tCtrl+Alt+U", _(L("Slice a file into a G-code, save as")),
|
||||
[this](wxCommandEvent&) {
|
||||
wxTheApp->CallAfter([this]() {
|
||||
quick_slice(qsSaveAs);
|
||||
m_menu_item_repeat->Enable(is_last_input_file());
|
||||
}); }, "cog_go.png");
|
||||
m_menu_item_repeat = append_menu_item(fileMenu, wxID_ANY, _(L("Repeat Last Quick Slice\tCtrl+Shift+U")), _(L("Repeat last quick slice")),
|
||||
m_menu_item_repeat = append_menu_item(fileMenu, wxID_ANY, _(L("Repeat Last Quick Slice")) +"\tCtrl+Shift+U", _(L("Repeat last quick slice")),
|
||||
[this](wxCommandEvent&) {
|
||||
wxTheApp->CallAfter([this]() {
|
||||
quick_slice(qsReslice);
|
||||
|
@ -288,13 +297,13 @@ void MainFrame::init_menubar()
|
|||
m_menu_item_repeat->Enable(false);
|
||||
fileMenu->AppendSeparator();
|
||||
#endif
|
||||
m_menu_item_reslice_now = append_menu_item(fileMenu, wxID_ANY, _(L("(Re)Slice Now\tCtrl+R")), _(L("Start new slicing process")),
|
||||
m_menu_item_reslice_now = append_menu_item(fileMenu, wxID_ANY, _(L("(Re)Slice &Now")) + "\tCtrl+R", _(L("Start new slicing process")),
|
||||
[this](wxCommandEvent&) { reslice_now(); }, "shape_handles.png");
|
||||
fileMenu->AppendSeparator();
|
||||
append_menu_item(fileMenu, wxID_ANY, _(L("Repair STL file…")), _(L("Automatically repair an STL file")),
|
||||
append_menu_item(fileMenu, wxID_ANY, _(L("&Repair STL file")) + dots, _(L("Automatically repair an STL file")),
|
||||
[this](wxCommandEvent&) { repair_stl(); }, "wrench.png");
|
||||
fileMenu->AppendSeparator();
|
||||
append_menu_item(fileMenu, wxID_EXIT, _(L("Quit")), _(L("Quit Slic3r")),
|
||||
append_menu_item(fileMenu, wxID_EXIT, _(L("&Quit")), _(L("Quit Slic3r")),
|
||||
[this](wxCommandEvent&) { Close(false); });
|
||||
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_plater != nullptr); }, item_open->GetId());
|
||||
|
@ -304,6 +313,7 @@ void MainFrame::init_menubar()
|
|||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_export_gcode()); }, item_export_gcode->GetId());
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_export_model()); }, item_export_stl->GetId());
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_export_model()); }, item_export_amf->GetId());
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_slice()); }, m_menu_item_reslice_now->GetId());
|
||||
}
|
||||
|
||||
// Edit menu
|
||||
|
@ -311,12 +321,12 @@ void MainFrame::init_menubar()
|
|||
if (m_plater != nullptr)
|
||||
{
|
||||
editMenu = new wxMenu();
|
||||
wxMenuItem* item_select_all = append_menu_item(editMenu, wxID_ANY, L("Select all\tCtrl+A"), L("Selects all objects"),
|
||||
wxMenuItem* item_select_all = append_menu_item(editMenu, wxID_ANY, _(L("&Select all")) + "\tCtrl+A", _(L("Selects all objects")),
|
||||
[this](wxCommandEvent&) { m_plater->select_all(); }, "");
|
||||
editMenu->AppendSeparator();
|
||||
wxMenuItem* item_delete_sel = append_menu_item(editMenu, wxID_ANY, L("Delete selected\tDel"), L("Deletes the current selection"),
|
||||
wxMenuItem* item_delete_sel = append_menu_item(editMenu, wxID_ANY, _(L("&Delete selected")) + "\tDel", _(L("Deletes the current selection")),
|
||||
[this](wxCommandEvent&) { m_plater->remove_selected(); }, "");
|
||||
wxMenuItem* item_delete_all = append_menu_item(editMenu, wxID_ANY, L("Delete all\tCtrl+Del"), L("Deletes all objects"),
|
||||
wxMenuItem* item_delete_all = append_menu_item(editMenu, wxID_ANY, _(L("Delete &all")) + "\tCtrl+Del", _(L("Deletes all objects")),
|
||||
[this](wxCommandEvent&) { m_plater->reset(); }, "");
|
||||
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_select()); }, item_select_all->GetId());
|
||||
|
@ -329,24 +339,24 @@ void MainFrame::init_menubar()
|
|||
{
|
||||
size_t tab_offset = 0;
|
||||
if (m_plater) {
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 1, L("Plater Tab\tCtrl+1"), L("Show the plater"),
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 1, _(L("&Plater Tab")) + "\tCtrl+1", _(L("Show the plater")),
|
||||
[this](wxCommandEvent&) { select_tab(0); }, "application_view_tile.png");
|
||||
tab_offset += 1;
|
||||
}
|
||||
if (tab_offset > 0) {
|
||||
windowMenu->AppendSeparator();
|
||||
}
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 2, L("Print Settings Tab\tCtrl+2"), L("Show the print settings"),
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 2, _(L("P&rint Settings Tab")) + "\tCtrl+2", _(L("Show the print settings")),
|
||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png");
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 3, L("Filament Settings Tab\tCtrl+3"), L("Show the filament settings"),
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 3, _(L("&Filament Settings Tab")) + "\tCtrl+3", _(L("Show the filament settings")),
|
||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool.png");
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 4, L("Printer Settings Tab\tCtrl+4"), L("Show the printer settings"),
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 4, _(L("Print&er Settings Tab")) + "\tCtrl+4", _(L("Show the printer settings")),
|
||||
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png");
|
||||
if (m_plater) {
|
||||
windowMenu->AppendSeparator();
|
||||
wxMenuItem* item_3d = append_menu_item(windowMenu, wxID_HIGHEST + 5, L("3D\tCtrl+5"), L("Show the 3D editing view"),
|
||||
wxMenuItem* item_3d = append_menu_item(windowMenu, wxID_HIGHEST + 5, _(L("3&D")) + "\tCtrl+5", _(L("Show the 3D editing view")),
|
||||
[this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, "");
|
||||
wxMenuItem* item_preview = append_menu_item(windowMenu, wxID_HIGHEST + 6, L("Preview\tCtrl+6"), L("Show the 3D slices preview"),
|
||||
wxMenuItem* item_preview = append_menu_item(windowMenu, wxID_HIGHEST + 6, _(L("Pre&view")) + "\tCtrl+6", _(L("Show the 3D slices preview")),
|
||||
[this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, "");
|
||||
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_3d->GetId());
|
||||
|
@ -367,7 +377,7 @@ void MainFrame::init_menubar()
|
|||
#endif // _WIN32
|
||||
|
||||
windowMenu->AppendSeparator();
|
||||
append_menu_item(windowMenu, wxID_ANY, L("Print Host Upload Queue\tCtrl+J"), L("Display the Print Host Upload Queue window"),
|
||||
append_menu_item(windowMenu, wxID_ANY, _(L("Print &Host Upload Queue")) + "\tCtrl+J", _(L("Display the Print Host Upload Queue window")),
|
||||
[this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "arrow_up.png");
|
||||
}
|
||||
|
||||
|
@ -378,14 +388,14 @@ void MainFrame::init_menubar()
|
|||
// \xA0 is a non-breaing space. It is entered here to spoil the automatic accelerators,
|
||||
// as the simple numeric accelerators spoil all numeric data entry.
|
||||
// The camera control accelerators are captured by GLCanvas3D::on_char().
|
||||
wxMenuItem* item_iso = append_menu_item(viewMenu, wxID_ANY, _(L("Iso")) + "\t\xA0" + "0", _(L("Iso View")), [this](wxCommandEvent&) { select_view("iso"); });
|
||||
wxMenuItem* item_iso = append_menu_item(viewMenu, wxID_ANY, _(L("&Iso")) + "\t\xA0" + "0", _(L("Iso View")), [this](wxCommandEvent&) { select_view("iso"); });
|
||||
viewMenu->AppendSeparator();
|
||||
wxMenuItem* item_top = append_menu_item(viewMenu, wxID_ANY, _(L("Top")) + "\t\xA0" + "1", _(L("Top View")), [this](wxCommandEvent&) { select_view("top"); });
|
||||
wxMenuItem* item_bottom = append_menu_item(viewMenu, wxID_ANY, _(L("Bottom")) + "\t\xA0" + "2", _(L("Bottom View")), [this](wxCommandEvent&) { select_view("bottom"); });
|
||||
wxMenuItem* item_front = append_menu_item(viewMenu, wxID_ANY, _(L("Front")) + "\t\xA0" + "3", _(L("Front View")), [this](wxCommandEvent&) { select_view("front"); });
|
||||
wxMenuItem* item_rear = append_menu_item(viewMenu, wxID_ANY, _(L("Rear")) + "\t\xA0" + "4", _(L("Rear View")), [this](wxCommandEvent&) { select_view("rear"); });
|
||||
wxMenuItem* item_left = append_menu_item(viewMenu, wxID_ANY, _(L("Left")) + "\t\xA0" + "5", _(L("Left View")), [this](wxCommandEvent&) { select_view("left"); });
|
||||
wxMenuItem* item_right = append_menu_item(viewMenu, wxID_ANY, _(L("Right")) + "\t\xA0" + "6", _(L("Right View")), [this](wxCommandEvent&) { select_view("right"); });
|
||||
wxMenuItem* item_top = append_menu_item(viewMenu, wxID_ANY, _(L("&Top")) + "\t\xA0" + "1", _(L("Top View")), [this](wxCommandEvent&) { select_view("top"); });
|
||||
wxMenuItem* item_bottom = append_menu_item(viewMenu, wxID_ANY, _(L("&Bottom")) + "\t\xA0" + "2", _(L("Bottom View")), [this](wxCommandEvent&) { select_view("bottom"); });
|
||||
wxMenuItem* item_front = append_menu_item(viewMenu, wxID_ANY, _(L("&Front")) + "\t\xA0" + "3", _(L("Front View")), [this](wxCommandEvent&) { select_view("front"); });
|
||||
wxMenuItem* item_rear = append_menu_item(viewMenu, wxID_ANY, _(L("R&ear")) + "\t\xA0" + "4", _(L("Rear View")), [this](wxCommandEvent&) { select_view("rear"); });
|
||||
wxMenuItem* item_left = append_menu_item(viewMenu, wxID_ANY, _(L("&Left")) + "\t\xA0" + "5", _(L("Left View")), [this](wxCommandEvent&) { select_view("left"); });
|
||||
wxMenuItem* item_right = append_menu_item(viewMenu, wxID_ANY, _(L("&Right")) + "\t\xA0" + "6", _(L("Right View")), [this](wxCommandEvent&) { select_view("right"); });
|
||||
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_iso->GetId());
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_top->GetId());
|
||||
|
@ -399,29 +409,29 @@ void MainFrame::init_menubar()
|
|||
// Help menu
|
||||
auto helpMenu = new wxMenu();
|
||||
{
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("Prusa 3D Drivers")), _(L("Open the Prusa3D drivers download page in your browser")),
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("Prusa 3D &Drivers")), _(L("Open the Prusa3D drivers download page in your browser")),
|
||||
[this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://www.prusa3d.com/drivers/"); });
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("Prusa Edition Releases")), _(L("Open the Prusa Edition releases page in your browser")),
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("Prusa Edition &Releases")), _(L("Open the Prusa Edition releases page in your browser")),
|
||||
[this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://github.com/prusa3d/slic3r/releases"); });
|
||||
//# my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", "Check for new Slic3r versions", sub{
|
||||
//# wxTheApp->check_version(1);
|
||||
//# });
|
||||
//# $versioncheck->Enable(wxTheApp->have_version_check);
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("Slic3r Website")), _(L("Open the Slic3r website in your browser")),
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("Slic3r &Website")), _(L("Open the Slic3r website in your browser")),
|
||||
[this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://slic3r.org/"); });
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("Slic3r Manual")), _(L("Open the Slic3r manual in your browser")),
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("Slic3r &Manual")), _(L("Open the Slic3r manual in your browser")),
|
||||
[this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://manual.slic3r.org/"); });
|
||||
helpMenu->AppendSeparator();
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("System Info")), _(L("Show system information")),
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("System &Info")), _(L("Show system information")),
|
||||
[this](wxCommandEvent&) { wxGetApp().system_info(); });
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("Show Configuration Folder")), _(L("Show user configuration folder (datadir)")),
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("Show &Configuration Folder")), _(L("Show user configuration folder (datadir)")),
|
||||
[this](wxCommandEvent&) { Slic3r::GUI::desktop_open_datadir_folder(); });
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("Report an Issue")), _(L("Report an issue on the Slic3r Prusa Edition")),
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("Report an I&ssue")), _(L("Report an issue on the Slic3r Prusa Edition")),
|
||||
[this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://github.com/prusa3d/slic3r/issues/new"); });
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("About Slic3r")), _(L("Show about dialog")),
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("&About Slic3r")), _(L("Show about dialog")),
|
||||
[this](wxCommandEvent&) { Slic3r::GUI::about(); });
|
||||
helpMenu->AppendSeparator();
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("Keyboard Shortcuts")) + "\t\xA0?", _(L("Show the list of the keyboard shortcuts")),
|
||||
append_menu_item(helpMenu, wxID_ANY, _(L("&Keyboard Shortcuts")) + "\t\xA0?", _(L("Show the list of the keyboard shortcuts")),
|
||||
[this](wxCommandEvent&) { wxGetApp().keyboard_shortcuts(); });
|
||||
}
|
||||
|
||||
|
@ -429,17 +439,18 @@ void MainFrame::init_menubar()
|
|||
// assign menubar to frame after appending items, otherwise special items
|
||||
// will not be handled correctly
|
||||
auto menubar = new wxMenuBar();
|
||||
menubar->Append(fileMenu, L("&File"));
|
||||
if (editMenu) menubar->Append(editMenu, L("&Edit"));
|
||||
menubar->Append(windowMenu, L("&Window"));
|
||||
if (viewMenu) menubar->Append(viewMenu, L("&View"));
|
||||
menubar->Append(fileMenu, _(L("&File")));
|
||||
if (editMenu) menubar->Append(editMenu, _(L("&Edit")));
|
||||
menubar->Append(windowMenu, _(L("&Window")));
|
||||
if (viewMenu) menubar->Append(viewMenu, _(L("&View")));
|
||||
// Add additional menus from C++
|
||||
wxGetApp().add_config_menu(menubar);
|
||||
menubar->Append(helpMenu, L("&Help"));
|
||||
menubar->Append(helpMenu, _(L("&Help")));
|
||||
SetMenuBar(menubar);
|
||||
|
||||
#ifdef __APPLE__
|
||||
// This fixes a bug (?) on Mac OS where the quit command doesn't emit window close events
|
||||
// This fixes a bug on Mac OS where the quit command doesn't emit window close events
|
||||
// wx bug: https://trac.wxwidgets.org/ticket/18328
|
||||
wxMenu *apple_menu = menubar->OSXGetAppleMenu();
|
||||
if (apple_menu != nullptr) {
|
||||
apple_menu->Bind(wxEVT_MENU, [this](wxCommandEvent &) {
|
||||
|
@ -550,7 +561,7 @@ void MainFrame::quick_slice(const int qs)
|
|||
}
|
||||
|
||||
// show processbar dialog
|
||||
m_progress_dialog = new wxProgressDialog(_(L("Slicing…")), _(L("Processing ")) + input_file_basename + "…",
|
||||
m_progress_dialog = new wxProgressDialog(_(L("Slicing")) + dots, _(L("Processing ")) + input_file_basename + "…",
|
||||
100, this, 4);
|
||||
m_progress_dialog->Pulse();
|
||||
{
|
||||
|
@ -846,7 +857,7 @@ void MainFrame::on_value_changed(wxCommandEvent& event)
|
|||
void MainFrame::update_ui_from_settings()
|
||||
{
|
||||
bool bp_on = wxGetApp().app_config->get("background_processing") == "1";
|
||||
m_menu_item_reslice_now->Enable(!bp_on);
|
||||
// m_menu_item_reslice_now->Enable(!bp_on);
|
||||
m_plater->sidebar().show_reslice(!bp_on);
|
||||
m_plater->sidebar().Layout();
|
||||
if (m_plater)
|
||||
|
|
|
@ -62,6 +62,7 @@ class MainFrame : public wxFrame
|
|||
bool can_save() const;
|
||||
bool can_export_model() const;
|
||||
bool can_export_gcode() const;
|
||||
bool can_slice() const;
|
||||
bool can_change_view() const;
|
||||
bool can_select() const;
|
||||
bool can_delete() const;
|
||||
|
|
|
@ -231,11 +231,11 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
|
|||
wxSizer* sizer_tmp = sizer;
|
||||
// add label if any
|
||||
if (option.label != "") {
|
||||
wxString str_label = _(option.label);
|
||||
// wxString str_label = _(option.label);
|
||||
//! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1
|
||||
// wxString str_label = (option.label == "Top" || option.label == "Bottom") ?
|
||||
// wxGETTEXT_IN_CONTEXT("Layers", wxString(option.label.c_str()):
|
||||
// L_str(option.label);
|
||||
wxString str_label = (option.label == "Top" || option.label == "Bottom") ?
|
||||
_CTX(option.label, "Layers") :
|
||||
_(option.label);
|
||||
label = new wxStaticText(parent(), wxID_ANY, str_label + ":", wxDefaultPosition, wxDefaultSize);
|
||||
label->SetFont(label_font);
|
||||
sizer_tmp->Add(label, 0, /*wxALIGN_RIGHT |*/ wxALIGN_CENTER_VERTICAL, 0);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef slic3r_OptionsGroup_hpp_
|
||||
#define slic3r_OptionsGroup_hpp_
|
||||
|
||||
#include <wx/wx.h>
|
||||
//#include <wx/wx.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/settings.h>
|
||||
//#include <wx/window.h>
|
||||
|
@ -11,7 +11,7 @@
|
|||
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
// #include "libslic3r/libslic3r.h"
|
||||
|
||||
#include "Field.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
|
|
@ -114,7 +114,7 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
|
|||
grid_sizer->AddGrowableCol(3, 1);
|
||||
|
||||
auto init_info_label = [parent, grid_sizer](wxStaticText **info_label, wxString text_label) {
|
||||
auto *text = new wxStaticText(parent, wxID_ANY, text_label);
|
||||
auto *text = new wxStaticText(parent, wxID_ANY, text_label+":");
|
||||
text->SetFont(wxGetApp().small_font());
|
||||
*info_label = new wxStaticText(parent, wxID_ANY, "");
|
||||
(*info_label)->SetFont(wxGetApp().small_font());
|
||||
|
@ -122,13 +122,13 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
|
|||
grid_sizer->Add(*info_label, 0);
|
||||
};
|
||||
|
||||
init_info_label(&info_size, _(L("Size:")));
|
||||
init_info_label(&info_volume, _(L("Volume:")));
|
||||
init_info_label(&info_facets, _(L("Facets:")));
|
||||
init_info_label(&info_materials, _(L("Materials:")));
|
||||
init_info_label(&info_size, _(L("Size")));
|
||||
init_info_label(&info_volume, _(L("Volume")));
|
||||
init_info_label(&info_facets, _(L("Facets")));
|
||||
init_info_label(&info_materials, _(L("Materials")));
|
||||
Add(grid_sizer, 0, wxEXPAND);
|
||||
|
||||
auto *info_manifold_text = new wxStaticText(parent, wxID_ANY, _(L("Manifold:")));
|
||||
auto *info_manifold_text = new wxStaticText(parent, wxID_ANY, _(L("Manifold")));
|
||||
info_manifold_text->SetFont(wxGetApp().small_font());
|
||||
info_manifold = new wxStaticText(parent, wxID_ANY, "");
|
||||
info_manifold->SetFont(wxGetApp().small_font());
|
||||
|
@ -602,7 +602,7 @@ Sidebar::Sidebar(Plater *parent)
|
|||
scrolled_sizer->Add(p->sliced_info, 0, wxEXPAND | wxTOP | wxLEFT, 20);
|
||||
|
||||
// Buttons underneath the scrolled area
|
||||
p->btn_export_gcode = new wxButton(this, wxID_ANY, _(L("Export G-code…")));
|
||||
p->btn_export_gcode = new wxButton(this, wxID_ANY, _(L("Export G-code")) + dots);
|
||||
p->btn_export_gcode->SetFont(wxGetApp().bold_font());
|
||||
p->btn_reslice = new wxButton(this, wxID_ANY, _(L("Slice now")));
|
||||
p->btn_reslice->SetFont(wxGetApp().bold_font());
|
||||
|
@ -792,7 +792,7 @@ void Sidebar::show_info_sizer()
|
|||
wxString tooltip = wxString::Format(_(L("Auto-repaired (%d errors)")), errors);
|
||||
p->object_info->info_manifold->SetLabel(tooltip);
|
||||
|
||||
tooltip += wxString::Format(_(L(":\n%d degenerate facets, %d edges fixed, %d facets removed, "
|
||||
tooltip += ":\n" + wxString::Format(_(L("%d degenerate facets, %d edges fixed, %d facets removed, "
|
||||
"%d facets added, %d facets reversed, %d backwards edges")),
|
||||
stats.degenerate_facets, stats.edges_fixed, stats.facets_removed,
|
||||
stats.facets_added, stats.facets_reversed, stats.backwards_edges);
|
||||
|
@ -849,7 +849,7 @@ void Sidebar::show_sliced_info_sizer(const bool show)
|
|||
if (ps.estimated_normal_print_time == "N/A" && ps.estimated_silent_print_time == "N/A")
|
||||
p->sliced_info->SetTextAndShow(siEstimatedTime, "N/A");
|
||||
else {
|
||||
new_label = "Estimated printing time :";
|
||||
new_label = _(L("Estimated printing time")) +" :";
|
||||
info_text = "";
|
||||
if (ps.estimated_normal_print_time != "N/A") {
|
||||
new_label += wxString::Format("\n - %s", _(L("normal mode")));
|
||||
|
@ -934,11 +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;
|
||||
PrusaMenu sla_object_menu;
|
||||
|
||||
// Data
|
||||
Slic3r::DynamicPrintConfig *config; // FIXME: leak?
|
||||
|
@ -970,6 +970,9 @@ struct Plater::priv
|
|||
static const std::regex pattern_3mf;
|
||||
static const std::regex pattern_zip_amf;
|
||||
static const std::regex pattern_any_amf;
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
static const std::regex pattern_prusa;
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
priv(Plater *q, MainFrame *main_frame);
|
||||
|
||||
|
@ -1063,6 +1066,7 @@ private:
|
|||
bool can_delete_object() const;
|
||||
bool can_increase_instances() const;
|
||||
bool can_decrease_instances() const;
|
||||
bool can_set_instance_to_object() const;
|
||||
bool can_split_to_objects() const;
|
||||
bool can_split_to_volumes() const;
|
||||
bool can_split() const;
|
||||
|
@ -1079,6 +1083,9 @@ const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf
|
|||
const std::regex Plater::priv::pattern_3mf(".*3mf", std::regex::icase);
|
||||
const std::regex Plater::priv::pattern_zip_amf(".*[.]zip[.]amf", std::regex::icase);
|
||||
const std::regex Plater::priv::pattern_any_amf(".*[.](amf|amf[.]xml|zip[.]amf)", std::regex::icase);
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
const std::regex Plater::priv::pattern_prusa(".*prusa", std::regex::icase);
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
: q(q)
|
||||
|
@ -1088,7 +1095,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
"brim_width", "variable_layer_height", "serial_port", "serial_speed", "host_type", "print_host",
|
||||
"printhost_apikey", "printhost_cafile", "nozzle_diameter", "single_extruder_multi_material",
|
||||
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle",
|
||||
"extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology"
|
||||
"extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology",
|
||||
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
|
||||
"layer_height", "first_layer_height", "min_layer_height", "max_layer_height",
|
||||
"brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers", "bottom_solid_layers", "solid_infill_extruder",
|
||||
"support_material", "support_material_extruder", "support_material_interface_extruder", "support_material_contact_distance", "raft_layers"
|
||||
}))
|
||||
, sidebar(new Sidebar(q))
|
||||
, delayed_scene_refresh(false)
|
||||
|
@ -1157,7 +1168,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
view3D_canvas->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this);
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this);
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this);
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_MODEL_UPDATE, [this](SimpleEvent&) { this->schedule_background_process(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); });
|
||||
|
@ -1262,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()
|
||||
|
@ -1303,7 +1318,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
}
|
||||
}
|
||||
|
||||
const auto loading = _(L("Loading…"));
|
||||
const auto loading = _(L("Loading")) + dots;
|
||||
wxProgressDialog dlg(loading, loading);
|
||||
dlg.Pulse();
|
||||
|
||||
|
@ -1319,6 +1334,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
const bool type_3mf = std::regex_match(path.string(), pattern_3mf);
|
||||
const bool type_zip_amf = !type_3mf && std::regex_match(path.string(), pattern_zip_amf);
|
||||
const bool type_any_amf = !type_3mf && std::regex_match(path.string(), pattern_any_amf);
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
const bool type_prusa = std::regex_match(path.string(), pattern_prusa);
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
Slic3r::Model model;
|
||||
try {
|
||||
|
@ -1363,23 +1381,33 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
{
|
||||
// The model should now be initialized
|
||||
|
||||
if (model.looks_like_multipart_object()) {
|
||||
// wxMessageDialog dlg(q, _(L(
|
||||
// "This file contains several objects positioned at multiple heights. "
|
||||
// "Instead of considering them as multiple objects, should I consider\n"
|
||||
// "this file as a single object having multiple parts?\n"
|
||||
// )), _(L("Multi-part object detected")), wxICON_WARNING | wxYES | wxNO);
|
||||
// if (dlg.ShowModal() == wxID_YES) {
|
||||
model.convert_multipart_object(nozzle_dmrs->values.size());
|
||||
// }
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
if (!type_3mf && !type_any_amf && !type_prusa) {
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
if (model.looks_like_multipart_object()) {
|
||||
wxMessageDialog dlg(q, _(L(
|
||||
"This file contains several objects positioned at multiple heights. "
|
||||
"Instead of considering them as multiple objects, should I consider\n"
|
||||
"this file as a single object having multiple parts?\n"
|
||||
)), _(L("Multi-part object detected")), wxICON_WARNING | wxYES | wxNO);
|
||||
if (dlg.ShowModal() == wxID_YES) {
|
||||
model.convert_multipart_object(nozzle_dmrs->values.size());
|
||||
}
|
||||
}
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
}
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
if (type_3mf || type_any_amf) {
|
||||
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
for (ModelObject* model_object : model.objects) {
|
||||
model_object->center_around_origin();
|
||||
model_object->ensure_on_bed();
|
||||
}
|
||||
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
}
|
||||
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
// check multi-part object adding for the SLA-printing
|
||||
if (printer_technology == ptSLA)
|
||||
|
@ -1440,29 +1468,40 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
return obj_idxs;
|
||||
}
|
||||
|
||||
// #define AUTOPLACEMENT_ON_LOAD
|
||||
|
||||
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects)
|
||||
{
|
||||
const BoundingBoxf bed_shape = bed_shape_bb();
|
||||
const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast<double>(), 1.0) - 2.0 * Vec3d::Ones();
|
||||
|
||||
#ifndef AUTOPLACEMENT_ON_LOAD
|
||||
bool need_arrange = false;
|
||||
#endif /* AUTOPLACEMENT_ON_LOAD */
|
||||
bool scaled_down = false;
|
||||
std::vector<size_t> obj_idxs;
|
||||
unsigned int obj_count = model.objects.size();
|
||||
|
||||
#ifdef AUTOPLACEMENT_ON_LOAD
|
||||
ModelInstancePtrs new_instances;
|
||||
#endif /* AUTOPLACEMENT_ON_LOAD */
|
||||
for (ModelObject *model_object : model_objects) {
|
||||
auto *object = model.add_object(*model_object);
|
||||
std::string object_name = object->name.empty() ? fs::path(object->input_file).filename().string() : object->name;
|
||||
obj_idxs.push_back(obj_count++);
|
||||
|
||||
if (model_object->instances.empty()) {
|
||||
// if object has no defined position(s) we need to rearrange everything after loading
|
||||
need_arrange = true;
|
||||
|
||||
// add a default instance and center object around origin
|
||||
object->center_around_origin(); // also aligns object to Z = 0
|
||||
ModelInstance* instance = object->add_instance();
|
||||
#ifdef AUTOPLACEMENT_ON_LOAD
|
||||
object->center_around_origin();
|
||||
new_instances.emplace_back(object->add_instance());
|
||||
#else /* AUTOPLACEMENT_ON_LOAD */
|
||||
// if object has no defined position(s) we need to rearrange everything after loading object->center_around_origin();
|
||||
need_arrange = true;
|
||||
// add a default instance and center object around origin
|
||||
object->center_around_origin(); // also aligns object to Z = 0
|
||||
ModelInstance* instance = object->add_instance();
|
||||
instance->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -object->origin_translation(2)));
|
||||
#endif /* AUTOPLACEMENT_ON_LOAD */
|
||||
}
|
||||
|
||||
const Vec3d size = object->bounding_box().size();
|
||||
|
@ -1477,10 +1516,11 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
|
|||
object->center_around_origin();
|
||||
scaled_down = true;
|
||||
} else if (max_ratio > 5) {
|
||||
const Vec3d inverse = ratio.cwiseInverse();
|
||||
for (ModelInstance *instance : model_object->instances) {
|
||||
const Vec3d inverse = 1.0 / max_ratio * Vec3d::Ones();
|
||||
for (ModelInstance *instance : object->instances) {
|
||||
instance->set_scaling_factor(inverse);
|
||||
}
|
||||
scaled_down = true;
|
||||
}
|
||||
|
||||
object->ensure_on_bed();
|
||||
|
@ -1489,6 +1529,18 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
|
|||
// print.add_model_object(object);
|
||||
}
|
||||
|
||||
#ifdef AUTOPLACEMENT_ON_LOAD
|
||||
// FIXME distance should be a config value /////////////////////////////////
|
||||
auto min_obj_distance = static_cast<coord_t>(6/SCALING_FACTOR);
|
||||
const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
|
||||
assert(bed_shape_opt);
|
||||
auto& bedpoints = bed_shape_opt->values;
|
||||
Polyline bed; bed.points.reserve(bedpoints.size());
|
||||
for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1)));
|
||||
|
||||
arr::find_new_position(model, new_instances, min_obj_distance, bed);
|
||||
#endif /* AUTOPLACEMENT_ON_LOAD */
|
||||
|
||||
if (scaled_down) {
|
||||
GUI::show_info(q,
|
||||
_(L("Your object appears to be too large, so it was automatically scaled down to fit your print bed.")),
|
||||
|
@ -1589,7 +1641,16 @@ void Plater::priv::selection_changed()
|
|||
view3D->enable_toolbar_item("fewer", can_decrease_instances());
|
||||
view3D->enable_toolbar_item("splitobjects", can_split/*_to_objects*/());
|
||||
view3D->enable_toolbar_item("splitvolumes", can_split/*_to_volumes*/());
|
||||
view3D->enable_toolbar_item("layersediting", layers_height_allowed());
|
||||
|
||||
// if the selection is not valid to allow for layer editing, we need to turn off the tool if it is running
|
||||
bool enable_layer_editing = layers_height_allowed();
|
||||
if (!enable_layer_editing && view3D->is_layers_editing_enabled()) {
|
||||
SimpleEvent evt(EVT_GLTOOLBAR_LAYERSEDITING);
|
||||
on_action_layersediting(evt);
|
||||
}
|
||||
|
||||
view3D->enable_toolbar_item("layersediting", enable_layer_editing);
|
||||
|
||||
// forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears)
|
||||
view3D->render();
|
||||
}
|
||||
|
@ -1854,6 +1915,8 @@ void Plater::priv::schedule_background_process()
|
|||
{
|
||||
// Trigger the timer event after 0.5s
|
||||
this->background_process_timer.Start(500, wxTIMER_ONE_SHOT);
|
||||
// Notify the Canvas3D that something has changed, so it may invalidate some of the layer editing stuff.
|
||||
this->view3D->get_canvas3d()->set_config(this->config);
|
||||
}
|
||||
|
||||
void Plater::priv::update_print_volume_state()
|
||||
|
@ -2035,7 +2098,7 @@ void Plater::priv::reload_from_disk()
|
|||
}
|
||||
}
|
||||
|
||||
// XXX: Restore more: layer_height_ranges, layer_height_profile, layer_height_profile_valid (?)
|
||||
// XXX: Restore more: layer_height_ranges, layer_height_profile (?)
|
||||
}
|
||||
|
||||
remove(obj_orig_idx);
|
||||
|
@ -2059,14 +2122,14 @@ 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++) {
|
||||
o->volumes[i]->config.apply(model_object->volumes[i]->config);
|
||||
}
|
||||
}
|
||||
// FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid,
|
||||
// FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile
|
||||
}
|
||||
|
||||
remove(obj_idx);
|
||||
|
@ -2297,7 +2360,7 @@ void Plater::priv::on_right_click(Vec2dEvent& evt)
|
|||
return;
|
||||
|
||||
wxMenu* menu = printer_technology == ptSLA ? &sla_object_menu :
|
||||
get_selection().is_single_full_instance/*object*/() ? // show "Object menu" for each FullInstance instead of FullObject
|
||||
get_selection().is_single_full_instance() ? // show "Object menu" for each FullInstance instead of FullObject
|
||||
&object_menu : &part_menu;
|
||||
|
||||
sidebar->obj_list()->append_menu_item_settings(menu);
|
||||
|
@ -2345,27 +2408,32 @@ bool Plater::priv::init_object_menu()
|
|||
|
||||
bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/)
|
||||
{
|
||||
wxMenuItem* item_delete = append_menu_item(menu, wxID_ANY, _(L("Delete\tDel")), _(L("Remove the selected object")),
|
||||
wxMenuItem* item_delete = append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")),
|
||||
[this](wxCommandEvent&) { q->remove_selected(); }, "brick_delete.png");
|
||||
if (!is_part){
|
||||
wxMenuItem* item_increase = append_menu_item(menu, wxID_ANY, _(L("Increase copies\t+")), _(L("Place one more copy of the selected object")),
|
||||
wxMenuItem* item_increase = append_menu_item(menu, wxID_ANY, _(L("Increase copies")) + "\t+", _(L("Place one more copy of the selected object")),
|
||||
[this](wxCommandEvent&) { q->increase_instances(); }, "add.png");
|
||||
wxMenuItem* item_decrease = append_menu_item(menu, wxID_ANY, _(L("Decrease copies\t-")), _(L("Remove one copy of the selected object")),
|
||||
wxMenuItem* item_decrease = append_menu_item(menu, wxID_ANY, _(L("Decrease copies")) + "\t-", _(L("Remove one copy of the selected object")),
|
||||
[this](wxCommandEvent&) { q->decrease_instances(); }, "delete.png");
|
||||
wxMenuItem* item_set_number_of_copies = append_menu_item(menu, wxID_ANY, _(L("Set number of copies…")), _(L("Change the number of copies of the selected object")),
|
||||
wxMenuItem* item_set_number_of_copies = append_menu_item(menu, wxID_ANY, _(L("Set number of copies")) + dots, _(L("Change the number of copies of the selected object")),
|
||||
[this](wxCommandEvent&) { q->set_number_of_copies(); }, "textfield.png");
|
||||
|
||||
menu->AppendSeparator();
|
||||
wxMenuItem* item_instance_to_object = sidebar->obj_list()->append_menu_item_instance_to_object(menu);
|
||||
|
||||
if (q != nullptr)
|
||||
{
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_increase_instances()); }, item_increase->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_decrease_instances()); }, item_decrease->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_increase_instances()); }, item_set_number_of_copies->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_set_instance_to_object()); }, item_instance_to_object->GetId());
|
||||
}
|
||||
menu->AppendSeparator();
|
||||
|
||||
append_menu_item(menu, wxID_ANY, _(L("Reload from Disk")), _(L("Reload the selected file from Disk")),
|
||||
[this](wxCommandEvent&) { reload_from_disk(); });
|
||||
|
||||
append_menu_item(menu, wxID_ANY, _(L("Export object as STL…")), _(L("Export this single object as STL file")),
|
||||
append_menu_item(menu, wxID_ANY, _(L("Export object as STL")) + dots, _(L("Export this single object as STL file")),
|
||||
[this](wxCommandEvent&) { q->export_stl(true); });
|
||||
}
|
||||
menu->AppendSeparator();
|
||||
|
@ -2405,25 +2473,16 @@ bool Plater::priv::complit_init_object_menu()
|
|||
[this](wxCommandEvent&) { split_volume(); }, "shape_ungroup_p.png", &object_menu);
|
||||
|
||||
wxMenuItem* item_split = append_submenu(&object_menu, split_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object")), "shape_ungroup.png");
|
||||
|
||||
// append_menu_item(&object_menu, wxID_ANY, _(L("Reload from Disk")), _(L("Reload the selected file from Disk")),
|
||||
// [this](wxCommandEvent&) { reload_from_disk(); });
|
||||
//
|
||||
// append_menu_item(&object_menu, wxID_ANY, _(L("Export object as STL…")), _(L("Export this single object as STL file")),
|
||||
// [this](wxCommandEvent&) { q->export_stl(true); });
|
||||
|
||||
// Append "Add..." popupmenu
|
||||
object_menu.AppendSeparator();
|
||||
sidebar->obj_list()->append_menu_items_add_volume(&object_menu);
|
||||
|
||||
// object_menu.AppendSeparator();
|
||||
// "Add (volumes)" popupmenu will be added later in append_menu_items_add_volume()
|
||||
|
||||
// ui updates needs to be binded to the parent panel
|
||||
if (q != nullptr)
|
||||
{
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects() || can_split_to_volumes*/()); }, item_split->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split_objects->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split_volumes->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split_objects->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split_volumes->GetId());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -2433,16 +2492,16 @@ bool Plater::priv::complit_init_sla_object_menu()
|
|||
wxMenuItem* item_split = append_menu_item(&sla_object_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object into individual objects")),
|
||||
[this](wxCommandEvent&) { split_object(); }, "shape_ungroup_o.png");
|
||||
|
||||
sla_object_menu.AppendSeparator();
|
||||
|
||||
// Add the automatic rotation sub-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)
|
||||
{
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split->GetId());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -2453,15 +2512,15 @@ bool Plater::priv::complit_init_part_menu()
|
|||
wxMenuItem* item_split = append_menu_item(&part_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object into individual sub-parts")),
|
||||
[this](wxCommandEvent&) { split_volume(); }, "shape_ungroup_p.png");
|
||||
|
||||
part_menu.AppendSeparator();
|
||||
|
||||
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)
|
||||
{
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split->GetId());
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split->GetId());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -2529,6 +2588,12 @@ bool Plater::priv::can_increase_instances() const
|
|||
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size());
|
||||
}
|
||||
|
||||
bool Plater::priv::can_set_instance_to_object() const
|
||||
{
|
||||
const int obj_idx = get_selected_object_idx();
|
||||
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1);
|
||||
}
|
||||
|
||||
bool Plater::priv::can_decrease_instances() const
|
||||
{
|
||||
int obj_idx = get_selected_object_idx();
|
||||
|
@ -2552,8 +2617,6 @@ bool Plater::priv::can_split_to_volumes() const
|
|||
|
||||
bool Plater::priv::can_split() const
|
||||
{
|
||||
if (printer_technology == ptSLA)
|
||||
return false;
|
||||
return sidebar->obj_list()->is_splittable();
|
||||
}
|
||||
|
||||
|
@ -2767,6 +2830,10 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe
|
|||
|
||||
wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds");
|
||||
|
||||
if (!keep_upper && !keep_lower) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto new_objects = object->cut(instance_idx, z, keep_upper, keep_lower, rotate_lower);
|
||||
|
||||
remove(obj_idx);
|
||||
|
|
|
@ -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.")));
|
||||
}
|
||||
|
||||
|
|
|
@ -1153,11 +1153,11 @@ std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&othe
|
|||
std::string PresetCollection::name() const
|
||||
{
|
||||
switch (this->type()) {
|
||||
case Preset::TYPE_PRINT: return "print";
|
||||
case Preset::TYPE_FILAMENT: return "filament";
|
||||
case Preset::TYPE_SLA_PRINT: return "SLA print";
|
||||
case Preset::TYPE_SLA_MATERIAL: return "SLA material";
|
||||
case Preset::TYPE_PRINTER: return "printer";
|
||||
case Preset::TYPE_PRINT: return L("print");
|
||||
case Preset::TYPE_FILAMENT: return L("filament");
|
||||
case Preset::TYPE_SLA_PRINT: return L("SLA print");
|
||||
case Preset::TYPE_SLA_MATERIAL: return L("SLA material");
|
||||
case Preset::TYPE_PRINTER: return L("printer");
|
||||
default: return "invalid";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,10 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path)
|
|||
, txt_filename(new wxTextCtrl(this, wxID_ANY, path.filename().wstring()))
|
||||
, box_print(new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload"))))
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
txt_filename->OSXDisableAllSmartSubstitutions();
|
||||
#endif
|
||||
|
||||
auto *label_dir_hint = new wxStaticText(this, wxID_ANY, _(L("Use forward slashes ( / ) as a directory separator if needed.")));
|
||||
label_dir_hint->Wrap(CONTENT_WIDTH);
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ SysInfoDialog::SysInfoDialog()
|
|||
main_sizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3);
|
||||
|
||||
// this->Bind(wxEVT_LEFT_DOWN, &SysInfoDialog::onCloseDialog, this);
|
||||
logo->Bind(wxEVT_LEFT_DOWN, &SysInfoDialog::onCloseDialog, this);
|
||||
// logo->Bind(wxEVT_LEFT_DOWN, &SysInfoDialog::onCloseDialog, this);
|
||||
|
||||
SetSizer(main_sizer);
|
||||
main_sizer->SetSizeHints(this);
|
||||
|
|
|
@ -245,9 +245,6 @@ void Tab::create_preset_tab()
|
|||
// Initialize the DynamicPrintConfig by default keys/values.
|
||||
build();
|
||||
rebuild_page_tree();
|
||||
// update();
|
||||
// Load the currently selected preset into the GUI, update the preset selection box.
|
||||
load_current_preset();
|
||||
}
|
||||
|
||||
void Tab::load_initial_data()
|
||||
|
@ -2012,7 +2009,7 @@ PageShp TabPrinter::build_kinematics_page()
|
|||
|
||||
if (m_use_silent_mode) {
|
||||
// Legend for OptionsGroups
|
||||
auto optgroup = page->new_optgroup(_(L("")));
|
||||
auto optgroup = page->new_optgroup("");
|
||||
optgroup->set_show_modified_btns_val(false);
|
||||
optgroup->label_width = 230;
|
||||
auto line = Line{ "", "" };
|
||||
|
@ -2562,19 +2559,20 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr
|
|||
std::string type_name = presets->name();
|
||||
wxString tab = " ";
|
||||
wxString name = old_preset.is_default ?
|
||||
_(L("Default ")) + type_name + _(L(" preset")) :
|
||||
(type_name + _(L(" preset\n")) + tab + old_preset.name);
|
||||
wxString::Format(_(L("Default preset (%s)")), _(type_name)) : //_(L("Default ")) + type_name + _(L(" preset")) :
|
||||
wxString::Format(_(L("Preset (%s)")), _(type_name)) + "\n" + tab + old_preset.name; //type_name + _(L(" preset\n")) + tab + old_preset.name;
|
||||
|
||||
// Collect descriptions of the dirty options.
|
||||
wxString changes;
|
||||
for (const std::string &opt_key : presets->current_dirty_options()) {
|
||||
const ConfigOptionDef &opt = m_config->def()->options.at(opt_key);
|
||||
std::string name = "";
|
||||
/*std::string*/wxString name = "";
|
||||
if (! opt.category.empty())
|
||||
name += opt.category + " > ";
|
||||
name += _(opt.category) + " > ";
|
||||
name += !opt.full_label.empty() ?
|
||||
opt.full_label :
|
||||
opt.label;
|
||||
changes += tab + from_u8(name) + "\n";
|
||||
_(opt.full_label) :
|
||||
_(opt.label);
|
||||
changes += tab + /*from_u8*/(name) + "\n";
|
||||
}
|
||||
// Show a confirmation dialog with the list of dirty options.
|
||||
wxString message = name + "\n\n";
|
||||
|
@ -2588,7 +2586,7 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr
|
|||
message += _(L("and it has the following unsaved changes:"));
|
||||
}
|
||||
auto confirm = new wxMessageDialog(parent(),
|
||||
message + "\n" + changes + _(L("\n\nDiscard changes and continue anyway?")),
|
||||
message + "\n" + changes + "\n\n" + _(L("Discard changes and continue anyway?")),
|
||||
_(L("Unsaved Changes")), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
|
||||
return confirm->ShowModal() == wxID_YES;
|
||||
}
|
||||
|
@ -2600,8 +2598,8 @@ bool Tab::may_switch_to_SLA_preset()
|
|||
if (wxGetApp().obj_list()->has_multi_part_objects())
|
||||
{
|
||||
show_info( parent(),
|
||||
_(L("It's impossible to print multi-part object(s) with SLA technology.")) +
|
||||
_(L("\n\nPlease check your object list before preset changing.")),
|
||||
_(L("It's impossible to print multi-part object(s) with SLA technology.")) + "\n\n" +
|
||||
_(L("Please check your object list before preset changing.")),
|
||||
_(L("Attention!")) );
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -213,9 +213,7 @@ public:
|
|||
|
||||
public:
|
||||
Tab(wxNotebook* parent, const wxString& title, const char* name);
|
||||
~Tab() {
|
||||
wxGetApp().delete_tab_from_list(this);
|
||||
}
|
||||
~Tab() {}
|
||||
|
||||
wxWindow* parent() const { return m_parent; }
|
||||
wxString title() const { return m_title; }
|
||||
|
|
|
@ -406,7 +406,7 @@ void PrusaObjectDataViewModelNode::set_object_action_icon() {
|
|||
m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("add_object.png")), wxBITMAP_TYPE_PNG);
|
||||
}
|
||||
void PrusaObjectDataViewModelNode::set_part_action_icon() {
|
||||
m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG);
|
||||
m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var(m_type == itVolume ? "cog.png" : "brick_go.png")), wxBITMAP_TYPE_PNG);
|
||||
}
|
||||
|
||||
Slic3r::GUI::BitmapCache *m_bitmap_cache = nullptr;
|
||||
|
@ -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>
|
||||
|
@ -275,6 +276,7 @@ public:
|
|||
else if (type == itInstance) {
|
||||
m_idx = parent->GetChildCount();
|
||||
m_name = wxString::Format("Instance_%d", m_idx+1);
|
||||
set_part_action_icon();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -919,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 **********************************************
|
||||
// ******************************************************************************************
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue