Merge branch 'master' into tm_colldetection_upgr

This commit is contained in:
tamasmeszaros 2019-01-28 16:52:50 +01:00
commit 3999ffe713
131 changed files with 71551 additions and 40902 deletions

View file

@ -169,6 +169,11 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STRE
endif ()
endif()
if (APPLE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=partial-availability -Werror=unguarded-availability -Werror=unguarded-availability-new")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=partial-availability -Werror=unguarded-availability -Werror=unguarded-availability-new")
endif ()
# Where all the bundled libraries reside?
set(LIBDIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
set(LIBDIR_BIN ${CMAKE_CURRENT_BINARY_DIR}/src)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,32 +1,37 @@
xs/src/slic3r/GUI/AboutDialog.cpp
xs/src/slic3r/GUI/BedShapeDialog.cpp
xs/src/slic3r/GUI/BedShapeDialog.hpp
xs/src/slic3r/GUI/BonjourDialog.cpp
xs/src/slic3r/GUI/ButtonsDescription.cpp
xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp
xs/src/slic3r/GUI/ConfigWizard.cpp
xs/src/slic3r/GUI/FirmwareDialog.cpp
xs/src/slic3r/GUI/GLCanvas3D.cpp
xs/src/slic3r/GUI/GUI.cpp
xs/src/slic3r/GUI/MsgDialog.cpp
xs/src/slic3r/GUI/Tab.cpp
xs/src/slic3r/GUI/Tab.hpp
xs/src/slic3r/GUI/Field.cpp
xs/src/slic3r/GUI/OptionsGroup.cpp
xs/src/slic3r/GUI/Preset.cpp
xs/src/slic3r/GUI/PresetBundle.cpp
xs/src/slic3r/GUI/PresetHints.cpp
xs/src/slic3r/GUI/Preferences.cpp
xs/src/slic3r/GUI/RammingChart.cpp
xs/src/slic3r/GUI/UpdateDialogs.cpp
xs/src/slic3r/GUI/WipeTowerDialog.cpp
xs/src/slic3r/Utils/OctoPrint.cpp
xs/src/slic3r/Utils/PresetUpdater.cpp
xs/src/libslic3r/Print.cpp
xs/src/libslic3r/PrintConfig.cpp
xs/src/libslic3r/GCode/PreviewData.cpp
lib/Slic3r/GUI.pm
lib/Slic3r/GUI/MainFrame.pm
lib/Slic3r/GUI/Plater.pm
lib/Slic3r/GUI/Plater/2D.pm
lib/Slic3r/GUI/Plater/3DPreview.pm
src/slic3r/GUI/AboutDialog.cpp
src/slic3r/GUI/BedShapeDialog.cpp
src/slic3r/GUI/BedShapeDialog.hpp
src/slic3r/GUI/BonjourDialog.cpp
src/slic3r/GUI/ButtonsDescription.cpp
src/slic3r/GUI/ConfigSnapshotDialog.cpp
src/slic3r/GUI/ConfigWizard.cpp
src/slic3r/GUI/Field.cpp
src/slic3r/GUI/FirmwareDialog.cpp
src/slic3r/GUI/GLCanvas3D.cpp
src/slic3r/GUI/GLGizmo.cpp
src/slic3r/GUI/GUI.cpp
src/slic3r/GUI/GUI_App.cpp
src/slic3r/GUI/GUI_ObjectList.cpp
src/slic3r/GUI/GUI_ObjectManipulation.cpp
src/slic3r/GUI/GUI_Preview.cpp
src/slic3r/GUI/KBShortcutsDialog.cpp
src/slic3r/GUI/MainFrame.cpp
src/slic3r/GUI/MsgDialog.cpp
src/slic3r/GUI/Plater.cpp
src/slic3r/GUI/Preferences.cpp
src/slic3r/GUI/Preset.cpp
src/slic3r/GUI/PresetBundle.cpp
src/slic3r/GUI/PresetHints.cpp
src/slic3r/GUI/PrintHostDialogs.cpp
src/slic3r/GUI/RammingChart.cpp
src/slic3r/GUI/SysInfoDialog.cpp
src/slic3r/GUI/Tab.cpp
src/slic3r/GUI/Tab.hpp
src/slic3r/GUI/UpdateDialogs.cpp
src/slic3r/GUI/WipeTowerDialog.cpp
src/slic3r/Utils/OctoPrint.cpp
src/slic3r/Utils/PresetUpdater.cpp
src/slic3r/Utils/FixModelByWin10.cpp
src/libslic3r/Print.cpp
src/libslic3r/PrintConfig.cpp
src/libslic3r/GCode/PreviewData.cpp

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -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()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,6 +579,7 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const
}
// perform triangulation
try {
cdt.Triangulate();
std::vector<p2t::Triangle*> triangles = cdt.GetTriangles();
@ -489,14 +591,17 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const
}
polygons->push_back(p);
}
} catch (const std::runtime_error & /* err */) {
assert(false);
// just ignore, don't triangulate
}
for (p2t::Point *ptr : ContourPoints)
delete ptr;
}
}
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) {

View file

@ -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

View file

@ -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
{

View file

@ -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);

View file

@ -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,7 +642,11 @@ 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));
@ -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";
}

View file

@ -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);

View file

@ -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,15 +149,23 @@ 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) {
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
int result = run_script_win32(script, gcode_file.string());
#else
//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);
#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);
@ -51,26 +175,12 @@ void run_post_process_scripts(const std::string &path, const PrintConfig &config
: ((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;
#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
#else
result = boost::process::system(script, gcode_file);
#endif
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
}
}
}
} // namespace Slic3r
#endif

View file

@ -207,8 +207,7 @@ static bool sort_pointfs(const Vec3d& a, const Vec3d& b)
}
// This implementation is based on Andrew's monotone chain 2D convex hull algorithm
Polygon
convex_hull(Points points)
Polygon convex_hull(Points points)
{
assert(points.size() >= 3);
// sort input points
@ -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))

View file

@ -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;

View file

@ -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,14 +912,72 @@ 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());
#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();
@ -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()

View file

@ -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); }

View file

@ -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>;
@ -291,13 +305,13 @@ protected:
Packer m_pck;
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,6 +553,12 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
for(ModelObject* objptr : model.objects) {
if(objptr) {
// 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();
@ -517,12 +570,30 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
rmesh.rotate_x(float(finst->get_rotation()(X)));
rmesh.rotate_y(float(finst->get_rotation()(Y)));
// TODO export the exact 2D projection
// 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());
auto clpath = Slic3rMultiPoint_to_ClipperPath(p);
clpath = Slic3rMultiPoint_to_ClipperPath(p);
}
#else
// Object instances should carry the same scaling and
// x, y rotation that is why we use the first instance.
{
ModelInstance *finst = objptr->instances.front();
Vec3d rotation = finst->get_rotation();
rotation.z() = 0.;
Transform3d trafo_instance = Geometry::assemble_transform(Vec3d::Zero(), rotation, finst->get_scaling_factor(), finst->get_mirror());
Polygon p = objptr->convex_hull_2d(trafo_instance);
assert(! p.points.empty());
p.reverse();
assert(! p.is_counter_clockwise());
p.append(p.first_point());
clpath = Slic3rMultiPoint_to_ClipperPath(p);
}
#endif
for(ModelInstance* objinst : objptr->instances) {
if(objinst) {
@ -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;
}
}
}
}

View file

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

View file

@ -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;
}

View file

@ -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 &region_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,15 +1186,24 @@ 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).");
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;
}
}
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();
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");
@ -1257,31 +1213,39 @@ std::string Print::validate() const
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 (m_config.variable_layer_height) { // comparing layer height profiles
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;
// layer_height_profile should be set by Print::apply().
if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size()) {
if (layer_height_profile_tallest.size() >= 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])) {
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 == 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;
++ 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
} else
failed = true;
if (failed)
return L("The Wipe tower is only supported if all objects have the same layer height profile");
}
}
}
}
{
// find the smallest nozzle diameter

View file

@ -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 &region_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;
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();

View file

@ -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;

View file

@ -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

View file

@ -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 &region_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);
}
}

View file

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

View file

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

View file

@ -440,10 +440,6 @@ std::vector<float> SLAPrint::calculate_heights(const BoundingBoxf3& bb3d,
auto flh = float(layer_height);
auto gnd = float(bb3d.min(Z));
// The first layer (the one before the initial height) is added only
// if there is no pad and no elevation value
if(minZ >= gnd) heights.emplace_back(minZ);
for(float h = minZ + initial_layer_height; h < maxZ; h += flh)
if(h >= gnd) heights.emplace_back(h);
@ -510,6 +506,9 @@ void SLAPrint::process()
po.m_supportdata.reset(new SLAPrintObject::SupportData());
po.m_supportdata->emesh = 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);

View file

@ -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,

View file

@ -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)

View file

@ -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;
}
}

View file

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

View file

@ -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:

View file

@ -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;
}
}

View file

@ -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_;
}

View file

@ -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.
*
@ -37,8 +37,15 @@
#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 {

View file

@ -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.
*

View file

@ -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)

View file

@ -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.
*

View file

@ -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.
*

View file

@ -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.
*

View file

@ -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);

View file

@ -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.
*

View file

@ -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_);

View file

@ -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);

View file

@ -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);

View file

@ -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;
@ -31,6 +33,9 @@ typedef double tppl_float;
struct TPPLPoint {
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;
@ -71,15 +76,16 @@ struct TPPLPoint {
}
};
//Polygon implemented as an array of points with a 'hole' flag
class TPPLPoly {
protected:
protected:
TPPLPoint *points;
long numpoints;
bool hole;
public:
public:
//constructors/destructors
TPPLPoly();
@ -105,6 +111,10 @@ public:
return points[i];
}
const TPPLPoint &GetPoint(long i) const {
return points[i];
}
TPPLPoint *GetPoints() {
return points;
}
@ -113,6 +123,10 @@ public:
return points[i];
}
const TPPLPoint& operator[] (int i) const {
return points[i];
}
//clears the polygon points
void Clear();
@ -137,10 +151,19 @@ public:
// 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:
protected:
struct PartitionVertex {
bool isActive;
bool isConvex;
@ -150,6 +173,8 @@ protected:
tppl_float angle;
PartitionVertex *previous;
PartitionVertex *next;
PartitionVertex();
};
struct MonotoneVertex {
@ -162,7 +187,7 @@ protected:
MonotoneVertex *vertices;
public:
VertexSorter(MonotoneVertex *v) : vertices(v) {}
bool operator() (long index1, long index2) const;
bool operator() (long index1, long index2);
};
struct Diagonal {
@ -170,6 +195,12 @@ protected:
long index2;
};
#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;
@ -181,12 +212,12 @@ protected:
struct DPState2 {
bool visible;
long weight;
list<Diagonal> pairs;
DiagonalList pairs;
};
//edge that intersects the scanline
struct ScanLineEdge {
long index;
mutable long index;
TPPLPoint p1;
TPPLPoint p2;
@ -220,12 +251,14 @@ protected:
//helper functions for MonotonePartition
bool Below(TPPLPoint &p1, TPPLPoint &p2);
void AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2);
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, list<TPPLPoly> *triangles);
int TriangulateMonotone(TPPLPoly *inPoly, TPPLPolyList *triangles);
public:
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
@ -237,7 +270,7 @@ public:
// 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);
int RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys);
//triangulates a polygon by ear clipping
//time complexity O(n^2), n is the number of vertices
@ -247,7 +280,7 @@ public:
// 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);
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
@ -259,7 +292,7 @@ public:
// 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);
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
@ -269,7 +302,7 @@ public:
// 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);
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
@ -279,7 +312,7 @@ public:
// 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);
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
@ -290,7 +323,7 @@ public:
// 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);
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
@ -301,7 +334,7 @@ public:
// 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);
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
@ -314,7 +347,7 @@ public:
// 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);
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
@ -328,7 +361,7 @@ public:
// 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);
int ConvexPartition_HM(TPPLPolyList *inpolys, TPPLPolyList *parts);
//optimal convex partitioning (in terms of number of resulting convex polygons)
//using the Keil-Snoeyink algorithm
@ -339,5 +372,8 @@ public:
// 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);
int ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts);
};
#endif

View file

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

View file

@ -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, &current_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) ||
if ((((type == GLVolumeCollection::Opaque) && !is_transparent) ||
((type == GLVolumeCollection::Transparent) && is_transparent) ||
(type == GLVolumeCollection::All))
list.push_back(std::make_pair(volume, 0.0));
(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
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();

View file

@ -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];
@ -416,48 +375,12 @@ 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; }
};
@ -465,7 +388,6 @@ 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

View file

@ -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);

View file

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

View file

@ -90,7 +90,7 @@ 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()));
(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_menu = L("Configuration &Wizard");
#else
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;
}
}

View file

@ -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

View file

@ -2,7 +2,9 @@
#define slic3r_GLCanvas3D_hpp_
#include <stddef.h>
#include <memory>
#include "libslic3r/Technologies.hpp"
#include "3DScene.hpp"
#include "GLToolbar.hpp"
#include "Event.hpp"
@ -20,6 +22,9 @@ class wxTimerEvent;
class wxPaintEvent;
class wxGLCanvas;
// Support for Retina OpenGL on Mac OS
#define ENABLE_RETINA_GL __APPLE__
class GLUquadric;
typedef class GLUquadric GLUquadricObj;
@ -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:
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.

View file

@ -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));

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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;

View file

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

View file

@ -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;

View file

@ -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.

View file

@ -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,17 +285,7 @@ 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);
@ -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) = @_;

View file

@ -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)

Some files were not shown because too many files have changed in this diff Show more