mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-16 03:07:55 -06:00
Merge branch 'master' into tm_colldetection_upgr
This commit is contained in:
commit
3999ffe713
131 changed files with 71551 additions and 40902 deletions
|
@ -169,6 +169,11 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STRE
|
||||||
endif ()
|
endif ()
|
||||||
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?
|
# Where all the bundled libraries reside?
|
||||||
set(LIBDIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
set(LIBDIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||||
set(LIBDIR_BIN ${CMAKE_CURRENT_BINARY_DIR}/src)
|
set(LIBDIR_BIN ${CMAKE_CURRENT_BINARY_DIR}/src)
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
@ -1,32 +1,37 @@
|
||||||
xs/src/slic3r/GUI/AboutDialog.cpp
|
src/slic3r/GUI/AboutDialog.cpp
|
||||||
xs/src/slic3r/GUI/BedShapeDialog.cpp
|
src/slic3r/GUI/BedShapeDialog.cpp
|
||||||
xs/src/slic3r/GUI/BedShapeDialog.hpp
|
src/slic3r/GUI/BedShapeDialog.hpp
|
||||||
xs/src/slic3r/GUI/BonjourDialog.cpp
|
src/slic3r/GUI/BonjourDialog.cpp
|
||||||
xs/src/slic3r/GUI/ButtonsDescription.cpp
|
src/slic3r/GUI/ButtonsDescription.cpp
|
||||||
xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp
|
src/slic3r/GUI/ConfigSnapshotDialog.cpp
|
||||||
xs/src/slic3r/GUI/ConfigWizard.cpp
|
src/slic3r/GUI/ConfigWizard.cpp
|
||||||
xs/src/slic3r/GUI/FirmwareDialog.cpp
|
src/slic3r/GUI/Field.cpp
|
||||||
xs/src/slic3r/GUI/GLCanvas3D.cpp
|
src/slic3r/GUI/FirmwareDialog.cpp
|
||||||
xs/src/slic3r/GUI/GUI.cpp
|
src/slic3r/GUI/GLCanvas3D.cpp
|
||||||
xs/src/slic3r/GUI/MsgDialog.cpp
|
src/slic3r/GUI/GLGizmo.cpp
|
||||||
xs/src/slic3r/GUI/Tab.cpp
|
src/slic3r/GUI/GUI.cpp
|
||||||
xs/src/slic3r/GUI/Tab.hpp
|
src/slic3r/GUI/GUI_App.cpp
|
||||||
xs/src/slic3r/GUI/Field.cpp
|
src/slic3r/GUI/GUI_ObjectList.cpp
|
||||||
xs/src/slic3r/GUI/OptionsGroup.cpp
|
src/slic3r/GUI/GUI_ObjectManipulation.cpp
|
||||||
xs/src/slic3r/GUI/Preset.cpp
|
src/slic3r/GUI/GUI_Preview.cpp
|
||||||
xs/src/slic3r/GUI/PresetBundle.cpp
|
src/slic3r/GUI/KBShortcutsDialog.cpp
|
||||||
xs/src/slic3r/GUI/PresetHints.cpp
|
src/slic3r/GUI/MainFrame.cpp
|
||||||
xs/src/slic3r/GUI/Preferences.cpp
|
src/slic3r/GUI/MsgDialog.cpp
|
||||||
xs/src/slic3r/GUI/RammingChart.cpp
|
src/slic3r/GUI/Plater.cpp
|
||||||
xs/src/slic3r/GUI/UpdateDialogs.cpp
|
src/slic3r/GUI/Preferences.cpp
|
||||||
xs/src/slic3r/GUI/WipeTowerDialog.cpp
|
src/slic3r/GUI/Preset.cpp
|
||||||
xs/src/slic3r/Utils/OctoPrint.cpp
|
src/slic3r/GUI/PresetBundle.cpp
|
||||||
xs/src/slic3r/Utils/PresetUpdater.cpp
|
src/slic3r/GUI/PresetHints.cpp
|
||||||
xs/src/libslic3r/Print.cpp
|
src/slic3r/GUI/PrintHostDialogs.cpp
|
||||||
xs/src/libslic3r/PrintConfig.cpp
|
src/slic3r/GUI/RammingChart.cpp
|
||||||
xs/src/libslic3r/GCode/PreviewData.cpp
|
src/slic3r/GUI/SysInfoDialog.cpp
|
||||||
lib/Slic3r/GUI.pm
|
src/slic3r/GUI/Tab.cpp
|
||||||
lib/Slic3r/GUI/MainFrame.pm
|
src/slic3r/GUI/Tab.hpp
|
||||||
lib/Slic3r/GUI/Plater.pm
|
src/slic3r/GUI/UpdateDialogs.cpp
|
||||||
lib/Slic3r/GUI/Plater/2D.pm
|
src/slic3r/GUI/WipeTowerDialog.cpp
|
||||||
lib/Slic3r/GUI/Plater/3DPreview.pm
|
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
|
||||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
6894
resources/localization/zh_CN/Slic3rPE_zh_new.po
Normal file
6894
resources/localization/zh_CN/Slic3rPE_zh_new.po
Normal file
File diff suppressed because it is too large
Load diff
BIN
resources/models/sl1_bed.stl
Normal file
BIN
resources/models/sl1_bed.stl
Normal file
Binary file not shown.
|
@ -50,7 +50,6 @@ if (SLIC3R_GUI)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
message(STATUS "WXWIN environment set to: $ENV{WXWIN}")
|
message(STATUS "WXWIN environment set to: $ENV{WXWIN}")
|
||||||
elseif(UNIX)
|
elseif(UNIX)
|
||||||
message(STATUS "wx-config path: ${wxWidgets_CONFIG_EXECUTABLE}")
|
|
||||||
set(wxWidgets_USE_UNICODE ON)
|
set(wxWidgets_USE_UNICODE ON)
|
||||||
if(SLIC3R_STATIC)
|
if(SLIC3R_STATIC)
|
||||||
set(wxWidgets_USE_STATIC ON)
|
set(wxWidgets_USE_STATIC ON)
|
||||||
|
@ -72,6 +71,10 @@ if (SLIC3R_GUI)
|
||||||
find_package(wxWidgets 3.1 REQUIRED COMPONENTS base core adv html gl)
|
find_package(wxWidgets 3.1 REQUIRED COMPONENTS base core adv html gl)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
if(UNIX)
|
||||||
|
message(STATUS "wx-config path: ${wxWidgets_CONFIG_EXECUTABLE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
include(${wxWidgets_USE_FILE})
|
include(${wxWidgets_USE_FILE})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,11 @@ if(TBB_FOUND)
|
||||||
target_compile_definitions(libnest2d INTERFACE -DTBB_USE_CAPTURED_EXCEPTION=0)
|
target_compile_definitions(libnest2d INTERFACE -DTBB_USE_CAPTURED_EXCEPTION=0)
|
||||||
|
|
||||||
target_link_libraries(libnest2d INTERFACE tbb)
|
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()
|
else()
|
||||||
find_package(OpenMP QUIET)
|
find_package(OpenMP QUIET)
|
||||||
|
|
||||||
|
@ -88,7 +93,7 @@ endif()
|
||||||
add_subdirectory(${SRC_DIR}/libnest2d/backends/${LIBNEST2D_GEOMETRIES})
|
add_subdirectory(${SRC_DIR}/libnest2d/backends/${LIBNEST2D_GEOMETRIES})
|
||||||
add_subdirectory(${SRC_DIR}/libnest2d/optimizers/${LIBNEST2D_OPTIMIZER})
|
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})
|
target_include_directories(libnest2d INTERFACE ${SRC_DIR})
|
||||||
|
|
||||||
if(NOT LIBNEST2D_HEADER_ONLY)
|
if(NOT LIBNEST2D_HEADER_ONLY)
|
||||||
|
|
|
@ -62,9 +62,9 @@ if(NOT Boost_INCLUDE_DIRS_FOUND)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(ClipperBackend INTERFACE ${Boost_INCLUDE_DIRS} )
|
target_include_directories(ClipperBackend INTERFACE ${Boost_INCLUDE_DIRS} )
|
||||||
#target_sources(ClipperBackend INTERFACE
|
target_sources(ClipperBackend INTERFACE
|
||||||
# ${CMAKE_CURRENT_SOURCE_DIR}/geometries.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/geometries.hpp
|
||||||
# ${SRC_DIR}/libnest2d/utils/boost_alg.hpp )
|
${SRC_DIR}/libnest2d/utils/boost_alg.hpp )
|
||||||
|
|
||||||
target_compile_definitions(ClipperBackend INTERFACE LIBNEST2D_BACKEND_CLIPPER)
|
target_compile_definitions(ClipperBackend INTERFACE LIBNEST2D_BACKEND_CLIPPER)
|
||||||
|
|
||||||
|
|
|
@ -113,6 +113,7 @@ template<> struct CountourType<PolygonImpl> {
|
||||||
|
|
||||||
template<> struct ShapeTag<PolygonImpl> { using Type = PolygonTag; };
|
template<> struct ShapeTag<PolygonImpl> { using Type = PolygonTag; };
|
||||||
template<> struct ShapeTag<PathImpl> { using Type = PathTag; };
|
template<> struct ShapeTag<PathImpl> { using Type = PathTag; };
|
||||||
|
template<> struct ShapeTag<PointImpl> { using Type = PointTag; };
|
||||||
|
|
||||||
template<> struct ShapeTag<TMultiShape<PolygonImpl>> {
|
template<> struct ShapeTag<TMultiShape<PolygonImpl>> {
|
||||||
using Type = MultiPolygonTag;
|
using Type = MultiPolygonTag;
|
||||||
|
|
|
@ -69,12 +69,14 @@ struct PointPair {
|
||||||
RawPoint p2;
|
RawPoint p2;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PointTag {};
|
||||||
struct PolygonTag {};
|
struct PolygonTag {};
|
||||||
struct PathTag {};
|
struct PathTag {};
|
||||||
struct MultiPolygonTag {};
|
struct MultiPolygonTag {};
|
||||||
struct BoxTag {};
|
struct BoxTag {};
|
||||||
struct CircleTag {};
|
struct CircleTag {};
|
||||||
|
|
||||||
|
/// Meta-functions to derive the tags
|
||||||
template<class Shape> struct ShapeTag { using Type = typename Shape::Tag; };
|
template<class Shape> struct ShapeTag { using Type = typename Shape::Tag; };
|
||||||
template<class S> using Tag = typename ShapeTag<remove_cvref_t<S>>::Type;
|
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) {}
|
_Circle(const RawPoint& center, double r): center_(center), radius_(r) {}
|
||||||
|
|
||||||
inline const RawPoint& center() const BP2D_NOEXCEPT { return center_; }
|
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 double radius() const BP2D_NOEXCEPT { return radius_; }
|
||||||
inline void radius(double r) { radius_ = r; }
|
inline void radius(double r) { radius_ = r; }
|
||||||
|
@ -518,20 +520,18 @@ inline bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class TGuest, class THost>
|
||||||
inline bool isInside(const TPoint<RawShape>& /*point*/,
|
inline bool isInside(const TGuest&, const THost&,
|
||||||
const RawShape& /*shape*/)
|
const PointTag&, const PolygonTag&) {
|
||||||
{
|
static_assert(always_false<THost>::value,
|
||||||
static_assert(always_false<RawShape>::value,
|
"shapelike::isInside(point, path) unimplemented!");
|
||||||
"shapelike::isInside(point, shape) unimplemented!");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class TGuest, class THost>
|
||||||
inline bool isInside(const RawShape& /*shape*/,
|
inline bool isInside(const TGuest&, const THost&,
|
||||||
const RawShape& /*shape*/)
|
const PolygonTag&, const PolygonTag&) {
|
||||||
{
|
static_assert(always_false<THost>::value,
|
||||||
static_assert(always_false<RawShape>::value,
|
|
||||||
"shapelike::isInside(shape, shape) unimplemented!");
|
"shapelike::isInside(shape, shape) unimplemented!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -651,7 +651,7 @@ template<class RawPath> inline bool isConvex(const RawPath& sh, const PathTag&)
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
inline typename TContour<RawShape>::iterator
|
inline typename TContour<RawShape>::iterator
|
||||||
begin(RawShape& sh, const PolygonTag& t)
|
begin(RawShape& sh, const PolygonTag&)
|
||||||
{
|
{
|
||||||
return begin(contour(sh), PathTag());
|
return begin(contour(sh), PathTag());
|
||||||
}
|
}
|
||||||
|
@ -818,16 +818,16 @@ inline auto convexHull(const RawShape& sh)
|
||||||
return convexHull(sh, Tag<RawShape>());
|
return convexHull(sh, Tag<RawShape>());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class TP, class TC>
|
||||||
inline bool isInside(const TPoint<RawShape>& point,
|
inline bool isInside(const TP& point, const TC& circ,
|
||||||
const _Circle<TPoint<RawShape>>& circ)
|
const PointTag&, const CircleTag&)
|
||||||
{
|
{
|
||||||
return pointlike::distance(point, circ.center()) < circ.radius();
|
return pointlike::distance(point, circ.center()) < circ.radius();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class TP, class TB>
|
||||||
inline bool isInside(const TPoint<RawShape>& point,
|
inline bool isInside(const TP& point, const TB& box,
|
||||||
const _Box<TPoint<RawShape>>& box)
|
const PointTag&, const BoxTag&)
|
||||||
{
|
{
|
||||||
auto px = getX(point);
|
auto px = getX(point);
|
||||||
auto py = getY(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;
|
return px > minx && px < maxx && py > miny && py < maxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape, class TC>
|
||||||
inline bool isInside(const RawShape& sh,
|
inline bool isInside(const RawShape& sh, const TC& circ,
|
||||||
const _Circle<TPoint<RawShape>>& circ)
|
const PolygonTag&, const CircleTag&)
|
||||||
{
|
{
|
||||||
return std::all_of(cbegin(sh), cend(sh),
|
return std::all_of(cbegin(sh), cend(sh), [&circ](const TPoint<RawShape>& p)
|
||||||
[&circ](const TPoint<RawShape>& p){
|
{
|
||||||
return isInside<RawShape>(p, circ);
|
return isInside(p, circ, PointTag(), CircleTag());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class TB, class TC>
|
||||||
inline bool isInside(const _Box<TPoint<RawShape>>& box,
|
inline bool isInside(const TB& box, const TC& circ,
|
||||||
const _Circle<TPoint<RawShape>>& circ)
|
const BoxTag&, const CircleTag&)
|
||||||
{
|
{
|
||||||
return isInside<RawShape>(box.minCorner(), circ) &&
|
return isInside(box.minCorner(), circ, BoxTag(), CircleTag()) &&
|
||||||
isInside<RawShape>(box.maxCorner(), circ);
|
isInside(box.maxCorner(), circ, BoxTag(), CircleTag());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class TBGuest, class TBHost>
|
||||||
inline bool isInside(const _Box<TPoint<RawShape>>& ibb,
|
inline bool isInside(const TBGuest& ibb, const TBHost& box,
|
||||||
const _Box<TPoint<RawShape>>& box)
|
const BoxTag&, const BoxTag&)
|
||||||
{
|
{
|
||||||
auto iminX = getX(ibb.minCorner());
|
auto iminX = getX(ibb.minCorner());
|
||||||
auto imaxX = getX(ibb.maxCorner());
|
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;
|
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
|
template<class RawShape> // Potential O(1) implementation may exist
|
||||||
inline TPoint<RawShape>& vertex(RawShape& sh, unsigned long idx,
|
inline TPoint<RawShape>& vertex(RawShape& sh, unsigned long idx,
|
||||||
const PolygonTag&)
|
const PolygonTag&)
|
||||||
|
|
|
@ -251,6 +251,460 @@ inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
|
||||||
return {rsh, top_nfp};
|
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
|
// Specializable NFP implementation class. Specialize it if you have a faster
|
||||||
// or better NFP implementation
|
// or better NFP implementation
|
||||||
template<class RawShape, NfpLevel nfptype>
|
template<class RawShape, NfpLevel nfptype>
|
||||||
|
|
|
@ -482,17 +482,40 @@ public:
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const {
|
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
|
template<class RawShape> inline bool
|
||||||
_Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const {
|
_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>
|
template<class Iterator>
|
||||||
struct ConstItemRange {
|
struct ConstItemRange {
|
||||||
|
@ -524,8 +547,10 @@ class PlacementStrategyLike {
|
||||||
PlacementStrategy impl_;
|
PlacementStrategy impl_;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
using RawShape = typename PlacementStrategy::ShapeType;
|
||||||
|
|
||||||
/// The item type that the placer works with.
|
/// 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.
|
/// The placer's config type. Should be a simple struct but can be anything.
|
||||||
using Config = typename PlacementStrategy::Config;
|
using Config = typename PlacementStrategy::Config;
|
||||||
|
@ -544,8 +569,7 @@ public:
|
||||||
*/
|
*/
|
||||||
using PackResult = typename PlacementStrategy::PackResult;
|
using PackResult = typename PlacementStrategy::PackResult;
|
||||||
|
|
||||||
using ItemRef = _ItemRef<Item>;
|
using ItemGroup = _ItemGroup<RawShape>;
|
||||||
using ItemGroup = _ItemGroup<Item>;
|
|
||||||
using DefaultIterator = typename ItemGroup::const_iterator;
|
using DefaultIterator = typename ItemGroup::const_iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -619,6 +643,15 @@ public:
|
||||||
return impl_.pack(item, remaining);
|
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).
|
/// Unpack the last element (remove it from the list of packed items).
|
||||||
inline void unpackLast() { impl_.unpackLast(); }
|
inline void unpackLast() { impl_.unpackLast(); }
|
||||||
|
|
||||||
|
@ -649,11 +682,11 @@ template<class SelectionStrategy>
|
||||||
class SelectionStrategyLike {
|
class SelectionStrategyLike {
|
||||||
SelectionStrategy impl_;
|
SelectionStrategy impl_;
|
||||||
public:
|
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 Config = typename SelectionStrategy::Config;
|
||||||
|
|
||||||
using ItemRef = std::reference_wrapper<Item>;
|
|
||||||
using ItemGroup = std::vector<ItemRef>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Provide a different configuration for the selection strategy.
|
* @brief Provide a different configuration for the selection strategy.
|
||||||
|
@ -703,60 +736,29 @@ public:
|
||||||
std::forward<PConfig>(config));
|
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.
|
* @brief Get the items for a particular bin.
|
||||||
* @param binIndex The index of the requested bin.
|
* @param binIndex The index of the requested bin.
|
||||||
* @return Returns a list of all items packed into the requested bin.
|
* @return Returns a list of all items packed into the requested bin.
|
||||||
*/
|
*/
|
||||||
inline ItemGroup itemsForBin(size_t binIndex) {
|
inline const PackGroup& getResult() const {
|
||||||
return impl_.itemsForBin(binIndex);
|
return impl_.getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as itemsForBin but for a const context.
|
/**
|
||||||
inline const ItemGroup itemsForBin(size_t binIndex) const {
|
* @brief Loading a group of already packed bins. It is best to use a result
|
||||||
return impl_.itemsForBin(binIndex);
|
* 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
|
* 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
|
* 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.
|
/// Set a predicate to tell when to abort nesting.
|
||||||
inline Nester& stopCondition(StopCondition fn) {
|
inline Nester& stopCondition(StopCondition fn)
|
||||||
|
{
|
||||||
selector_.stopCondition(fn); return *this;
|
selector_.stopCondition(fn); return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline PackGroup lastResult() {
|
inline const PackGroup& lastResult() const
|
||||||
PackGroup ret;
|
{
|
||||||
for(size_t i = 0; i < selector_.binCount(); i++) {
|
return selector_.getResult();
|
||||||
auto items = selector_.itemsForBin(i);
|
|
||||||
ret.push_back(items);
|
|
||||||
}
|
}
|
||||||
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:
|
private:
|
||||||
|
@ -892,7 +906,7 @@ private:
|
||||||
// have to exist for the lifetime of this call.
|
// have to exist for the lifetime of this call.
|
||||||
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
|
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);
|
__execute(from, to);
|
||||||
return lastResult();
|
return lastResult();
|
||||||
|
@ -902,7 +916,7 @@ private:
|
||||||
class IT = remove_cvref_t<typename TIterator::value_type>,
|
class IT = remove_cvref_t<typename TIterator::value_type>,
|
||||||
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
|
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};
|
item_cache_ = {from, to};
|
||||||
|
|
||||||
|
@ -946,10 +960,12 @@ private:
|
||||||
TSel& selector)
|
TSel& selector)
|
||||||
{
|
{
|
||||||
IndexedPackGroup pg;
|
IndexedPackGroup pg;
|
||||||
pg.reserve(selector.binCount());
|
pg.reserve(selector.getResult().size());
|
||||||
|
|
||||||
for(size_t i = 0; i < selector.binCount(); i++) {
|
const PackGroup& pckgrp = selector.getResult();
|
||||||
auto items = selector.itemsForBin(i);
|
|
||||||
|
for(size_t i = 0; i < pckgrp.size(); i++) {
|
||||||
|
auto items = pckgrp[i];
|
||||||
pg.push_back({});
|
pg.push_back({});
|
||||||
pg[i].reserve(items.size());
|
pg[i].reserve(items.size());
|
||||||
|
|
||||||
|
|
|
@ -48,12 +48,12 @@ else()
|
||||||
target_link_libraries(NloptOptimizer INTERFACE Nlopt::Nlopt)
|
target_link_libraries(NloptOptimizer INTERFACE Nlopt::Nlopt)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
#target_sources( NloptOptimizer INTERFACE
|
target_sources( NloptOptimizer INTERFACE
|
||||||
#${CMAKE_CURRENT_SOURCE_DIR}/simplex.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/simplex.hpp
|
||||||
#${CMAKE_CURRENT_SOURCE_DIR}/subplex.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/subplex.hpp
|
||||||
#${CMAKE_CURRENT_SOURCE_DIR}/genetic.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/genetic.hpp
|
||||||
#${CMAKE_CURRENT_SOURCE_DIR}/nlopt_boilerplate.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/nlopt_boilerplate.hpp
|
||||||
#)
|
)
|
||||||
|
|
||||||
target_compile_definitions(NloptOptimizer INTERFACE LIBNEST2D_OPTIMIZER_NLOPT)
|
target_compile_definitions(NloptOptimizer INTERFACE LIBNEST2D_OPTIMIZER_NLOPT)
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,7 @@ namespace placers {
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
struct NfpPConfig {
|
struct NfpPConfig {
|
||||||
|
|
||||||
using ItemGroup = _ItemGroup<_Item<RawShape>>;
|
using ItemGroup = _ItemGroup<RawShape>;
|
||||||
|
|
||||||
enum class Alignment {
|
enum class Alignment {
|
||||||
CENTER,
|
CENTER,
|
||||||
|
@ -138,6 +138,8 @@ struct NfpPConfig {
|
||||||
BOTTOM_RIGHT,
|
BOTTOM_RIGHT,
|
||||||
TOP_LEFT,
|
TOP_LEFT,
|
||||||
TOP_RIGHT,
|
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.
|
/// Which angles to try out for better results.
|
||||||
|
@ -545,8 +547,8 @@ public:
|
||||||
_NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default;
|
_NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default;
|
||||||
|
|
||||||
#ifndef BP2D_COMPILER_MSVC12 // MSVC2013 does not support default move ctors
|
#ifndef BP2D_COMPILER_MSVC12 // MSVC2013 does not support default move ctors
|
||||||
_NofitPolyPlacer(_NofitPolyPlacer&&) /*BP2D_NOEXCEPT*/ = default;
|
_NofitPolyPlacer(_NofitPolyPlacer&&) = default;
|
||||||
_NofitPolyPlacer& operator=(_NofitPolyPlacer&&) /*BP2D_NOEXCEPT*/ = default;
|
_NofitPolyPlacer& operator=(_NofitPolyPlacer&&) = default;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static inline double overfit(const Box& bb, const RawShape& bin) {
|
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
|
// This is the kernel part of the object function that is
|
||||||
// customizable by the library client
|
// customizable by the library client
|
||||||
auto _objfunc = config_.object_function?
|
std::function<double(const Item&)> _objfunc;
|
||||||
config_.object_function :
|
if(config_.object_function) _objfunc = config_.object_function;
|
||||||
[norm, bin, binbb, pbb](const Item& item)
|
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 ibb = item.boundingBox();
|
||||||
auto fullbb = boundingBox(pbb, ibb);
|
auto fullbb = boundingBox(pbb, ibb);
|
||||||
|
|
||||||
double score = pl::distance(ibb.center(), binbb.center());
|
double score = pl::distance(ibb.center(),
|
||||||
|
binbb.center());
|
||||||
score /= norm;
|
score /= norm;
|
||||||
|
|
||||||
double miss = overfit(fullbb, bin);
|
score += ins_check(fullbb);
|
||||||
miss = miss > 0? miss : 0;
|
|
||||||
score += std::pow(miss, 2);
|
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Our object function for placement
|
// Our object function for placement
|
||||||
auto rawobjfunc =
|
auto rawobjfunc = [_objfunc, iv, startpos]
|
||||||
[_objfunc, iv, startpos] (Vertex v, Item& itm)
|
(Vertex v, Item& itm)
|
||||||
{
|
{
|
||||||
auto d = v - iv;
|
auto d = v - iv;
|
||||||
d += startpos;
|
d += startpos;
|
||||||
|
@ -938,9 +958,10 @@ private:
|
||||||
ecache[opt.nfpidx].coords(opt.hidx, opt.relpos);
|
ecache[opt.nfpidx].coords(opt.hidx, opt.relpos);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto boundaryCheck =
|
auto alignment = config_.alignment;
|
||||||
[&merged_pile, &getNfpPoint, &item, &bin, &iv, &startpos]
|
|
||||||
(const Optimum& o)
|
auto boundaryCheck = [alignment, &merged_pile, &getNfpPoint,
|
||||||
|
&item, &bin, &iv, &startpos] (const Optimum& o)
|
||||||
{
|
{
|
||||||
auto v = getNfpPoint(o);
|
auto v = getNfpPoint(o);
|
||||||
auto d = v - iv;
|
auto d = v - iv;
|
||||||
|
@ -951,7 +972,12 @@ private:
|
||||||
auto chull = sl::convexHull(merged_pile);
|
auto chull = sl::convexHull(merged_pile);
|
||||||
merged_pile.pop_back();
|
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);
|
Optimum optimum(0, 0);
|
||||||
|
@ -1101,7 +1127,9 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void finalAlign(_Circle<TPoint<RawShape>> cbin) {
|
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;
|
nfp::Shapes<RawShape> m;
|
||||||
m.reserve(items_.size());
|
m.reserve(items_.size());
|
||||||
for(Item& item : items_) m.emplace_back(item.transformedShape());
|
for(Item& item : items_) m.emplace_back(item.transformedShape());
|
||||||
|
@ -1113,7 +1141,9 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void finalAlign(Box bbin) {
|
inline void finalAlign(Box bbin) {
|
||||||
if(items_.empty()) return;
|
if(items_.empty() ||
|
||||||
|
config_.alignment == Config::Alignment::DONT_ALIGN) return;
|
||||||
|
|
||||||
nfp::Shapes<RawShape> m;
|
nfp::Shapes<RawShape> m;
|
||||||
m.reserve(items_.size());
|
m.reserve(items_.size());
|
||||||
for(Item& item : items_) m.emplace_back(item.transformedShape());
|
for(Item& item : items_) m.emplace_back(item.transformedShape());
|
||||||
|
@ -1147,6 +1177,7 @@ private:
|
||||||
cb = bbin.maxCorner();
|
cb = bbin.maxCorner();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default: ; // DONT_ALIGN
|
||||||
}
|
}
|
||||||
|
|
||||||
auto d = cb - ci;
|
auto d = cb - ci;
|
||||||
|
@ -1184,6 +1215,7 @@ private:
|
||||||
cb = bbin.maxCorner();
|
cb = bbin.maxCorner();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default:;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto d = cb - ci;
|
auto d = cb - ci;
|
||||||
|
|
|
@ -12,6 +12,7 @@ class PlacerBoilerplate {
|
||||||
mutable bool farea_valid_ = false;
|
mutable bool farea_valid_ = false;
|
||||||
mutable double farea_ = 0.0;
|
mutable double farea_ = 0.0;
|
||||||
public:
|
public:
|
||||||
|
using ShapeType = RawShape;
|
||||||
using Item = _Item<RawShape>;
|
using Item = _Item<RawShape>;
|
||||||
using Vertex = TPoint<RawShape>;
|
using Vertex = TPoint<RawShape>;
|
||||||
using Segment = _Segment<Vertex>;
|
using Segment = _Segment<Vertex>;
|
||||||
|
@ -19,7 +20,7 @@ public:
|
||||||
using Coord = TCoord<Vertex>;
|
using Coord = TCoord<Vertex>;
|
||||||
using Unit = Coord;
|
using Unit = Coord;
|
||||||
using Config = Cfg;
|
using Config = Cfg;
|
||||||
using ItemGroup = _ItemGroup<Item>;
|
using ItemGroup = _ItemGroup<RawShape>;
|
||||||
using DefaultIter = typename ItemGroup::const_iterator;
|
using DefaultIter = typename ItemGroup::const_iterator;
|
||||||
|
|
||||||
class PackResult {
|
class PackResult {
|
||||||
|
@ -59,8 +60,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Range = ConstItemRange<DefaultIter>>
|
template<class Range = ConstItemRange<DefaultIter>>
|
||||||
bool pack(Item& item,
|
bool pack(Item& item, const Range& rem = Range()) {
|
||||||
const Range& rem = Range()) {
|
|
||||||
auto&& r = static_cast<Subclass*>(this)->trypack(item, rem);
|
auto&& r = static_cast<Subclass*>(this)->trypack(item, rem);
|
||||||
if(r) {
|
if(r) {
|
||||||
items_.push_back(*(r.item_ptr_));
|
items_.push_back(*(r.item_ptr_));
|
||||||
|
@ -69,6 +69,11 @@ public:
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void preload(const ItemGroup& packeditems) {
|
||||||
|
items_.insert(items_.end(), packeditems.begin(), packeditems.end());
|
||||||
|
farea_valid_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
void accept(PackResult& r) {
|
void accept(PackResult& r) {
|
||||||
if(r) {
|
if(r) {
|
||||||
r.item_ptr_->translation(r.move_);
|
r.item_ptr_->translation(r.move_);
|
||||||
|
@ -117,6 +122,7 @@ using Base::bin_; \
|
||||||
using Base::items_; \
|
using Base::items_; \
|
||||||
using Base::config_; \
|
using Base::config_; \
|
||||||
public: \
|
public: \
|
||||||
|
using typename Base::ShapeType; \
|
||||||
using typename Base::Item; \
|
using typename Base::Item; \
|
||||||
using typename Base::ItemGroup; \
|
using typename Base::ItemGroup; \
|
||||||
using typename Base::BinType; \
|
using typename Base::BinType; \
|
||||||
|
|
|
@ -33,7 +33,7 @@ class _DJDHeuristic: public SelectionBoilerplate<RawShape> {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using typename Base::Item;
|
using typename Base::Item;
|
||||||
using typename Base::ItemRef;
|
using ItemRef = std::reference_wrapper<Item>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The Config for DJD heuristic.
|
* @brief The Config for DJD heuristic.
|
||||||
|
@ -126,6 +126,8 @@ public:
|
||||||
|
|
||||||
store_.clear();
|
store_.clear();
|
||||||
store_.reserve(last-first);
|
store_.reserve(last-first);
|
||||||
|
|
||||||
|
// TODO: support preloading
|
||||||
packed_bins_.clear();
|
packed_bins_.clear();
|
||||||
|
|
||||||
std::copy(first, last, std::back_inserter(store_));
|
std::copy(first, last, std::back_inserter(store_));
|
||||||
|
|
|
@ -34,6 +34,10 @@ public:
|
||||||
store_.clear();
|
store_.clear();
|
||||||
auto total = last-first;
|
auto total = last-first;
|
||||||
store_.reserve(total);
|
store_.reserve(total);
|
||||||
|
|
||||||
|
// TODO: support preloading
|
||||||
|
packed_bins_.clear();
|
||||||
|
|
||||||
packed_bins_.emplace_back();
|
packed_bins_.emplace_back();
|
||||||
|
|
||||||
auto makeProgress = [this, &total](
|
auto makeProgress = [this, &total](
|
||||||
|
|
|
@ -36,11 +36,19 @@ public:
|
||||||
|
|
||||||
store_.clear();
|
store_.clear();
|
||||||
store_.reserve(last-first);
|
store_.reserve(last-first);
|
||||||
packed_bins_.clear();
|
|
||||||
|
|
||||||
std::vector<Placer> placers;
|
std::vector<Placer> placers;
|
||||||
placers.reserve(last-first);
|
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_));
|
std::copy(first, last, std::back_inserter(store_));
|
||||||
|
|
||||||
auto sortfunc = [](Item& i1, Item& i2) {
|
auto sortfunc = [](Item& i1, Item& i2) {
|
||||||
|
|
|
@ -9,27 +9,23 @@ namespace libnest2d { namespace selections {
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
class SelectionBoilerplate {
|
class SelectionBoilerplate {
|
||||||
public:
|
public:
|
||||||
|
using ShapeType = RawShape;
|
||||||
using Item = _Item<RawShape>;
|
using Item = _Item<RawShape>;
|
||||||
using ItemRef = std::reference_wrapper<Item>;
|
using ItemGroup = _ItemGroup<RawShape>;
|
||||||
using ItemGroup = std::vector<ItemRef>;
|
using PackGroup = _PackGroup<RawShape>;
|
||||||
using PackGroup = std::vector<ItemGroup>;
|
|
||||||
|
|
||||||
size_t binCount() const { return packed_bins_.size(); }
|
inline const PackGroup& getResult() const {
|
||||||
|
return packed_bins_;
|
||||||
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 void progressIndicator(ProgressFunction fn) { progress_ = fn; }
|
inline void progressIndicator(ProgressFunction fn) { progress_ = fn; }
|
||||||
|
|
||||||
inline void stopCondition(StopCondition cond) { stopcond_ = cond; }
|
inline void stopCondition(StopCondition cond) { stopcond_ = cond; }
|
||||||
|
|
||||||
|
inline void preload(const PackGroup& pckgrp) { packed_bins_ = pckgrp; }
|
||||||
|
|
||||||
|
inline void clear() { packed_bins_.clear(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
PackGroup packed_bins_;
|
PackGroup packed_bins_;
|
||||||
|
|
|
@ -356,13 +356,15 @@ inline double area(const PolygonImpl& shape, const PolygonTag&)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<>
|
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);
|
return boost::geometry::within(point, shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
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);
|
return boost::geometry::within(sh1, sh2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ public:
|
||||||
}
|
}
|
||||||
this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1));
|
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 PointClass &point);
|
||||||
void merge(const std::vector<PointClass> &points);
|
void merge(const std::vector<PointClass> &points);
|
||||||
void merge(const BoundingBoxBase<PointClass> &bb);
|
void merge(const BoundingBoxBase<PointClass> &bb);
|
||||||
|
|
|
@ -310,16 +310,15 @@ ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines)
|
||||||
polylines->insert(polylines->end(), tp.begin(), tp.end());
|
polylines->insert(polylines->end(), tp.begin(), tp.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
/*
|
||||||
ExPolygon::get_trapezoids(Polygons* polygons) const
|
void ExPolygon::get_trapezoids(Polygons* polygons) const
|
||||||
{
|
{
|
||||||
ExPolygons expp;
|
ExPolygons expp;
|
||||||
expp.push_back(*this);
|
expp.push_back(*this);
|
||||||
boost::polygon::get_trapezoids(*polygons, expp);
|
boost::polygon::get_trapezoids(*polygons, expp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ExPolygon::get_trapezoids(Polygons* polygons, double angle) const
|
||||||
ExPolygon::get_trapezoids(Polygons* polygons, double angle) const
|
|
||||||
{
|
{
|
||||||
ExPolygon clone = *this;
|
ExPolygon clone = *this;
|
||||||
clone.rotate(PI/2 - angle, Point(0,0));
|
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)
|
for (Polygons::iterator polygon = polygons->begin(); polygon != polygons->end(); ++polygon)
|
||||||
polygon->rotate(-(PI/2 - angle), Point(0,0));
|
polygon->rotate(-(PI/2 - angle), Point(0,0));
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// This algorithm may return more trapezoids than necessary
|
// This algorithm may return more trapezoids than necessary
|
||||||
// (i.e. it may break a single trapezoid in several because
|
// (i.e. it may break a single trapezoid in several because
|
||||||
// other parts of the object have x coordinates in the middle)
|
// other parts of the object have x coordinates in the middle)
|
||||||
void
|
void ExPolygon::get_trapezoids2(Polygons* polygons) const
|
||||||
ExPolygon::get_trapezoids2(Polygons* polygons) const
|
|
||||||
{
|
{
|
||||||
// get all points of this ExPolygon
|
// get all points of this ExPolygon
|
||||||
Points pp = *this;
|
Points pp = *this;
|
||||||
|
@ -370,8 +369,7 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void ExPolygon::get_trapezoids2(Polygons* polygons, double angle) const
|
||||||
ExPolygon::get_trapezoids2(Polygons* polygons, double angle) const
|
|
||||||
{
|
{
|
||||||
ExPolygon clone = *this;
|
ExPolygon clone = *this;
|
||||||
clone.rotate(PI/2 - angle, Point(0,0));
|
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
|
// While this triangulates successfully, it's NOT a constrained triangulation
|
||||||
// as it will create more vertices on the boundaries than the ones supplied.
|
// as it will create more vertices on the boundaries than the ones supplied.
|
||||||
void
|
void ExPolygon::triangulate(Polygons* polygons) const
|
||||||
ExPolygon::triangulate(Polygons* polygons) const
|
|
||||||
{
|
{
|
||||||
// first make trapezoids
|
// first make trapezoids
|
||||||
Polygons trapezoids;
|
Polygons trapezoids;
|
||||||
|
@ -394,8 +391,8 @@ ExPolygon::triangulate(Polygons* polygons) const
|
||||||
polygon->triangulate_convex(polygons);
|
polygon->triangulate_convex(polygons);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
/*
|
||||||
ExPolygon::triangulate_pp(Polygons* polygons) const
|
void ExPolygon::triangulate_pp(Polygons* polygons) const
|
||||||
{
|
{
|
||||||
// convert polygons
|
// convert polygons
|
||||||
std::list<TPPLPoly> input;
|
std::list<TPPLPoly> input;
|
||||||
|
@ -452,9 +449,113 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
|
||||||
polygons->push_back(p);
|
polygons->push_back(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
void
|
std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygon &ex)
|
||||||
ExPolygon::triangulate_p2t(Polygons* polygons) const
|
{
|
||||||
|
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);
|
ExPolygons expp = simplify_polygons_ex(*this, true);
|
||||||
|
|
||||||
|
@ -478,6 +579,7 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const
|
||||||
}
|
}
|
||||||
|
|
||||||
// perform triangulation
|
// perform triangulation
|
||||||
|
try {
|
||||||
cdt.Triangulate();
|
cdt.Triangulate();
|
||||||
std::vector<p2t::Triangle*> triangles = cdt.GetTriangles();
|
std::vector<p2t::Triangle*> triangles = cdt.GetTriangles();
|
||||||
|
|
||||||
|
@ -489,14 +591,17 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const
|
||||||
}
|
}
|
||||||
polygons->push_back(p);
|
polygons->push_back(p);
|
||||||
}
|
}
|
||||||
|
} catch (const std::runtime_error & /* err */) {
|
||||||
|
assert(false);
|
||||||
|
// just ignore, don't triangulate
|
||||||
|
}
|
||||||
|
|
||||||
for (p2t::Point *ptr : ContourPoints)
|
for (p2t::Point *ptr : ContourPoints)
|
||||||
delete ptr;
|
delete ptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Lines
|
Lines ExPolygon::lines() const
|
||||||
ExPolygon::lines() const
|
|
||||||
{
|
{
|
||||||
Lines lines = this->contour.lines();
|
Lines lines = this->contour.lines();
|
||||||
for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) {
|
for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) {
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
#include "Polyline.hpp"
|
#include "Polyline.hpp"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
// polygon class of the polypartition library
|
||||||
|
class TPPLPoly;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
class ExPolygon;
|
class ExPolygon;
|
||||||
|
@ -55,12 +58,13 @@ public:
|
||||||
void simplify(double tolerance, ExPolygons* expolygons) const;
|
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, ThickPolylines* polylines) const;
|
||||||
void medial_axis(double max_width, double min_width, Polylines* 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) const;
|
||||||
void get_trapezoids(Polygons* polygons, double angle) const;
|
// void get_trapezoids(Polygons* polygons, double angle) const;
|
||||||
void get_trapezoids2(Polygons* polygons) const;
|
void get_trapezoids2(Polygons* polygons) const;
|
||||||
void get_trapezoids2(Polygons* polygons, double angle) const;
|
void get_trapezoids2(Polygons* polygons, double angle) const;
|
||||||
void triangulate(Polygons* polygons) 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;
|
void triangulate_p2t(Polygons* polygons) const;
|
||||||
Lines lines() 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 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
|
} // namespace Slic3r
|
||||||
|
|
||||||
// start Boost
|
// start Boost
|
||||||
|
|
|
@ -6,9 +6,12 @@
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include "../libslic3r.h"
|
#include "../libslic3r.h"
|
||||||
#include "../BoundingBox.hpp"
|
#include "../BoundingBox.hpp"
|
||||||
#include "../PrintConfig.hpp"
|
#include "../PrintConfig.hpp"
|
||||||
|
#include "../Utils.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -38,6 +41,7 @@ struct FillParams
|
||||||
// in this case we don't try to make more continuous paths
|
// in this case we don't try to make more continuous paths
|
||||||
bool complete;
|
bool complete;
|
||||||
};
|
};
|
||||||
|
static_assert(IsTriviallyCopyable<FillParams>::value, "FillParams class is not POD (and it should be - see constructor).");
|
||||||
|
|
||||||
class Fill
|
class Fill
|
||||||
{
|
{
|
||||||
|
|
|
@ -582,10 +582,7 @@ namespace Slic3r {
|
||||||
|
|
||||||
IdToLayerHeightsProfileMap::iterator obj_layer_heights_profile = m_layer_heights_profiles.find(object.first);
|
IdToLayerHeightsProfileMap::iterator obj_layer_heights_profile = m_layer_heights_profiles.find(object.first);
|
||||||
if (obj_layer_heights_profile != m_layer_heights_profiles.end())
|
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 = 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);
|
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())
|
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)
|
for (const ModelObject* object : model.objects)
|
||||||
{
|
{
|
||||||
++count;
|
++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))
|
if ((layer_height_profile.size() >= 4) && ((layer_height_profile.size() % 2) == 0))
|
||||||
{
|
{
|
||||||
sprintf(buffer, "object_id=%d|", count);
|
sprintf(buffer, "object_id=%d|", count);
|
||||||
|
|
|
@ -528,6 +528,7 @@ void AMFParserContext::endElement(const char * /* name */)
|
||||||
}
|
}
|
||||||
stl_get_size(&stl);
|
stl_get_size(&stl);
|
||||||
m_volume->mesh.repair();
|
m_volume->mesh.repair();
|
||||||
|
m_volume->center_geometry();
|
||||||
m_volume->calculate_convex_hull();
|
m_volume->calculate_convex_hull();
|
||||||
m_volume_facets.clear();
|
m_volume_facets.clear();
|
||||||
m_volume = nullptr;
|
m_volume = nullptr;
|
||||||
|
@ -578,7 +579,6 @@ void AMFParserContext::endElement(const char * /* name */)
|
||||||
break;
|
break;
|
||||||
p = end + 1;
|
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) {
|
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.
|
// Parse object's layer height profile, a semicolon separated list of floats.
|
||||||
|
@ -642,7 +642,11 @@ void AMFParserContext::endDocument()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (const Instance &instance : object.second.instances)
|
for (const Instance &instance : object.second.instances)
|
||||||
|
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
|
{
|
||||||
|
#else
|
||||||
if (instance.deltax_set && instance.deltay_set) {
|
if (instance.deltax_set && instance.deltay_set) {
|
||||||
|
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
ModelInstance *mi = m_model.objects[object.second.idx]->add_instance();
|
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_offset(Vec3d(instance.deltax_set ? (double)instance.deltax : 0.0, instance.deltay_set ? (double)instance.deltay : 0.0, instance.deltaz_set ? (double)instance.deltaz : 0.0));
|
||||||
mi->set_rotation(Vec3d(instance.rx_set ? (double)instance.rx : 0.0, instance.ry_set ? (double)instance.ry : 0.0, instance.rz_set ? (double)instance.rz : 0.0));
|
mi->set_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";
|
stream << " <metadata type=\"slic3r." << key << "\">" << object->config.serialize(key) << "</metadata>\n";
|
||||||
if (!object->name.empty())
|
if (!object->name.empty())
|
||||||
stream << " <metadata type=\"name\">" << xml_escape(object->name) << "</metadata>\n";
|
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) {
|
if (layer_height_profile.size() >= 4 && (layer_height_profile.size() % 2) == 0) {
|
||||||
// Store the layer height profile as a single semicolon separated list.
|
// Store the layer height profile as a single semicolon separated list.
|
||||||
stream << " <metadata type=\"slic3r.layer_height_profile\">";
|
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;
|
auto &stl = volume->mesh.stl;
|
||||||
if (stl.v_shared == nullptr)
|
if (stl.v_shared == nullptr)
|
||||||
stl_generate_shared_vertices(&stl);
|
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 << " <vertex>\n";
|
||||||
stream << " <coordinates>\n";
|
stream << " <coordinates>\n";
|
||||||
stream << " <x>" << stl.v_shared[i](0) << "</x>\n";
|
Vec3d v = matrix * stl.v_shared[i].cast<double>();
|
||||||
stream << " <y>" << stl.v_shared[i](1) << "</y>\n";
|
stream << " <x>" << v(0) << "</x>\n";
|
||||||
stream << " <z>" << stl.v_shared[i](2) << "</z>\n";
|
stream << " <y>" << v(1) << "</y>\n";
|
||||||
|
stream << " <z>" << v(2) << "</z>\n";
|
||||||
stream << " </coordinates>\n";
|
stream << " </coordinates>\n";
|
||||||
stream << " </vertex>\n";
|
stream << " </vertex>\n";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1589,9 +1589,11 @@ void GCode::process_layer(
|
||||||
auto objects_by_extruder_it = by_extruder.find(extruder_id);
|
auto objects_by_extruder_it = by_extruder.find(extruder_id);
|
||||||
if (objects_by_extruder_it == by_extruder.end())
|
if (objects_by_extruder_it == by_extruder.end())
|
||||||
continue;
|
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):
|
// 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) {
|
bool is_anything_overridden = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden();
|
||||||
if (print_wipe_extrusions == 0)
|
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";
|
gcode+="; PURGING FINISHED\n";
|
||||||
|
|
||||||
for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
|
for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
|
||||||
|
@ -1628,7 +1630,7 @@ void GCode::process_layer(
|
||||||
m_layer = layers[layer_id].layer();
|
m_layer = layers[layer_id].layer();
|
||||||
}
|
}
|
||||||
for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
|
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) {
|
if (print.config().infill_first) {
|
||||||
gcode += this->extrude_infill(print, by_region_specific);
|
gcode += this->extrude_infill(print, by_region_specific);
|
||||||
|
|
|
@ -1,24 +1,140 @@
|
||||||
#include "PostProcessor.hpp"
|
#include "PostProcessor.hpp"
|
||||||
|
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
#ifdef WIN32
|
#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
|
// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
|
||||||
void run_post_process_scripts(const std::string &path, const PrintConfig &config)
|
// 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
|
#else
|
||||||
|
|
||||||
#include <boost/process/system.hpp>
|
|
||||||
#ifndef WIN32
|
|
||||||
#include <sys/stat.h> //for getting filesystem UID/GID
|
#include <sys/stat.h> //for getting filesystem UID/GID
|
||||||
#include <unistd.h> //for getting current UID/GID
|
#include <unistd.h> //for getting current UID/GID
|
||||||
|
#include <boost/process.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
@ -33,15 +149,23 @@ void run_post_process_scripts(const std::string &path, const PrintConfig &config
|
||||||
if (! boost::filesystem::exists(gcode_file))
|
if (! boost::filesystem::exists(gcode_file))
|
||||||
throw std::runtime_error(std::string("Post-processor can't find exported 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.
|
// Ignore empty post processing script lines.
|
||||||
boost::trim(script);
|
boost::trim(script);
|
||||||
if (script.empty())
|
if (script.empty())
|
||||||
continue;
|
continue;
|
||||||
BOOST_LOG_TRIVIAL(info) << "Executing script " << script << " on file " << path;
|
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)))
|
if (! boost::filesystem::exists(boost::filesystem::path(script)))
|
||||||
throw std::runtime_error(std::string("The configured post-processing script does not exist: ") + script);
|
throw std::runtime_error(std::string("The configured post-processing script does not exist: ") + script);
|
||||||
#ifndef WIN32
|
|
||||||
struct stat info;
|
struct stat info;
|
||||||
if (stat(script.c_str(), &info))
|
if (stat(script.c_str(), &info))
|
||||||
throw std::runtime_error(std::string("Cannot read information for post-processing script: ") + script);
|
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
|
: ((info.st_gid == getegid()) ? boost::filesystem::perms::group_exe
|
||||||
: boost::filesystem::perms::others_exe))))
|
: boost::filesystem::perms::others_exe))))
|
||||||
throw std::runtime_error(std::string("The configured post-processing script is not executable: check permissions. ") + script);
|
throw std::runtime_error(std::string("The configured post-processing script is not executable: check permissions. ") + script);
|
||||||
#endif
|
int result = boost::process::system(script, gcode_file);
|
||||||
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
|
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
BOOST_LOG_TRIVIAL(error) << "Script " << script << " on file " << path << " failed. Negative error code returned.";
|
BOOST_LOG_TRIVIAL(error) << "Script " << script << " on file " << path << " failed. Negative error code returned.";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -207,8 +207,7 @@ static bool sort_pointfs(const Vec3d& a, const Vec3d& b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This implementation is based on Andrew's monotone chain 2D convex hull algorithm
|
// This implementation is based on Andrew's monotone chain 2D convex hull algorithm
|
||||||
Polygon
|
Polygon convex_hull(Points points)
|
||||||
convex_hull(Points points)
|
|
||||||
{
|
{
|
||||||
assert(points.size() >= 3);
|
assert(points.size() >= 3);
|
||||||
// sort input points
|
// 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)
|
Vec3d extract_euler_angles(const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>& rotation_matrix)
|
||||||
{
|
{
|
||||||
#if ENABLE_NEW_EULER_ANGLES
|
#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; };
|
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);
|
Vec3d angles1 = Vec3d::Zero();
|
||||||
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);
|
Vec3d angles2 = Vec3d::Zero();
|
||||||
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);
|
if (is_approx(std::abs(rotation_matrix(2, 0)), 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)
|
|
||||||
{
|
{
|
||||||
angles = rotation_matrix.eulerAngles(0, 1, 2);
|
angles1(2) = 0.0;
|
||||||
if (x_only && (std::abs(angles(1)) == (double)PI) && (std::abs(angles(2)) == (double)PI))
|
if (rotation_matrix(2, 0) < 0.0) // == -1.0
|
||||||
{
|
{
|
||||||
angles(0) -= (double)PI;
|
angles1(1) = 0.5 * (double)PI;
|
||||||
angles(1) = 0.0;
|
angles1(0) = angles1(2) + ::atan2(rotation_matrix(0, 1), rotation_matrix(0, 2));
|
||||||
angles(2) = 0.0;
|
|
||||||
}
|
}
|
||||||
|
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
|
else
|
||||||
{
|
{
|
||||||
double cy_abs = ::sqrt(sqr(rotation_matrix(0, 0)) + sqr(rotation_matrix(1, 0)));
|
angles1(1) = -::asin(rotation_matrix(2, 0));
|
||||||
angles(0) = ::atan2(rotation_matrix(2, 1), rotation_matrix(2, 2));
|
double inv_cos1 = 1.0 / ::cos(angles1(1));
|
||||||
angles(1) = ::atan2(-rotation_matrix(2, 0), cy_abs);
|
angles1(0) = ::atan2(rotation_matrix(2, 1) * inv_cos1, rotation_matrix(2, 2) * inv_cos1);
|
||||||
angles(2) = ::atan2(rotation_matrix(1, 0), rotation_matrix(0, 0));
|
angles1(2) = ::atan2(rotation_matrix(1, 0) * inv_cos1, rotation_matrix(0, 0) * inv_cos1);
|
||||||
if (yx_only && (angles(2) == (double)PI))
|
|
||||||
{
|
angles2(1) = (double)PI - angles1(1);
|
||||||
angles(0) -= (double)PI;
|
double inv_cos2 = 1.0 / ::cos(angles2(1));
|
||||||
angles(1) = (double)PI - angles(1);
|
angles2(0) = ::atan2(rotation_matrix(2, 1) * inv_cos2, rotation_matrix(2, 2) * inv_cos2);
|
||||||
angles(2) = 0.0;
|
angles2(2) = ::atan2(rotation_matrix(1, 0) * inv_cos2, rotation_matrix(0, 0) * inv_cos2);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// // debug check
|
// 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)
|
||||||
// Geometry::Transformation t;
|
// but there are other use-cases were it does not
|
||||||
// t.set_rotation(angles);
|
// We need to improve it
|
||||||
// if (!t.get_matrix().matrix().block(0, 0, 3, 3).isApprox(rotation_matrix))
|
double min_1 = angles1.cwiseAbs().minCoeff();
|
||||||
// {
|
double min_2 = angles2.cwiseAbs().minCoeff();
|
||||||
// std::cout << "something went wrong in extracting euler angles from matrix" << std::endl;
|
bool use_1 = (min_1 < min_2) || (is_approx(min_1, min_2) && (angles1.norm() <= angles2.norm()));
|
||||||
//
|
|
||||||
//// Eigen::Matrix<double, 3, 3, Eigen::DontAlign> m = t.get_matrix().matrix().block(0, 0, 3, 3);
|
Vec3d angles = use_1 ? angles1 : angles2;
|
||||||
//// 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;
|
|
||||||
//// }
|
|
||||||
//// }
|
|
||||||
// }
|
|
||||||
#else
|
#else
|
||||||
auto y_only = [](const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>& matrix) -> bool {
|
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);
|
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()
|
Transformation::Transformation()
|
||||||
|
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
: m_offset(Vec3d::Zero())
|
: m_offset(Vec3d::Zero())
|
||||||
, m_rotation(Vec3d::Zero())
|
, m_rotation(Vec3d::Zero())
|
||||||
, m_scaling_factor(Vec3d::Ones())
|
, m_scaling_factor(Vec3d::Ones())
|
||||||
, m_mirror(Vec3d::Ones())
|
, m_mirror(Vec3d::Ones())
|
||||||
, m_matrix(Transform3d::Identity())
|
, m_matrix(Transform3d::Identity())
|
||||||
, m_dirty(false)
|
, m_dirty(false)
|
||||||
|
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
{
|
{
|
||||||
|
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
|
reset();
|
||||||
|
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
}
|
}
|
||||||
|
|
||||||
Transformation::Transformation(const Transform3d& transform)
|
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;
|
// 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
|
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))
|
if (m_dirty || m_flags.needs_update(dont_translate, dont_rotate, dont_scale, dont_mirror))
|
||||||
|
|
|
@ -237,7 +237,7 @@ public:
|
||||||
void set_rotation(const Vec3d& rotation);
|
void set_rotation(const Vec3d& rotation);
|
||||||
void set_rotation(Axis axis, double 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); }
|
double get_scaling_factor(Axis axis) const { return m_scaling_factor(axis); }
|
||||||
|
|
||||||
void set_scaling_factor(const Vec3d& scaling_factor);
|
void set_scaling_factor(const Vec3d& scaling_factor);
|
||||||
|
@ -251,6 +251,10 @@ public:
|
||||||
|
|
||||||
void set_from_transform(const Transform3d& transform);
|
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;
|
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;
|
Transformation operator * (const Transformation& other) const;
|
||||||
|
|
|
@ -570,10 +570,11 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
|
||||||
this->sla_support_points = rhs.sla_support_points;
|
this->sla_support_points = rhs.sla_support_points;
|
||||||
this->layer_height_ranges = rhs.layer_height_ranges;
|
this->layer_height_ranges = rhs.layer_height_ranges;
|
||||||
this->layer_height_profile = rhs.layer_height_profile;
|
this->layer_height_profile = rhs.layer_height_profile;
|
||||||
this->layer_height_profile_valid = rhs.layer_height_profile_valid;
|
|
||||||
this->origin_translation = rhs.origin_translation;
|
this->origin_translation = rhs.origin_translation;
|
||||||
m_bounding_box = rhs.m_bounding_box;
|
m_bounding_box = rhs.m_bounding_box;
|
||||||
m_bounding_box_valid = rhs.m_bounding_box_valid;
|
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->clear_volumes();
|
||||||
this->volumes.reserve(rhs.volumes.size());
|
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->sla_support_points = std::move(rhs.sla_support_points);
|
||||||
this->layer_height_ranges = std::move(rhs.layer_height_ranges);
|
this->layer_height_ranges = std::move(rhs.layer_height_ranges);
|
||||||
this->layer_height_profile = std::move(rhs.layer_height_profile);
|
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);
|
this->origin_translation = std::move(rhs.origin_translation);
|
||||||
m_bounding_box = std::move(rhs.m_bounding_box);
|
m_bounding_box = std::move(rhs.m_bounding_box);
|
||||||
m_bounding_box_valid = std::move(rhs.m_bounding_box_valid);
|
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->clear_volumes();
|
||||||
this->volumes = std::move(rhs.volumes);
|
this->volumes = std::move(rhs.volumes);
|
||||||
|
@ -641,6 +643,9 @@ ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh)
|
||||||
{
|
{
|
||||||
ModelVolume* v = new ModelVolume(this, mesh);
|
ModelVolume* v = new ModelVolume(this, mesh);
|
||||||
this->volumes.push_back(v);
|
this->volumes.push_back(v);
|
||||||
|
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
|
v->center_geometry();
|
||||||
|
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
this->invalidate_bounding_box();
|
this->invalidate_bounding_box();
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
@ -649,6 +654,9 @@ ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh)
|
||||||
{
|
{
|
||||||
ModelVolume* v = new ModelVolume(this, std::move(mesh));
|
ModelVolume* v = new ModelVolume(this, std::move(mesh));
|
||||||
this->volumes.push_back(v);
|
this->volumes.push_back(v);
|
||||||
|
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
|
v->center_geometry();
|
||||||
|
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
this->invalidate_bounding_box();
|
this->invalidate_bounding_box();
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
@ -657,6 +665,9 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other)
|
||||||
{
|
{
|
||||||
ModelVolume* v = new ModelVolume(this, other);
|
ModelVolume* v = new ModelVolume(this, other);
|
||||||
this->volumes.push_back(v);
|
this->volumes.push_back(v);
|
||||||
|
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
|
v->center_geometry();
|
||||||
|
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
this->invalidate_bounding_box();
|
this->invalidate_bounding_box();
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
@ -665,6 +676,9 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&me
|
||||||
{
|
{
|
||||||
ModelVolume* v = new ModelVolume(this, other, std::move(mesh));
|
ModelVolume* v = new ModelVolume(this, other, std::move(mesh));
|
||||||
this->volumes.push_back(v);
|
this->volumes.push_back(v);
|
||||||
|
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
|
v->center_geometry();
|
||||||
|
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
this->invalidate_bounding_box();
|
this->invalidate_bounding_box();
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
@ -675,6 +689,23 @@ void ModelObject::delete_volume(size_t idx)
|
||||||
delete *i;
|
delete *i;
|
||||||
this->volumes.erase(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)
|
if (this->volumes.size() == 1)
|
||||||
{
|
{
|
||||||
// only one volume left
|
// only one volume left
|
||||||
|
@ -691,6 +722,7 @@ void ModelObject::delete_volume(size_t idx)
|
||||||
v->set_offset(Vec3d::Zero());
|
v->set_offset(Vec3d::Zero());
|
||||||
v->set_new_unique_id();
|
v->set_new_unique_id();
|
||||||
}
|
}
|
||||||
|
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
|
|
||||||
this->invalidate_bounding_box();
|
this->invalidate_bounding_box();
|
||||||
}
|
}
|
||||||
|
@ -755,19 +787,11 @@ void ModelObject::clear_instances()
|
||||||
const BoundingBoxf3& ModelObject::bounding_box() const
|
const BoundingBoxf3& ModelObject::bounding_box() const
|
||||||
{
|
{
|
||||||
if (! m_bounding_box_valid) {
|
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;
|
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;
|
return m_bounding_box;
|
||||||
}
|
}
|
||||||
|
@ -814,6 +838,26 @@ TriangleMesh ModelObject::full_raw_mesh() const
|
||||||
return mesh;
|
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.
|
// 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.
|
// This bounding box is only used for the actual slicing.
|
||||||
BoundingBoxf3 ModelObject::raw_bounding_box() const
|
BoundingBoxf3 ModelObject::raw_bounding_box() const
|
||||||
|
@ -868,14 +912,72 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
|
||||||
return bb;
|
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()
|
void ModelObject::center_around_origin()
|
||||||
{
|
{
|
||||||
// calculate the displacements needed to
|
// calculate the displacements needed to
|
||||||
// center this object around the origin
|
// center this object around the origin
|
||||||
|
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
|
BoundingBoxf3 bb = full_raw_mesh_bounding_box();
|
||||||
|
#else
|
||||||
BoundingBoxf3 bb;
|
BoundingBoxf3 bb;
|
||||||
for (ModelVolume *v : this->volumes)
|
for (ModelVolume *v : this->volumes)
|
||||||
if (v->is_model_part())
|
if (v->is_model_part())
|
||||||
bb.merge(v->mesh.bounding_box());
|
bb.merge(v->mesh.bounding_box());
|
||||||
|
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
|
|
||||||
// Shift is the vector from the center of the bounding box to the origin
|
// Shift is the vector from the center of the bounding box to the origin
|
||||||
Vec3d shift = -bb.center();
|
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_upper) { upper->add_volume(*volume); }
|
||||||
if (keep_lower) { lower->add_volume(*volume); }
|
if (keep_lower) { lower->add_volume(*volume); }
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
TriangleMesh upper_mesh, lower_mesh;
|
TriangleMesh upper_mesh, lower_mesh;
|
||||||
|
|
||||||
// Transform the mesh by the combined transformation matrix
|
// 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
|
// Perform cut
|
||||||
TriangleMeshSlicer tms(&volume->mesh);
|
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
|
// Reset volume transformation except for offset
|
||||||
const Vec3d offset = volume->get_offset();
|
const Vec3d offset = volume->get_offset();
|
||||||
|
@ -1153,7 +1256,9 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
|
||||||
for (const ModelInstance *model_instance : this->instances)
|
for (const ModelInstance *model_instance : this->instances)
|
||||||
new_object->add_instance(*model_instance);
|
new_object->add_instance(*model_instance);
|
||||||
ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh));
|
ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh));
|
||||||
|
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
new_vol->center_geometry();
|
new_vol->center_geometry();
|
||||||
|
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
|
|
||||||
for (ModelInstance* model_instance : new_object->instances)
|
for (ModelInstance* model_instance : new_object->instances)
|
||||||
{
|
{
|
||||||
|
@ -1321,10 +1426,20 @@ int ModelVolume::extruder_id() const
|
||||||
|
|
||||||
void ModelVolume::center_geometry()
|
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();
|
Vec3d shift = -mesh.bounding_box().center();
|
||||||
mesh.translate((float)shift(0), (float)shift(1), (float)shift(2));
|
mesh.translate((float)shift(0), (float)shift(1), (float)shift(2));
|
||||||
m_convex_hull.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);
|
translate(-shift);
|
||||||
|
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelVolume::calculate_convex_hull()
|
void ModelVolume::calculate_convex_hull()
|
||||||
|
|
|
@ -170,12 +170,8 @@ public:
|
||||||
// Variation of a layer thickness for spans of Z coordinates.
|
// Variation of a layer thickness for spans of Z coordinates.
|
||||||
t_layer_height_ranges layer_height_ranges;
|
t_layer_height_ranges layer_height_ranges;
|
||||||
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
|
// 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;
|
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
|
// This vector holds position of selected support points for SLA. The data are
|
||||||
// saved in mesh coordinates to allow using them for several instances.
|
// 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 approximate and not snug.
|
||||||
// This bounding box is being cached.
|
// This bounding box is being cached.
|
||||||
const BoundingBoxf3& bounding_box() const;
|
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.
|
// A mesh containing all transformed instances of this object.
|
||||||
TriangleMesh mesh() const;
|
TriangleMesh mesh() const;
|
||||||
|
@ -223,6 +219,16 @@ public:
|
||||||
BoundingBoxf3 raw_bounding_box() const;
|
BoundingBoxf3 raw_bounding_box() const;
|
||||||
// A snug bounding box around the transformed non-modifier object volumes.
|
// A snug bounding box around the transformed non-modifier object volumes.
|
||||||
BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const;
|
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 center_around_origin();
|
||||||
void ensure_on_bed();
|
void ensure_on_bed();
|
||||||
void translate_instances(const Vec3d& vector);
|
void translate_instances(const Vec3d& vector);
|
||||||
|
@ -261,7 +267,8 @@ protected:
|
||||||
void set_model(Model *model) { m_model = model; }
|
void set_model(Model *model) { m_model = model; }
|
||||||
|
|
||||||
private:
|
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();
|
~ModelObject();
|
||||||
|
|
||||||
/* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
|
/* 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.
|
// Bounding box, cached.
|
||||||
mutable BoundingBoxf3 m_bounding_box;
|
mutable BoundingBoxf3 m_bounding_box;
|
||||||
mutable bool m_bounding_box_valid;
|
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.
|
// 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).
|
// 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;
|
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.
|
// Split this volume, append the result to the object owning this volume.
|
||||||
// Return the number of volumes created from this one.
|
// Return the number of volumes created from this one.
|
||||||
// This is useful to assign different materials to different volumes of an object.
|
// This is useful to assign different materials to different volumes of an object.
|
||||||
|
@ -391,6 +403,12 @@ private:
|
||||||
TriangleMesh m_convex_hull;
|
TriangleMesh m_convex_hull;
|
||||||
Geometry::Transformation m_transformation;
|
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)
|
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object)
|
||||||
{
|
{
|
||||||
if (mesh.stl.stats.number_of_facets > 1)
|
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(const Vec3d& rotation) { m_transformation.set_rotation(rotation); }
|
||||||
void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, 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); }
|
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); }
|
void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); }
|
||||||
|
|
|
@ -15,6 +15,7 @@ namespace arr {
|
||||||
|
|
||||||
using namespace libnest2d;
|
using namespace libnest2d;
|
||||||
|
|
||||||
|
// Only for debugging. Prints the model object vertices on stdout.
|
||||||
std::string toString(const Model& model, bool holes = true) {
|
std::string toString(const Model& model, bool holes = true) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
|
@ -78,6 +79,7 @@ std::string toString(const Model& model, bool holes = true) {
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Debugging: Save model to svg file.
|
||||||
void toSVG(SVG& svg, const Model& model) {
|
void toSVG(SVG& svg, const Model& model) {
|
||||||
for(auto objptr : model.objects) {
|
for(auto objptr : model.objects) {
|
||||||
if(!objptr) continue;
|
if(!objptr) continue;
|
||||||
|
@ -121,6 +123,10 @@ Box boundingBox(const Box& pilebb, const Box& ibb ) {
|
||||||
return Box(minc, maxc);
|
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*/>
|
std::tuple<double /*score*/, Box /*farthest point from bin center*/>
|
||||||
objfunc(const PointImpl& bincenter,
|
objfunc(const PointImpl& bincenter,
|
||||||
const shapelike::Shapes<PolygonImpl>& merged_pile,
|
const shapelike::Shapes<PolygonImpl>& merged_pile,
|
||||||
|
@ -253,6 +259,8 @@ objfunc(const PointImpl& bincenter,
|
||||||
return std::make_tuple(score, fullbb);
|
return std::make_tuple(score, fullbb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fill in the placer algorithm configuration with values carefully chosen for
|
||||||
|
// Slic3r.
|
||||||
template<class PConf>
|
template<class PConf>
|
||||||
void fillConfig(PConf& pcfg) {
|
void fillConfig(PConf& pcfg) {
|
||||||
|
|
||||||
|
@ -274,13 +282,19 @@ void fillConfig(PConf& pcfg) {
|
||||||
pcfg.parallel = true;
|
pcfg.parallel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type trait for an arranger class for different bin types (box, circle,
|
||||||
|
// polygon, etc...)
|
||||||
template<class TBin>
|
template<class TBin>
|
||||||
class AutoArranger {};
|
class AutoArranger {};
|
||||||
|
|
||||||
|
|
||||||
|
// A class encapsulating the libnest2d Nester class and extending it with other
|
||||||
|
// management and spatial index structures for acceleration.
|
||||||
template<class TBin>
|
template<class TBin>
|
||||||
class _ArrBase {
|
class _ArrBase {
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
// Useful type shortcuts...
|
||||||
using Placer = TPacker<TBin>;
|
using Placer = TPacker<TBin>;
|
||||||
using Selector = FirstFitSelection;
|
using Selector = FirstFitSelection;
|
||||||
using Packer = Nester<Placer, Selector>;
|
using Packer = Nester<Placer, Selector>;
|
||||||
|
@ -291,13 +305,13 @@ protected:
|
||||||
Packer m_pck;
|
Packer m_pck;
|
||||||
PConfig m_pconf; // Placement configuration
|
PConfig m_pconf; // Placement configuration
|
||||||
double m_bin_area;
|
double m_bin_area;
|
||||||
SpatIndex m_rtree;
|
SpatIndex m_rtree; // spatial index for the normal (bigger) objects
|
||||||
SpatIndex m_smallsrtree;
|
SpatIndex m_smallsrtree; // spatial index for only the smaller items
|
||||||
double m_norm;
|
double m_norm; // A coefficient to scale distances
|
||||||
Pile m_merged_pile;
|
Pile m_merged_pile; // The already merged pile (vector of items)
|
||||||
Box m_pilebb;
|
Box m_pilebb; // The bounding box of the merged pile.
|
||||||
ItemGroup m_remaining;
|
ItemGroup m_remaining; // Remaining items (m_items at the beginning)
|
||||||
ItemGroup m_items;
|
ItemGroup m_items; // The items to be packed
|
||||||
public:
|
public:
|
||||||
|
|
||||||
_ArrBase(const TBin& bin, Distance dist,
|
_ArrBase(const TBin& bin, Distance dist,
|
||||||
|
@ -308,6 +322,8 @@ public:
|
||||||
{
|
{
|
||||||
fillConfig(m_pconf);
|
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 =
|
m_pconf.before_packing =
|
||||||
[this](const Pile& merged_pile, // merged pile
|
[this](const Pile& merged_pile, // merged pile
|
||||||
const ItemGroup& items, // packed items
|
const ItemGroup& items, // packed items
|
||||||
|
@ -342,18 +358,44 @@ public:
|
||||||
m_rtree.clear();
|
m_rtree.clear();
|
||||||
return m_pck.executeIndexed(std::forward<Args>(args)...);
|
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<>
|
// Arranger specialization for a Box shaped bin.
|
||||||
class AutoArranger<Box>: public _ArrBase<Box> {
|
template<> class AutoArranger<Box>: public _ArrBase<Box> {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
AutoArranger(const Box& bin, Distance dist,
|
AutoArranger(const Box& bin, Distance dist,
|
||||||
std::function<void(unsigned)> progressind,
|
std::function<void(unsigned)> progressind = [](unsigned){},
|
||||||
std::function<bool(void)> stopcond):
|
std::function<bool(void)> stopcond = [](){return false;}):
|
||||||
_ArrBase<Box>(bin, dist, progressind, stopcond)
|
_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) {
|
m_pconf.object_function = [this, bin] (const Item &item) {
|
||||||
|
|
||||||
auto result = objfunc(bin.center(),
|
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());
|
return lnCircle({circ.center()(0), circ.center()(1)}, circ.radius());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
// Arranger specialization for circle shaped bin.
|
||||||
class AutoArranger<lnCircle>: public _ArrBase<lnCircle> {
|
template<> class AutoArranger<lnCircle>: public _ArrBase<lnCircle> {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
AutoArranger(const lnCircle& bin, Distance dist,
|
AutoArranger(const lnCircle& bin, Distance dist,
|
||||||
std::function<void(unsigned)> progressind,
|
std::function<void(unsigned)> progressind = [](unsigned){},
|
||||||
std::function<bool(void)> stopcond):
|
std::function<bool(void)> stopcond = [](){return false;}):
|
||||||
_ArrBase<lnCircle>(bin, dist, progressind, stopcond) {
|
_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) {
|
m_pconf.object_function = [this, &bin] (const Item &item) {
|
||||||
|
|
||||||
auto result = objfunc(bin.center(),
|
auto result = objfunc(bin.center(),
|
||||||
|
@ -431,12 +474,13 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
// Arranger specialization for a generalized polygon.
|
||||||
class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
|
// Warning: this is unfinished business. It may or may not work.
|
||||||
|
template<> class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
|
||||||
public:
|
public:
|
||||||
AutoArranger(const PolygonImpl& bin, Distance dist,
|
AutoArranger(const PolygonImpl& bin, Distance dist,
|
||||||
std::function<void(unsigned)> progressind,
|
std::function<void(unsigned)> progressind = [](unsigned){},
|
||||||
std::function<bool(void)> stopcond):
|
std::function<bool(void)> stopcond = [](){return false;}):
|
||||||
_ArrBase<PolygonImpl>(bin, dist, progressind, stopcond)
|
_ArrBase<PolygonImpl>(bin, dist, progressind, stopcond)
|
||||||
{
|
{
|
||||||
m_pconf.object_function = [this, &bin] (const Item &item) {
|
m_pconf.object_function = [this, &bin] (const Item &item) {
|
||||||
|
@ -461,8 +505,10 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<> // Specialization with no bin
|
// Specialization with no bin. In this case the arranger should just arrange
|
||||||
class AutoArranger<bool>: public _ArrBase<Box> {
|
// 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:
|
public:
|
||||||
|
|
||||||
AutoArranger(Distance dist, std::function<void(unsigned)> progressind,
|
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
|
// A container which stores a pointer to the 3D object and its projected
|
||||||
// 2D shape from top view.
|
// 2D shape from top view.
|
||||||
using ShapeData2D =
|
using ShapeData2D = std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
|
||||||
std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
|
|
||||||
|
|
||||||
ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
||||||
ShapeData2D ret;
|
ShapeData2D ret;
|
||||||
|
|
||||||
auto s = std::accumulate(model.objects.begin(), model.objects.end(), size_t(0),
|
// Count all the items on the bin (all the object's instances)
|
||||||
[](size_t s, ModelObject* o){
|
auto s = std::accumulate(model.objects.begin(), model.objects.end(),
|
||||||
|
size_t(0), [](size_t s, ModelObject* o)
|
||||||
|
{
|
||||||
return s + o->instances.size();
|
return s + o->instances.size();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -506,6 +553,12 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
||||||
for(ModelObject* objptr : model.objects) {
|
for(ModelObject* objptr : model.objects) {
|
||||||
if(objptr) {
|
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();
|
TriangleMesh rmesh = objptr->raw_mesh();
|
||||||
|
|
||||||
ModelInstance * finst = objptr->instances.front();
|
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_x(float(finst->get_rotation()(X)));
|
||||||
rmesh.rotate_y(float(finst->get_rotation()(Y)));
|
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();
|
auto p = rmesh.convex_hull();
|
||||||
|
|
||||||
p.make_clockwise();
|
p.make_clockwise();
|
||||||
p.append(p.first_point());
|
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) {
|
for(ModelInstance* objinst : objptr->instances) {
|
||||||
if(objinst) {
|
if(objinst) {
|
||||||
|
@ -549,6 +620,8 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply the calculated translations and rotations (currently disabled) to the
|
||||||
|
// Model object instances.
|
||||||
void applyResult(
|
void applyResult(
|
||||||
IndexedPackGroup::value_type& group,
|
IndexedPackGroup::value_type& group,
|
||||||
Coord batch_offset,
|
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 bedShape(const Polyline &bed) {
|
||||||
BedShapeHint ret;
|
BedShapeHint ret;
|
||||||
|
|
||||||
|
@ -654,11 +728,15 @@ BedShapeHint bedShape(const Polyline &bed) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool arrange(Model &model,
|
// The final client function to arrange the Model. A progress indicator and
|
||||||
coord_t min_obj_distance,
|
// a stop predicate can be also be passed to control the process.
|
||||||
const Polyline &bed,
|
bool arrange(Model &model, // The model with the geometries
|
||||||
BedShapeHint bedhint,
|
coord_t min_obj_distance, // Has to be in scaled (clipper) measure
|
||||||
bool first_bin_only,
|
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<void (unsigned)> progressind,
|
||||||
std::function<bool ()> stopcondition)
|
std::function<bool ()> stopcondition)
|
||||||
{
|
{
|
||||||
|
@ -759,5 +837,174 @@ bool arrange(Model &model,
|
||||||
return ret && result.size() == 1;
|
return ret && result.size() == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void find_new_position(const Model &model,
|
||||||
|
ModelInstancePtrs toadd,
|
||||||
|
coord_t min_obj_distance,
|
||||||
|
const Polyline &bed)
|
||||||
|
{
|
||||||
|
// Get the 2D projected shapes with their 3D model instance pointers
|
||||||
|
auto shapemap = arr::projectModelFromTop(model);
|
||||||
|
|
||||||
|
// Copy the references for the shapes only as the arranger expects a
|
||||||
|
// sequence of objects convertible to Item or ClipperPolygon
|
||||||
|
PackGroup preshapes; preshapes.emplace_back();
|
||||||
|
ItemGroup shapes;
|
||||||
|
preshapes.front().reserve(shapemap.size());
|
||||||
|
|
||||||
|
std::vector<ModelInstance*> shapes_ptr; shapes_ptr.reserve(toadd.size());
|
||||||
|
IndexedPackGroup result;
|
||||||
|
|
||||||
|
// If there is no hint about the shape, we will try to guess
|
||||||
|
BedShapeHint bedhint = bedShape(bed);
|
||||||
|
|
||||||
|
BoundingBox bbb(bed);
|
||||||
|
|
||||||
|
auto binbb = Box({
|
||||||
|
static_cast<libnest2d::Coord>(bbb.min(0)),
|
||||||
|
static_cast<libnest2d::Coord>(bbb.min(1))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
static_cast<libnest2d::Coord>(bbb.max(0)),
|
||||||
|
static_cast<libnest2d::Coord>(bbb.max(1))
|
||||||
|
});
|
||||||
|
|
||||||
|
for(auto it = shapemap.begin(); it != shapemap.end(); ++it) {
|
||||||
|
if(std::find(toadd.begin(), toadd.end(), it->first) == toadd.end()) {
|
||||||
|
if(it->second.isInside(binbb)) // just ignore items which are outside
|
||||||
|
preshapes.front().emplace_back(std::ref(it->second));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
shapes_ptr.emplace_back(it->first);
|
||||||
|
shapes.emplace_back(std::ref(it->second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto try_first_to_center = [&shapes, &shapes_ptr, &binbb]
|
||||||
|
(std::function<bool(const Item&)> is_colliding,
|
||||||
|
std::function<void(Item&)> preload)
|
||||||
|
{
|
||||||
|
// Try to put the first item to the center, as the arranger will not
|
||||||
|
// do this for us.
|
||||||
|
auto shptrit = shapes_ptr.begin();
|
||||||
|
for(auto shit = shapes.begin(); shit != shapes.end(); ++shit, ++shptrit)
|
||||||
|
{
|
||||||
|
// Try to place items to the center
|
||||||
|
Item& itm = *shit;
|
||||||
|
auto ibb = itm.boundingBox();
|
||||||
|
auto d = binbb.center() - ibb.center();
|
||||||
|
itm.translate(d);
|
||||||
|
if(!is_colliding(itm)) {
|
||||||
|
preload(itm);
|
||||||
|
|
||||||
|
auto offset = itm.translation();
|
||||||
|
Radians rot = itm.rotation();
|
||||||
|
ModelInstance *minst = *shptrit;
|
||||||
|
Vec3d foffset(offset.X*SCALING_FACTOR,
|
||||||
|
offset.Y*SCALING_FACTOR,
|
||||||
|
minst->get_offset()(Z));
|
||||||
|
|
||||||
|
// write the transformation data into the model instance
|
||||||
|
minst->set_rotation(Z, rot);
|
||||||
|
minst->set_offset(foffset);
|
||||||
|
|
||||||
|
shit = shapes.erase(shit);
|
||||||
|
shptrit = shapes_ptr.erase(shptrit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
switch(bedhint.type) {
|
||||||
|
case BedShapeType::BOX: {
|
||||||
|
|
||||||
|
// Create the arranger for the box shaped bed
|
||||||
|
AutoArranger<Box> arrange(binbb, min_obj_distance);
|
||||||
|
|
||||||
|
if(!preshapes.front().empty()) { // If there is something on the plate
|
||||||
|
arrange.preload(preshapes);
|
||||||
|
try_first_to_center(
|
||||||
|
[&arrange](const Item& itm) {return arrange.is_colliding(itm);},
|
||||||
|
[&arrange](Item& itm) { arrange.preload({{itm}}); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrange and return the items with their respective indices within the
|
||||||
|
// input sequence.
|
||||||
|
result = arrange(shapes.begin(), shapes.end());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BedShapeType::CIRCLE: {
|
||||||
|
|
||||||
|
auto c = bedhint.shape.circ;
|
||||||
|
auto cc = to_lnCircle(c);
|
||||||
|
|
||||||
|
// Create the arranger for the box shaped bed
|
||||||
|
AutoArranger<lnCircle> arrange(cc, min_obj_distance);
|
||||||
|
|
||||||
|
if(!preshapes.front().empty()) { // If there is something on the plate
|
||||||
|
arrange.preload(preshapes);
|
||||||
|
try_first_to_center(
|
||||||
|
[&arrange](const Item& itm) {return arrange.is_colliding(itm);},
|
||||||
|
[&arrange](Item& itm) { arrange.preload({{itm}}); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrange and return the items with their respective indices within the
|
||||||
|
// input sequence.
|
||||||
|
result = arrange(shapes.begin(), shapes.end());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BedShapeType::IRREGULAR:
|
||||||
|
case BedShapeType::WHO_KNOWS: {
|
||||||
|
using P = libnest2d::PolygonImpl;
|
||||||
|
|
||||||
|
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
|
||||||
|
P irrbed = sl::create<PolygonImpl>(std::move(ctour));
|
||||||
|
|
||||||
|
AutoArranger<P> arrange(irrbed, min_obj_distance);
|
||||||
|
|
||||||
|
if(!preshapes.front().empty()) { // If there is something on the plate
|
||||||
|
arrange.preload(preshapes);
|
||||||
|
try_first_to_center(
|
||||||
|
[&arrange](const Item& itm) {return arrange.is_colliding(itm);},
|
||||||
|
[&arrange](Item& itm) { arrange.preload({{itm}}); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrange and return the items with their respective indices within the
|
||||||
|
// input sequence.
|
||||||
|
result = arrange(shapes.begin(), shapes.end());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Now we go through the result which will contain the fixed and the moving
|
||||||
|
// polygons as well. We will have to search for our item.
|
||||||
|
|
||||||
|
const auto STRIDE_PADDING = 1.2;
|
||||||
|
Coord stride = Coord(STRIDE_PADDING*binbb.width()*SCALING_FACTOR);
|
||||||
|
Coord batch_offset = 0;
|
||||||
|
|
||||||
|
for(auto& group : result) {
|
||||||
|
for(auto& r : group) if(r.first < shapes.size()) {
|
||||||
|
Item& resultitem = r.second;
|
||||||
|
unsigned idx = r.first;
|
||||||
|
auto offset = resultitem.translation();
|
||||||
|
Radians rot = resultitem.rotation();
|
||||||
|
ModelInstance *minst = shapes_ptr[idx];
|
||||||
|
Vec3d foffset(offset.X*SCALING_FACTOR + batch_offset,
|
||||||
|
offset.Y*SCALING_FACTOR,
|
||||||
|
minst->get_offset()(Z));
|
||||||
|
|
||||||
|
// write the transformation data into the model instance
|
||||||
|
minst->set_rotation(Z, rot);
|
||||||
|
minst->set_offset(foffset);
|
||||||
|
}
|
||||||
|
batch_offset += stride;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,13 @@ bool arrange(Model &model, coord_t min_obj_distance,
|
||||||
std::function<void(unsigned)> progressind,
|
std::function<void(unsigned)> progressind,
|
||||||
std::function<bool(void)> stopcondition);
|
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
|
#endif // MODELARRANGE_HPP
|
||||||
|
|
|
@ -270,9 +270,22 @@ namespace client
|
||||||
{
|
{
|
||||||
std::string out;
|
std::string out;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TYPE_BOOL: out = boost::lexical_cast<std::string>(data.b); break;
|
case TYPE_BOOL: out = data.b ? "true" : "false"; break;
|
||||||
case TYPE_INT: out = boost::lexical_cast<std::string>(data.i); break;
|
case TYPE_INT: out = std::to_string(data.i); break;
|
||||||
case TYPE_DOUBLE: out = boost::lexical_cast<std::string>(data.d); 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;
|
case TYPE_STRING: out = *data.s; break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,18 +287,8 @@ std::vector<unsigned int> Print::object_extruders() const
|
||||||
{
|
{
|
||||||
std::vector<unsigned int> extruders;
|
std::vector<unsigned int> extruders;
|
||||||
extruders.reserve(m_regions.size() * 3);
|
extruders.reserve(m_regions.size() * 3);
|
||||||
|
for (const PrintRegion *region : m_regions)
|
||||||
for (const PrintRegion *region : m_regions) {
|
region->collect_object_printing_extruders(extruders);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
sort_remove_duplicates(extruders);
|
sort_remove_duplicates(extruders);
|
||||||
return extruders;
|
return extruders;
|
||||||
}
|
}
|
||||||
|
@ -366,37 +356,6 @@ double Print::max_allowed_layer_height() const
|
||||||
return nozzle_diameter_max;
|
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
|
// Caller is responsible for supplying models whose objects don't collide
|
||||||
// and have explicit instance positions.
|
// and have explicit instance positions.
|
||||||
void Print::add_model_object(ModelObject* model_object, int idx)
|
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())
|
if (! volume->is_model_part() && ! volume->is_modifier())
|
||||||
continue;
|
continue;
|
||||||
// Get the config applied to this volume.
|
// 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.
|
// Find an existing print region with the same config.
|
||||||
size_t region_id = size_t(-1);
|
size_t region_id = size_t(-1);
|
||||||
for (size_t i = 0; i < m_regions.size(); ++ i)
|
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
|
// If the new config for this volume differs from the other
|
||||||
// volume configs currently associated to this region, it means
|
// volume configs currently associated to this region, it means
|
||||||
// the region subdivision does not make sense anymore.
|
// 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;
|
rearrange_regions = true;
|
||||||
goto exit_for_rearrange_regions;
|
goto exit_for_rearrange_regions;
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
this_region_config_set = true;
|
||||||
}
|
}
|
||||||
for (const PrintRegionConfig &cfg : other_region_configs) {
|
for (const PrintRegionConfig &cfg : other_region_configs) {
|
||||||
|
@ -563,10 +522,6 @@ exit_for_rearrange_regions:
|
||||||
invalidated = true;
|
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;
|
return invalidated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -888,8 +843,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
||||||
if (model_parts_differ || modifiers_differ ||
|
if (model_parts_differ || modifiers_differ ||
|
||||||
model_object.origin_translation != model_object_new.origin_translation ||
|
model_object.origin_translation != model_object_new.origin_translation ||
|
||||||
model_object.layer_height_ranges != model_object_new.layer_height_ranges ||
|
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 != model_object_new.layer_height_profile) {
|
||||||
model_object.layer_height_profile_valid != model_object_new.layer_height_profile_valid) {
|
|
||||||
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
|
// 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()));
|
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
|
||||||
for (auto it = range.first; it != range.second; ++ it) {
|
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)
|
if (object_config_changed)
|
||||||
model_object.config = model_object_new.config;
|
model_object.config = model_object_new.config;
|
||||||
if (! object_diff.empty() || object_config_changed) {
|
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()));
|
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
|
||||||
for (auto it = range.first; it != range.second; ++ it) {
|
for (auto it = range.first; it != range.second; ++ it) {
|
||||||
t_config_option_keys diff = it->print_object->config().diff(new_config);
|
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));
|
old.emplace_back(&(*it));
|
||||||
}
|
}
|
||||||
// Generate a list of trafos and XY offsets for instances of a ModelObject
|
// 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);
|
std::vector<PrintInstances> new_print_instances = print_objects_from_model_object(*model_object);
|
||||||
if (old.empty()) {
|
if (old.empty()) {
|
||||||
// Simple case, just generate new instances.
|
// 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
|
// If the new config for this volume differs from the other
|
||||||
// volume configs currently associated to this region, it means
|
// volume configs currently associated to this region, it means
|
||||||
// the region subdivision does not make sense anymore.
|
// 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.
|
// Regions were split. Reset this print_object.
|
||||||
goto print_object_end;
|
goto print_object_end;
|
||||||
} else {
|
} 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) {
|
for (size_t i = 0; i < region_id; ++i) {
|
||||||
const PrintRegion ®ion_other = *m_regions[i];
|
const PrintRegion ®ion_other = *m_regions[i];
|
||||||
if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
|
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;
|
int region_id = -1;
|
||||||
if (&print_object == &print_object0) {
|
if (&print_object == &print_object0) {
|
||||||
// Get the config applied to this volume.
|
// 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.
|
// Find an existing print region with the same config.
|
||||||
int idx_empty_slot = -1;
|
int idx_empty_slot = -1;
|
||||||
for (int i = 0; i < (int)m_regions.size(); ++ i) {
|
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.
|
//FIXME there may be a race condition with the G-code export running at the background thread.
|
||||||
this->update_object_placeholders();
|
this->update_object_placeholders();
|
||||||
|
|
||||||
|
@ -1178,7 +1125,7 @@ std::string Print::validate() const
|
||||||
// Check horizontal clearance.
|
// Check horizontal clearance.
|
||||||
{
|
{
|
||||||
Polygons convex_hulls_other;
|
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.
|
// Get convex hull of all meshes assigned to this print object.
|
||||||
Polygon convex_hull;
|
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.");
|
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)
|
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).");
|
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();
|
SlicingParameters slicing_params0 = m_objects.front()->slicing_parameters();
|
||||||
|
size_t tallest_object_idx = 0;
|
||||||
const PrintObject* tallest_object = m_objects.front(); // let's find the tallest object
|
if (has_custom_layering)
|
||||||
for (const auto* object : m_objects)
|
PrintObject::update_layer_height_profile(*m_objects.front()->model_object(), slicing_params0, layer_height_profiles.front());
|
||||||
if (*(object->layer_height_profile.end()-2) > *(tallest_object->layer_height_profile.end()-2) )
|
for (size_t i = 1; i < m_objects.size(); ++ i) {
|
||||||
tallest_object = object;
|
const PrintObject *object = m_objects[i];
|
||||||
|
const SlicingParameters slicing_params = object->slicing_parameters();
|
||||||
for (PrintObject *object : m_objects) {
|
|
||||||
SlicingParameters slicing_params = object->slicing_parameters();
|
|
||||||
if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
|
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)
|
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");
|
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");
|
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))
|
if (! equal_layering(slicing_params, slicing_params0))
|
||||||
return L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
|
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;
|
bool failed = false;
|
||||||
// layer_height_profile should be set by Print::apply().
|
if (layer_height_profile_tallest.size() >= layer_height_profile.size()) {
|
||||||
if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size()) {
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (i < object->layer_height_profile.size() && i < tallest_object->layer_height_profile.size()) {
|
while (i < layer_height_profile.size() && i < layer_height_profile_tallest.size()) {
|
||||||
if (std::abs(tallest_object->layer_height_profile[i] - object->layer_height_profile[i])) {
|
if (std::abs(layer_height_profile_tallest[i] - layer_height_profile[i])) {
|
||||||
failed = true;
|
failed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++i;
|
++ i;
|
||||||
if (i == object->layer_height_profile.size()-2) // this element contains this objects max z
|
if (i == 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
|
if (layer_height_profile_tallest[i] > layer_height_profile[i]) // the difference does not matter in this case
|
||||||
++i;
|
++ i;
|
||||||
}
|
}
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
failed = true;
|
failed = true;
|
||||||
|
|
||||||
if (failed)
|
if (failed)
|
||||||
return L("The Wipe tower is only supported if all objects have the same layer height profile");
|
return L("The Wipe tower is only supported if all objects have the same layer height profile");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// find the smallest nozzle diameter
|
// find the smallest nozzle diameter
|
||||||
|
|
|
@ -45,6 +45,10 @@ public:
|
||||||
// Average diameter of nozzles participating on extruding this region.
|
// Average diameter of nozzles participating on extruding this region.
|
||||||
coordf_t bridging_height_avg(const PrintConfig &print_config) const;
|
coordf_t bridging_height_avg(const PrintConfig &print_config) const;
|
||||||
|
|
||||||
|
// Collect extruder indices used to print this region's object.
|
||||||
|
void collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const;
|
||||||
|
static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, std::vector<unsigned int> &object_extruders);
|
||||||
|
|
||||||
// Methods modifying the PrintRegion's state:
|
// Methods modifying the PrintRegion's state:
|
||||||
public:
|
public:
|
||||||
Print* print() { return m_print; }
|
Print* print() { return m_print; }
|
||||||
|
@ -79,16 +83,6 @@ public:
|
||||||
// vector of (vectors of volume ids), indexed by region_id
|
// vector of (vectors of volume ids), indexed by region_id
|
||||||
std::vector<std::vector<int>> region_volumes;
|
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
|
// 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
|
// so that next call to make_perimeters() performs a union() before computing loops
|
||||||
bool typed_slices;
|
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);
|
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);
|
void delete_support_layer(int idx);
|
||||||
|
|
||||||
// To be used over the layer_height_profile of both the PrintObject and ModelObject
|
// Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters.
|
||||||
// to initialize the height profile with the height ranges.
|
// Returns true, if the layer_height_profile was changed.
|
||||||
bool update_layer_height_profile(std::vector<coordf_t> &layer_height_profile) const;
|
static bool update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector<coordf_t> &layer_height_profile);
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Collect the slicing parameters, to be used by variable layer thickness algorithm,
|
// Collect the slicing parameters, to be used by variable layer thickness algorithm,
|
||||||
// by the interactive layer height editor and by the printing process itself.
|
// by the interactive layer height editor and by the printing process itself.
|
||||||
// The slicing parameters are dependent on various configuration values
|
// The slicing parameters are dependent on various configuration values
|
||||||
// (layer height, first layer height, raft settings, print nozzle diameter etc).
|
// (layer height, first layer height, raft settings, print nozzle diameter etc).
|
||||||
SlicingParameters slicing_parameters() const;
|
SlicingParameters slicing_parameters() const;
|
||||||
|
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object);
|
||||||
|
|
||||||
|
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
|
||||||
|
std::vector<unsigned int> object_extruders() const;
|
||||||
|
|
||||||
// Called when slicing to SVG (see Print.pm sub export_svg), and used by perimeters.t
|
// Called when slicing to SVG (see Print.pm sub export_svg), and used by perimeters.t
|
||||||
void slice();
|
void slice();
|
||||||
|
@ -172,13 +162,16 @@ protected:
|
||||||
// Invalidate steps based on a set of parameters changed.
|
// Invalidate steps based on a set of parameters changed.
|
||||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
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:
|
private:
|
||||||
void make_perimeters();
|
void make_perimeters();
|
||||||
void prepare_infill();
|
void prepare_infill();
|
||||||
void infill();
|
void infill();
|
||||||
void generate_support_material();
|
void generate_support_material();
|
||||||
|
|
||||||
void _slice();
|
void _slice(const std::vector<coordf_t> &layer_height_profile);
|
||||||
std::string _fix_slicing_errors();
|
std::string _fix_slicing_errors();
|
||||||
void _simplify_slices(double distance);
|
void _simplify_slices(double distance);
|
||||||
void _make_perimeters();
|
void _make_perimeters();
|
||||||
|
|
|
@ -75,7 +75,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
"This is mostly useful with Bowden extruders which suffer from oozing. "
|
"This is mostly useful with Bowden extruders which suffer from oozing. "
|
||||||
"This feature slows down both the print and the G-code generation.");
|
"This feature slows down both the print and the G-code generation.");
|
||||||
def->cli = "avoid-crossing-perimeters!";
|
def->cli = "avoid-crossing-perimeters!";
|
||||||
def->mode = comAdvanced;
|
def->mode = comExpert;
|
||||||
def->default_value = new ConfigOptionBool(false);
|
def->default_value = new ConfigOptionBool(false);
|
||||||
|
|
||||||
def = this->add("bed_temperature", coInts);
|
def = this->add("bed_temperature", coInts);
|
||||||
|
@ -172,6 +172,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->cli = "bridge-speed=f";
|
def->cli = "bridge-speed=f";
|
||||||
def->aliases = { "bridge_feed_rate" };
|
def->aliases = { "bridge_feed_rate" };
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
|
def->mode = comAdvanced;
|
||||||
def->default_value = new ConfigOptionFloat(60);
|
def->default_value = new ConfigOptionFloat(60);
|
||||||
|
|
||||||
def = this->add("brim_width", coFloat);
|
def = this->add("brim_width", coFloat);
|
||||||
|
@ -326,7 +327,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->sidetext = L("mm");
|
def->sidetext = L("mm");
|
||||||
def->cli = "elefant-foot-compensation=f";
|
def->cli = "elefant-foot-compensation=f";
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->mode = comExpert;
|
def->mode = comAdvanced;
|
||||||
def->default_value = new ConfigOptionFloat(0);
|
def->default_value = new ConfigOptionFloat(0);
|
||||||
|
|
||||||
def = this->add("end_gcode", coString);
|
def = this->add("end_gcode", coString);
|
||||||
|
@ -403,6 +404,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->cli = "external-perimeter-speed=s";
|
def->cli = "external-perimeter-speed=s";
|
||||||
def->ratio_over = "perimeter_speed";
|
def->ratio_over = "perimeter_speed";
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
|
def->mode = comAdvanced;
|
||||||
def->default_value = new ConfigOptionFloatOrPercent(50, true);
|
def->default_value = new ConfigOptionFloatOrPercent(50, true);
|
||||||
|
|
||||||
def = this->add("external_perimeters_first", coBool);
|
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 "
|
"Slic3r keeps adding perimeters, until more than 70% of the loop immediately above "
|
||||||
"is supported.");
|
"is supported.");
|
||||||
def->cli = "extra-perimeters!";
|
def->cli = "extra-perimeters!";
|
||||||
def->mode = comAdvanced;
|
def->mode = comExpert;
|
||||||
def->default_value = new ConfigOptionBool(true);
|
def->default_value = new ConfigOptionBool(true);
|
||||||
|
|
||||||
def = this->add("extruder", coInt);
|
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("EDGE");
|
||||||
def->enum_values.push_back("NGEN");
|
def->enum_values.push_back("NGEN");
|
||||||
def->enum_values.push_back("PVA");
|
def->enum_values.push_back("PVA");
|
||||||
|
def->mode = comAdvanced;
|
||||||
def->default_value = new ConfigOptionStrings { "PLA" };
|
def->default_value = new ConfigOptionStrings { "PLA" };
|
||||||
|
|
||||||
def = this->add("filament_soluble", coBools);
|
def = this->add("filament_soluble", coBools);
|
||||||
def->label = L("Soluble material");
|
def->label = L("Soluble material");
|
||||||
def->tooltip = L("Soluble material is most likely used for a soluble support.");
|
def->tooltip = L("Soluble material is most likely used for a soluble support.");
|
||||||
def->cli = "filament-soluble!";
|
def->cli = "filament-soluble!";
|
||||||
|
def->mode = comAdvanced;
|
||||||
def->default_value = new ConfigOptionBools { false };
|
def->default_value = new ConfigOptionBools { false };
|
||||||
|
|
||||||
def = this->add("filament_cost", coFloats);
|
def = this->add("filament_cost", coFloats);
|
||||||
|
@ -884,6 +888,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->sidetext = L("mm/s");
|
def->sidetext = L("mm/s");
|
||||||
def->cli = "gap-fill-speed=f";
|
def->cli = "gap-fill-speed=f";
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
|
def->mode = comAdvanced;
|
||||||
def->default_value = new ConfigOptionFloat(20);
|
def->default_value = new ConfigOptionFloat(20);
|
||||||
|
|
||||||
def = this->add("gcode_comments", coBool);
|
def = this->add("gcode_comments", coBool);
|
||||||
|
@ -1014,6 +1019,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->cli = "infill-speed=f";
|
def->cli = "infill-speed=f";
|
||||||
def->aliases = { "print_feed_rate", "infill_feed_rate" };
|
def->aliases = { "print_feed_rate", "infill_feed_rate" };
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
|
def->mode = comAdvanced;
|
||||||
def->default_value = new ConfigOptionFloat(80);
|
def->default_value = new ConfigOptionFloat(80);
|
||||||
|
|
||||||
def = this->add("inherits", coString);
|
def = this->add("inherits", coString);
|
||||||
|
@ -1419,6 +1425,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->cli = "perimeter-speed=f";
|
def->cli = "perimeter-speed=f";
|
||||||
def->aliases = { "perimeter_feed_rate" };
|
def->aliases = { "perimeter_feed_rate" };
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
|
def->mode = comAdvanced;
|
||||||
def->default_value = new ConfigOptionFloat(60);
|
def->default_value = new ConfigOptionFloat(60);
|
||||||
|
|
||||||
def = this->add("perimeters", coInt);
|
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("Nearest"));
|
||||||
def->enum_labels.push_back(L("Aligned"));
|
def->enum_labels.push_back(L("Aligned"));
|
||||||
def->enum_labels.push_back(L("Rear"));
|
def->enum_labels.push_back(L("Rear"));
|
||||||
def->mode = comAdvanced;
|
def->mode = comSimple;
|
||||||
def->default_value = new ConfigOptionEnum<SeamPosition>(spAligned);
|
def->default_value = new ConfigOptionEnum<SeamPosition>(spAligned);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -1673,6 +1680,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->max = 300000;
|
def->max = 300000;
|
||||||
def->enum_values.push_back("115200");
|
def->enum_values.push_back("115200");
|
||||||
def->enum_values.push_back("250000");
|
def->enum_values.push_back("250000");
|
||||||
|
def->mode = comAdvanced;
|
||||||
def->default_value = new ConfigOptionInt(250000);
|
def->default_value = new ConfigOptionInt(250000);
|
||||||
|
|
||||||
def = this->add("skirt_distance", coFloat);
|
def = this->add("skirt_distance", coFloat);
|
||||||
|
@ -1726,6 +1734,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->cli = "small-perimeter-speed=s";
|
def->cli = "small-perimeter-speed=s";
|
||||||
def->ratio_over = "perimeter_speed";
|
def->ratio_over = "perimeter_speed";
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
|
def->mode = comAdvanced;
|
||||||
def->default_value = new ConfigOptionFloatOrPercent(15, false);
|
def->default_value = new ConfigOptionFloatOrPercent(15, false);
|
||||||
|
|
||||||
def = this->add("solid_infill_below_area", coFloat);
|
def = this->add("solid_infill_below_area", coFloat);
|
||||||
|
@ -1782,6 +1791,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->ratio_over = "infill_speed";
|
def->ratio_over = "infill_speed";
|
||||||
def->aliases = { "solid_infill_feed_rate" };
|
def->aliases = { "solid_infill_feed_rate" };
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
|
def->mode = comAdvanced;
|
||||||
def->default_value = new ConfigOptionFloatOrPercent(20, false);
|
def->default_value = new ConfigOptionFloatOrPercent(20, false);
|
||||||
|
|
||||||
def = this->add("solid_layers", coInt);
|
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."\
|
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.");
|
" If unchecked, supports will be generated inside the \"Support Enforcer\" volumes only.");
|
||||||
def->cli = "support-material-auto!";
|
def->cli = "support-material-auto!";
|
||||||
def->mode = comAdvanced;
|
def->mode = comSimple;
|
||||||
def->default_value = new ConfigOptionBool(true);
|
def->default_value = new ConfigOptionBool(true);
|
||||||
|
|
||||||
def = this->add("support_material_xy_spacing", coFloatOrPercent);
|
def = this->add("support_material_xy_spacing", coFloatOrPercent);
|
||||||
|
@ -1905,7 +1915,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->category = L("Support material");
|
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->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->cli = "support-material-buildplate-only!";
|
||||||
def->mode = comAdvanced;
|
def->mode = comSimple;
|
||||||
def->default_value = new ConfigOptionBool(false);
|
def->default_value = new ConfigOptionBool(false);
|
||||||
|
|
||||||
def = this->add("support_material_contact_distance", coFloat);
|
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->cli = "support-material-interface-speed=s";
|
||||||
def->ratio_over = "support_material_speed";
|
def->ratio_over = "support_material_speed";
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
|
def->mode = comAdvanced;
|
||||||
def->default_value = new ConfigOptionFloatOrPercent(100, true);
|
def->default_value = new ConfigOptionFloatOrPercent(100, true);
|
||||||
|
|
||||||
def = this->add("support_material_pattern", coEnum);
|
def = this->add("support_material_pattern", coEnum);
|
||||||
|
@ -2041,6 +2052,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->sidetext = L("mm/s");
|
def->sidetext = L("mm/s");
|
||||||
def->cli = "support-material-speed=f";
|
def->cli = "support-material-speed=f";
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
|
def->mode = comAdvanced;
|
||||||
def->default_value = new ConfigOptionFloat(60);
|
def->default_value = new ConfigOptionFloat(60);
|
||||||
|
|
||||||
def = this->add("support_material_synchronize_layers", coBool);
|
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->cli = "top-solid-infill-speed=s";
|
||||||
def->ratio_over = "solid_infill_speed";
|
def->ratio_over = "solid_infill_speed";
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
|
def->mode = comAdvanced;
|
||||||
def->default_value = new ConfigOptionFloatOrPercent(15, false);
|
def->default_value = new ConfigOptionFloatOrPercent(15, false);
|
||||||
|
|
||||||
def = this->add("top_solid_layers", coInt);
|
def = this->add("top_solid_layers", coInt);
|
||||||
|
@ -2487,7 +2500,6 @@ void PrintConfigDef::init_sla_params()
|
||||||
def->label = L("Generate supports");
|
def->label = L("Generate supports");
|
||||||
def->category = L("Supports");
|
def->category = L("Supports");
|
||||||
def->tooltip = L("Generate supports for the models");
|
def->tooltip = L("Generate supports for the models");
|
||||||
def->sidetext = L("");
|
|
||||||
def->cli = "";
|
def->cli = "";
|
||||||
def->default_value = new ConfigOptionBool(true);
|
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 "
|
def->tooltip = L("Merging bridges or pillars into another pillars can "
|
||||||
"increase the radius. Zero means no increase, one means "
|
"increase the radius. Zero means no increase, one means "
|
||||||
"full increase.");
|
"full increase.");
|
||||||
def->sidetext = L("");
|
|
||||||
def->cli = "";
|
def->cli = "";
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->max = 1;
|
def->max = 1;
|
||||||
|
@ -2631,14 +2642,13 @@ void PrintConfigDef::init_sla_params()
|
||||||
def->label = L("Use pad");
|
def->label = L("Use pad");
|
||||||
def->category = L("Pad");
|
def->category = L("Pad");
|
||||||
def->tooltip = L("Add a pad underneath the supported model");
|
def->tooltip = L("Add a pad underneath the supported model");
|
||||||
def->sidetext = L("");
|
|
||||||
def->cli = "";
|
def->cli = "";
|
||||||
def->default_value = new ConfigOptionBool(true);
|
def->default_value = new ConfigOptionBool(true);
|
||||||
|
|
||||||
def = this->add("pad_wall_thickness", coFloat);
|
def = this->add("pad_wall_thickness", coFloat);
|
||||||
def->label = L("Pad wall thickness");
|
def->label = L("Pad wall thickness");
|
||||||
def->category = L("Pad");
|
def->category = L("Pad");
|
||||||
def->tooltip = L("");
|
// def->tooltip = L("");
|
||||||
def->sidetext = L("mm");
|
def->sidetext = L("mm");
|
||||||
def->cli = "";
|
def->cli = "";
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
|
@ -2647,7 +2657,7 @@ void PrintConfigDef::init_sla_params()
|
||||||
def = this->add("pad_wall_height", coFloat);
|
def = this->add("pad_wall_height", coFloat);
|
||||||
def->label = L("Pad wall height");
|
def->label = L("Pad wall height");
|
||||||
def->category = L("Pad");
|
def->category = L("Pad");
|
||||||
def->tooltip = L("");
|
// def->tooltip = L("");
|
||||||
def->sidetext = L("mm");
|
def->sidetext = L("mm");
|
||||||
def->cli = "";
|
def->cli = "";
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
|
@ -2656,7 +2666,7 @@ void PrintConfigDef::init_sla_params()
|
||||||
def = this->add("pad_max_merge_distance", coFloat);
|
def = this->add("pad_max_merge_distance", coFloat);
|
||||||
def->label = L("Max merge distance");
|
def->label = L("Max merge distance");
|
||||||
def->category = L("Pad");
|
def->category = L("Pad");
|
||||||
def->tooltip = L("");
|
// def->tooltip = L("");
|
||||||
def->sidetext = L("mm");
|
def->sidetext = L("mm");
|
||||||
def->cli = "";
|
def->cli = "";
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
|
@ -2665,7 +2675,7 @@ void PrintConfigDef::init_sla_params()
|
||||||
def = this->add("pad_edge_radius", coFloat);
|
def = this->add("pad_edge_radius", coFloat);
|
||||||
def->label = L("Pad edge radius");
|
def->label = L("Pad edge radius");
|
||||||
def->category = L("Pad");
|
def->category = L("Pad");
|
||||||
def->tooltip = L("");
|
// def->tooltip = L("");
|
||||||
def->sidetext = L("mm");
|
def->sidetext = L("mm");
|
||||||
def->cli = "";
|
def->cli = "";
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
|
|
|
@ -38,8 +38,7 @@ namespace Slic3r {
|
||||||
PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_instances) :
|
PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_instances) :
|
||||||
PrintObjectBaseWithState(print, model_object),
|
PrintObjectBaseWithState(print, model_object),
|
||||||
typed_slices(false),
|
typed_slices(false),
|
||||||
size(Vec3crd::Zero()),
|
size(Vec3crd::Zero())
|
||||||
layer_height_profile_valid(false)
|
|
||||||
{
|
{
|
||||||
// Compute the translation to be applied to our meshes so that we work with smaller coordinates
|
// 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->set_copies(copies);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->layer_height_profile = model_object->layer_height_profile;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintBase::ApplyStatus PrintObject::set_copies(const Points &points)
|
PrintBase::ApplyStatus PrintObject::set_copies(const Points &points)
|
||||||
|
@ -106,7 +103,10 @@ void PrintObject::slice()
|
||||||
if (! this->set_started(posSlice))
|
if (! this->set_started(posSlice))
|
||||||
return;
|
return;
|
||||||
m_print->set_status(10, "Processing triangulated mesh");
|
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();
|
m_print->throw_if_canceled();
|
||||||
// Fix the model.
|
// Fix the model.
|
||||||
//FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend.
|
//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 == "first_layer_height"
|
||||||
|| opt_key == "raft_layers") {
|
|| opt_key == "raft_layers") {
|
||||||
steps.emplace_back(posSlice);
|
steps.emplace_back(posSlice);
|
||||||
this->reset_layer_height_profile();
|
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
opt_key == "clip_multipart_objects"
|
opt_key == "clip_multipart_objects"
|
||||||
|
@ -542,7 +541,6 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
||||||
} else {
|
} else {
|
||||||
// for legacy, if we can't handle this option let's invalidate all steps
|
// for legacy, if we can't handle this option let's invalidate all steps
|
||||||
this->invalidate_all_steps();
|
this->invalidate_all_steps();
|
||||||
this->reset_layer_height_profile();
|
|
||||||
invalidated = true;
|
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
|
SlicingParameters PrintObject::slicing_parameters() const
|
||||||
{
|
{
|
||||||
return SlicingParameters::create_from_config(
|
return SlicingParameters::create_from_config(
|
||||||
this->print()->config(), m_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;
|
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()) {
|
if (layer_height_profile.empty()) {
|
||||||
layer_height_profile = this->model_object()->layer_height_profile;
|
layer_height_profile = model_object.layer_height_profile;
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the layer_height_profile.
|
// Verify the layer_height_profile.
|
||||||
SlicingParameters slicing_params = this->slicing_parameters();
|
|
||||||
if (! layer_height_profile.empty() &&
|
if (! layer_height_profile.empty() &&
|
||||||
// Must not be of even length.
|
// Must not be of even length.
|
||||||
((layer_height_profile.size() & 1) != 0 ||
|
((layer_height_profile.size() & 1) != 0 ||
|
||||||
// Last entry must be at the top of the object.
|
// 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();
|
layer_height_profile.clear();
|
||||||
|
|
||||||
if (layer_height_profile.empty()) {
|
if (layer_height_profile.empty()) {
|
||||||
if (0)
|
if (0)
|
||||||
// if (this->layer_height_profile.empty())
|
// 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
|
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;
|
updated = true;
|
||||||
}
|
}
|
||||||
return updated;
|
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,
|
// 1) Decides Z positions of the layers,
|
||||||
// 2) Initializes layers and their regions
|
// 2) Initializes layers and their regions
|
||||||
// 3) Slices the object meshes
|
// 3) Slices the object meshes
|
||||||
|
@ -1387,7 +1437,7 @@ bool PrintObject::update_layer_height_profile()
|
||||||
// Resulting expolygons of layer regions are marked as Internal.
|
// Resulting expolygons of layer regions are marked as Internal.
|
||||||
//
|
//
|
||||||
// this should be idempotent
|
// 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();
|
BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info();
|
||||||
|
|
||||||
|
@ -1406,7 +1456,7 @@ void PrintObject::_slice()
|
||||||
{
|
{
|
||||||
this->clear_layers();
|
this->clear_layers();
|
||||||
// Object layers (pairs of bottom/top Z coordinate), without the raft.
|
// 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.
|
// Reserve object layers for the raft. Last layer of the raft is the contact layer.
|
||||||
int id = int(slicing_params.raft_layers());
|
int id = int(slicing_params.raft_layers());
|
||||||
slice_zs.reserve(object_layers.size());
|
slice_zs.reserve(object_layers.size());
|
||||||
|
@ -2198,22 +2248,4 @@ void PrintObject::_generate_support_material()
|
||||||
support_material.generate(*this);
|
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
|
} // namespace Slic3r
|
||||||
|
|
|
@ -61,4 +61,20 @@ coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const
|
||||||
return this->nozzle_dmr_avg(print_config) * sqrt(m_config.bridge_flow_ratio.value);
|
return this->nozzle_dmr_avg(print_config) * sqrt(m_config.bridge_flow_ratio.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, std::vector<unsigned int> &object_extruders)
|
||||||
|
{
|
||||||
|
// These checks reflect the same logic used in the GUI for enabling/disabling extruder selection fields.
|
||||||
|
if (region_config.perimeters.value > 0 || print_config.brim_width.value > 0)
|
||||||
|
object_extruders.emplace_back(region_config.perimeter_extruder - 1);
|
||||||
|
if (region_config.fill_density.value > 0)
|
||||||
|
object_extruders.emplace_back(region_config.infill_extruder - 1);
|
||||||
|
if (region_config.top_solid_layers.value > 0 || region_config.bottom_solid_layers.value > 0)
|
||||||
|
object_extruders.emplace_back(region_config.solid_infill_extruder - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintRegion::collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const
|
||||||
|
{
|
||||||
|
collect_object_printing_extruders(print()->config(), this->config(), object_extruders);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
|
||||||
#include "ExPolygon.hpp"
|
#include <libslic3r/ExPolygon.hpp>
|
||||||
#include "TriangleMesh.hpp"
|
#include <libslic3r/TriangleMesh.hpp>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace sla {
|
namespace sla {
|
||||||
|
@ -53,7 +53,7 @@ struct Contour3D {
|
||||||
|
|
||||||
void merge(const Contour3D& ctr) {
|
void merge(const Contour3D& ctr) {
|
||||||
auto s3 = coord_t(points.size());
|
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());
|
points.insert(points.end(), ctr.points.begin(), ctr.points.end());
|
||||||
indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.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;
|
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;
|
//using PointSet = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::DontAlign>; //Eigen::MatrixXd;
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
#include "SLASpatIndex.hpp"
|
#include "SLASpatIndex.hpp"
|
||||||
#include "SLABasePool.hpp"
|
#include "SLABasePool.hpp"
|
||||||
|
|
||||||
#include "ClipperUtils.hpp"
|
#include <libslic3r/ClipperUtils.hpp>
|
||||||
#include "Model.hpp"
|
#include <libslic3r/Model.hpp>
|
||||||
|
|
||||||
#include <boost/log/trivial.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;
|
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;
|
Contour3D ret;
|
||||||
|
|
||||||
auto steps = int(ssteps);
|
auto steps = int(ssteps);
|
||||||
|
@ -173,9 +179,10 @@ Contour3D cylinder(double r, double h, size_t ssteps) {
|
||||||
points.reserve(2*ssteps);
|
points.reserve(2*ssteps);
|
||||||
double a = 2*PI/steps;
|
double a = 2*PI/steps;
|
||||||
|
|
||||||
Vec3d jp = {0, 0, 0};
|
Vec3d jp = sp;
|
||||||
Vec3d endp = {0, 0, h};
|
Vec3d endp = {sp(X), sp(Y), sp(Z) + h};
|
||||||
|
|
||||||
|
// Upper circle points
|
||||||
for(int i = 0; i < steps; ++i) {
|
for(int i = 0; i < steps; ++i) {
|
||||||
double phi = i*a;
|
double phi = i*a;
|
||||||
double ex = endp(X) + r*std::cos(phi);
|
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));
|
points.emplace_back(ex, ey, endp(Z));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lower circle points
|
||||||
for(int i = 0; i < steps; ++i) {
|
for(int i = 0; i < steps; ++i) {
|
||||||
double phi = i*a;
|
double phi = i*a;
|
||||||
double x = jp(X) + r*std::cos(phi);
|
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));
|
points.emplace_back(x, y, jp(Z));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now create long triangles connecting upper and lower circles
|
||||||
indices.reserve(2*ssteps);
|
indices.reserve(2*ssteps);
|
||||||
auto offs = steps;
|
auto offs = steps;
|
||||||
for(int i = 0; i < steps - 1; ++i) {
|
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);
|
indices.emplace_back(i, offs + i + 1, i + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Last triangle connecting the first and last vertices
|
||||||
auto last = steps - 1;
|
auto last = steps - 1;
|
||||||
indices.emplace_back(0, last, offs);
|
indices.emplace_back(0, last, offs);
|
||||||
indices.emplace_back(last, offs + 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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,36 +377,15 @@ struct Pillar {
|
||||||
r(radius), steps(st), endpoint(endp), starts_from_head(false)
|
r(radius), steps(st), endpoint(endp), starts_from_head(false)
|
||||||
{
|
{
|
||||||
assert(steps > 0);
|
assert(steps > 0);
|
||||||
int steps_1 = int(steps - 1);
|
|
||||||
|
|
||||||
auto& points = mesh.points;
|
double h = jp(Z) - endp(Z);
|
||||||
auto& indices = mesh.indices;
|
assert(h > 0); // Endpoint is below the starting point
|
||||||
points.reserve(2*steps);
|
|
||||||
double a = 2*PI/steps;
|
|
||||||
|
|
||||||
for(size_t i = 0; i < steps; ++i) {
|
// We just create a bridge geometry with the pillar parameters and
|
||||||
double phi = i*a;
|
// move the data.
|
||||||
double x = jp(X) + r*std::cos(phi);
|
Contour3D body = cylinder(radius, h, st, endp);
|
||||||
double y = jp(Y) + r*std::sin(phi);
|
mesh.points.swap(body.points);
|
||||||
points.emplace_back(x, y, jp(Z));
|
mesh.indices.swap(body.indices);
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Pillar(const Junction& junc, const Vec3d& endp):
|
Pillar(const Junction& junc, const Vec3d& endp):
|
||||||
|
@ -826,45 +830,46 @@ public:
|
||||||
const TriangleMesh& merged_mesh() const {
|
const TriangleMesh& merged_mesh() const {
|
||||||
if(meshcache_valid) return meshcache;
|
if(meshcache_valid) return meshcache;
|
||||||
|
|
||||||
meshcache = TriangleMesh();
|
Contour3D merged;
|
||||||
|
|
||||||
for(auto& head : heads()) {
|
for(auto& head : heads()) {
|
||||||
if(m_ctl.stopcondition()) break;
|
if(m_ctl.stopcondition()) break;
|
||||||
if(head.is_valid()) {
|
if(head.is_valid())
|
||||||
auto&& m = mesh(head.mesh);
|
merged.merge(head.mesh);
|
||||||
meshcache.merge(m);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& stick : pillars()) {
|
for(auto& stick : pillars()) {
|
||||||
if(m_ctl.stopcondition()) break;
|
if(m_ctl.stopcondition()) break;
|
||||||
meshcache.merge(mesh(stick.mesh));
|
merged.merge(stick.mesh);
|
||||||
meshcache.merge(mesh(stick.base));
|
merged.merge(stick.base);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& j : junctions()) {
|
for(auto& j : junctions()) {
|
||||||
if(m_ctl.stopcondition()) break;
|
if(m_ctl.stopcondition()) break;
|
||||||
meshcache.merge(mesh(j.mesh));
|
merged.merge(j.mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& cb : compact_bridges()) {
|
for(auto& cb : compact_bridges()) {
|
||||||
if(m_ctl.stopcondition()) break;
|
if(m_ctl.stopcondition()) break;
|
||||||
meshcache.merge(mesh(cb.mesh));
|
merged.merge(cb.mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& bs : bridges()) {
|
for(auto& bs : bridges()) {
|
||||||
if(m_ctl.stopcondition()) break;
|
if(m_ctl.stopcondition()) break;
|
||||||
meshcache.merge(mesh(bs.mesh));
|
merged.merge(bs.mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(m_ctl.stopcondition()) {
|
if(m_ctl.stopcondition()) {
|
||||||
// In case of failure we have to return an empty mesh
|
// In case of failure we have to return an empty mesh
|
||||||
meshcache = TriangleMesh();
|
meshcache = TriangleMesh();
|
||||||
return meshcache;
|
return meshcache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
meshcache = mesh(merged);
|
||||||
|
|
||||||
// TODO: Is this necessary?
|
// TODO: Is this necessary?
|
||||||
meshcache.repair();
|
//meshcache.repair();
|
||||||
|
|
||||||
BoundingBoxf3&& bb = meshcache.bounding_box();
|
BoundingBoxf3&& bb = meshcache.bounding_box();
|
||||||
model_height = bb.max(Z) - bb.min(Z);
|
model_height = bb.max(Z) - bb.min(Z);
|
||||||
|
@ -1555,7 +1560,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||||
}
|
}
|
||||||
|
|
||||||
double d = distance(jp, jn);
|
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),
|
double chkd = bridge_mesh_intersect(jp, dirv(jp, jn),
|
||||||
pradius,
|
pradius,
|
||||||
|
|
|
@ -440,10 +440,6 @@ std::vector<float> SLAPrint::calculate_heights(const BoundingBoxf3& bb3d,
|
||||||
auto flh = float(layer_height);
|
auto flh = float(layer_height);
|
||||||
auto gnd = float(bb3d.min(Z));
|
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)
|
for(float h = minZ + initial_layer_height; h < maxZ; h += flh)
|
||||||
if(h >= gnd) heights.emplace_back(h);
|
if(h >= gnd) heights.emplace_back(h);
|
||||||
|
|
||||||
|
@ -510,6 +506,9 @@ void SLAPrint::process()
|
||||||
po.m_supportdata.reset(new SLAPrintObject::SupportData());
|
po.m_supportdata.reset(new SLAPrintObject::SupportData());
|
||||||
po.m_supportdata->emesh = EigenMesh3D(po.transformed_mesh());
|
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 "
|
BOOST_LOG_TRIVIAL(debug) << "Support point count "
|
||||||
<< mo.sla_support_points.size();
|
<< mo.sla_support_points.size();
|
||||||
|
|
||||||
|
@ -693,7 +692,6 @@ void SLAPrint::process()
|
||||||
// model_slice method. Only difference is that here it works with
|
// model_slice method. Only difference is that here it works with
|
||||||
// scaled coordinates
|
// scaled coordinates
|
||||||
po.m_level_ids.clear();
|
po.m_level_ids.clear();
|
||||||
if(sminZ >= smodelgnd) po.m_level_ids.emplace_back(sminZ);
|
|
||||||
for(LevelID h = sminZ + sih; h < smaxZ; h += slh)
|
for(LevelID h = sminZ + sih; h < smaxZ; h += slh)
|
||||||
if(h >= smodelgnd) po.m_level_ids.emplace_back(h);
|
if(h >= smodelgnd) po.m_level_ids.emplace_back(h);
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,14 @@
|
||||||
#ifndef slic3r_Slicing_hpp_
|
#ifndef slic3r_Slicing_hpp_
|
||||||
#define slic3r_Slicing_hpp_
|
#define slic3r_Slicing_hpp_
|
||||||
|
|
||||||
#include <set>
|
#include <cstring>
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
|
#include "Utils.hpp"
|
||||||
namespace Slic3r
|
namespace Slic3r
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -91,6 +94,8 @@ struct SlicingParameters
|
||||||
coordf_t object_print_z_min;
|
coordf_t object_print_z_min;
|
||||||
coordf_t object_print_z_max;
|
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.
|
// 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)
|
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);
|
const ModelVolumePtrs &volumes);
|
||||||
|
|
||||||
|
|
||||||
enum LayerHeightEditActionType {
|
enum LayerHeightEditActionType : unsigned int {
|
||||||
LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0,
|
LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0,
|
||||||
LAYER_HEIGHT_EDIT_ACTION_DECREASE = 1,
|
LAYER_HEIGHT_EDIT_ACTION_DECREASE = 1,
|
||||||
LAYER_HEIGHT_EDIT_ACTION_REDUCE = 2,
|
LAYER_HEIGHT_EDIT_ACTION_REDUCE = 2,
|
||||||
|
|
|
@ -9,20 +9,16 @@
|
||||||
#define ENABLE_SHOW_CAMERA_TARGET 0
|
#define ENABLE_SHOW_CAMERA_TARGET 0
|
||||||
// Log debug messages to console when changing selection
|
// Log debug messages to console when changing selection
|
||||||
#define ENABLE_SELECTION_DEBUG_OUTPUT 0
|
#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
|
// 1.42.0.alpha1 techs
|
||||||
//====================
|
//====================
|
||||||
#define ENABLE_1_42_0_ALPHA1 1
|
#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
|
// Disable synchronization of unselected instances
|
||||||
#define DISABLE_INSTANCES_SYNCH (0 && ENABLE_1_42_0_ALPHA1)
|
#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
|
// Scene's GUI made using imgui library
|
||||||
#define ENABLE_IMGUI (1 && ENABLE_1_42_0_ALPHA1)
|
#define ENABLE_IMGUI (1 && ENABLE_1_42_0_ALPHA1)
|
||||||
#define DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI (1 && ENABLE_IMGUI)
|
#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)
|
#define ENABLE_SLA_SUPPORT_GIZMO_MOD (1 && ENABLE_1_42_0_ALPHA1)
|
||||||
// Use wxDataViewRender instead of wxDataViewCustomRenderer
|
// Use wxDataViewRender instead of wxDataViewCustomRenderer
|
||||||
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1)
|
#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
|
// 1.42.0.alpha2 techs
|
||||||
|
@ -45,7 +35,7 @@
|
||||||
// Improves navigation between sidebar fields
|
// Improves navigation between sidebar fields
|
||||||
#define ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION (1 && ENABLE_1_42_0_ALPHA2)
|
#define ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION (1 && ENABLE_1_42_0_ALPHA2)
|
||||||
// Adds print bed models to 3D scene
|
// 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_
|
#endif // _technologies_h_
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,3 +52,7 @@
|
||||||
#define ENABLE_GENERIC_SUBPARTS_PLACEMENT (1 && ENABLE_1_42_0_ALPHA4)
|
#define ENABLE_GENERIC_SUBPARTS_PLACEMENT (1 && ENABLE_1_42_0_ALPHA4)
|
||||||
// Reworked management of bed shape changes
|
// Reworked management of bed shape changes
|
||||||
#define ENABLE_REWORKED_BED_SHAPE_CHANGE (1 && ENABLE_1_42_0_ALPHA4)
|
#define ENABLE_REWORKED_BED_SHAPE_CHANGE (1 && ENABLE_1_42_0_ALPHA4)
|
||||||
|
// Use anisotropic filtering on bed plate texture
|
||||||
|
#define ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES (1 && ENABLE_1_42_0_ALPHA4)
|
||||||
|
// Bunch of fixes related to volumes centering
|
||||||
|
#define ENABLE_VOLUMES_CENTERING_FIXES (1 && ENABLE_1_42_0_ALPHA4)
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd>& facets )
|
TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd>& facets)
|
||||||
: repaired(false)
|
: repaired(false)
|
||||||
{
|
{
|
||||||
stl_initialize(&this->stl);
|
stl_initialize(&this->stl);
|
||||||
|
@ -99,6 +99,8 @@ TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other)
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #define SLIC3R_TRACE_REPAIR
|
||||||
|
|
||||||
void TriangleMesh::repair()
|
void TriangleMesh::repair()
|
||||||
{
|
{
|
||||||
if (this->repaired) return;
|
if (this->repaired) return;
|
||||||
|
@ -109,7 +111,9 @@ void TriangleMesh::repair()
|
||||||
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() started";
|
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() started";
|
||||||
|
|
||||||
// checking exact
|
// checking exact
|
||||||
|
#ifdef SLIC3R_TRACE_REPAIR
|
||||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact";
|
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact";
|
||||||
|
#endif /* SLIC3R_TRACE_REPAIR */
|
||||||
stl_check_facets_exact(&stl);
|
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_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);
|
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++) {
|
for (int i = 0; i < iterations; i++) {
|
||||||
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
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);
|
//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";
|
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_nearby";
|
||||||
|
#endif /* SLIC3R_TRACE_REPAIR */
|
||||||
stl_check_facets_nearby(&stl, tolerance);
|
stl_check_facets_nearby(&stl, tolerance);
|
||||||
//printf(" Fixed %d edges.\n", stl.stats.edges_fixed - last_edges_fixed);
|
//printf(" Fixed %d edges.\n", stl.stats.edges_fixed - last_edges_fixed);
|
||||||
//last_edges_fixed = stl.stats.edges_fixed;
|
//last_edges_fixed = stl.stats.edges_fixed;
|
||||||
|
@ -137,7 +143,9 @@ void TriangleMesh::repair()
|
||||||
|
|
||||||
// remove_unconnected
|
// remove_unconnected
|
||||||
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
||||||
|
#ifdef SLIC3R_TRACE_REPAIR
|
||||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets";
|
BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets";
|
||||||
|
#endif /* SLIC3R_TRACE_REPAIR */
|
||||||
stl_remove_unconnected_facets(&stl);
|
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.
|
// 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.
|
// Rather let the slicing algorithm close gaps in 2D slices.
|
||||||
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
||||||
|
#ifdef SLIC3R_TRACE_REPAIR
|
||||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_fill_holes";
|
BOOST_LOG_TRIVIAL(trace) << "\tstl_fill_holes";
|
||||||
|
#endif /* SLIC3R_TRACE_REPAIR */
|
||||||
stl_fill_holes(&stl);
|
stl_fill_holes(&stl);
|
||||||
stl_clear_error(&stl);
|
stl_clear_error(&stl);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// normal_directions
|
// normal_directions
|
||||||
|
#ifdef SLIC3R_TRACE_REPAIR
|
||||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_directions";
|
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_directions";
|
||||||
|
#endif /* SLIC3R_TRACE_REPAIR */
|
||||||
stl_fix_normal_directions(&stl);
|
stl_fix_normal_directions(&stl);
|
||||||
|
|
||||||
// normal_values
|
// normal_values
|
||||||
|
#ifdef SLIC3R_TRACE_REPAIR
|
||||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_values";
|
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_values";
|
||||||
|
#endif /* SLIC3R_TRACE_REPAIR */
|
||||||
stl_fix_normal_values(&stl);
|
stl_fix_normal_values(&stl);
|
||||||
|
|
||||||
// always calculate the volume and reverse all normals if volume is negative
|
// always calculate the volume and reverse all normals if volume is negative
|
||||||
|
#ifdef SLIC3R_TRACE_REPAIR
|
||||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_calculate_volume";
|
BOOST_LOG_TRIVIAL(trace) << "\tstl_calculate_volume";
|
||||||
|
#endif /* SLIC3R_TRACE_REPAIR */
|
||||||
stl_calculate_volume(&stl);
|
stl_calculate_volume(&stl);
|
||||||
|
|
||||||
// neighbors
|
// neighbors
|
||||||
|
#ifdef SLIC3R_TRACE_REPAIR
|
||||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_verify_neighbors";
|
BOOST_LOG_TRIVIAL(trace) << "\tstl_verify_neighbors";
|
||||||
|
#endif /* SLIC3R_TRACE_REPAIR */
|
||||||
stl_verify_neighbors(&stl);
|
stl_verify_neighbors(&stl);
|
||||||
|
|
||||||
this->repaired = true;
|
this->repaired = true;
|
||||||
|
@ -507,67 +525,22 @@ BoundingBoxf3 TriangleMesh::bounding_box() const
|
||||||
return bb;
|
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);
|
BoundingBoxf3 bbox;
|
||||||
if (!has_shared)
|
if (stl.v_shared == nullptr) {
|
||||||
stl_generate_shared_vertices(const_cast<stl_file*>(&stl));
|
// Using the STL faces.
|
||||||
|
for (int i = 0; i < this->facets_count(); ++ i) {
|
||||||
unsigned int vertices_count = (stl.stats.shared_vertices > 0) ? (unsigned int)stl.stats.shared_vertices : 3 * (unsigned int)stl.stats.number_of_facets;
|
const stl_facet &facet = this->stl.facet_start[i];
|
||||||
|
for (size_t j = 0; j < 3; ++ j)
|
||||||
if (vertices_count == 0)
|
bbox.merge(trafo * facet.vertex[j].cast<double>());
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
} 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
|
return bbox;
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TriangleMesh TriangleMesh::convex_hull_3d() const
|
TriangleMesh TriangleMesh::convex_hull_3d() const
|
||||||
|
@ -1992,4 +1965,5 @@ TriangleMesh make_sphere(double rho, double fa) {
|
||||||
TriangleMesh mesh(vertices, facets);
|
TriangleMesh mesh(vertices, facets);
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ public:
|
||||||
Polygon convex_hull();
|
Polygon convex_hull();
|
||||||
BoundingBoxf3 bounding_box() const;
|
BoundingBoxf3 bounding_box() const;
|
||||||
// Returns the bbox of this TriangleMesh transformed by the given transformation
|
// 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
|
// Returns the convex hull of this TriangleMesh
|
||||||
TriangleMesh convex_hull_3d() const;
|
TriangleMesh convex_hull_3d() const;
|
||||||
void reset_repair_stats();
|
void reset_repair_stats();
|
||||||
|
|
|
@ -159,6 +159,16 @@ template<class T> size_t next_highest_power_of_2(T v,
|
||||||
extern std::string xml_escape(std::string text);
|
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
|
class ScopeGuard
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||||
* http://code.google.com/p/poly2tri/
|
* https://github.com/jhasse/poly2tri
|
||||||
*
|
*
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
@ -29,10 +29,16 @@
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
#include "shapes.h"
|
#include "shapes.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace p2t {
|
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)
|
Triangle::Triangle(Point& a, Point& b, Point& c)
|
||||||
{
|
{
|
||||||
points_[0] = &a; points_[1] = &b; points_[2] = &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]) {
|
if (p == points_[0]) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -163,7 +169,7 @@ int Triangle::Index(const Point* p) const
|
||||||
return -1;
|
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_[0] == p1) {
|
||||||
if (points_[1] == p2) {
|
if (points_[1] == p2) {
|
||||||
|
@ -259,7 +265,7 @@ Triangle* Triangle::NeighborCCW(const Point& point)
|
||||||
return neighbors_[1];
|
return neighbors_[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Triangle::GetConstrainedEdgeCCW(const Point& p) const
|
bool Triangle::GetConstrainedEdgeCCW(const Point& p)
|
||||||
{
|
{
|
||||||
if (&p == points_[0]) {
|
if (&p == points_[0]) {
|
||||||
return constrained_edge[2];
|
return constrained_edge[2];
|
||||||
|
@ -269,7 +275,7 @@ bool Triangle::GetConstrainedEdgeCCW(const Point& p) const
|
||||||
return constrained_edge[1];
|
return constrained_edge[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Triangle::GetConstrainedEdgeCW(const Point& p) const
|
bool Triangle::GetConstrainedEdgeCW(const Point& p)
|
||||||
{
|
{
|
||||||
if (&p == points_[0]) {
|
if (&p == points_[0]) {
|
||||||
return constrained_edge[1];
|
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]) {
|
if (&p == points_[0]) {
|
||||||
return delaunay_edge[2];
|
return delaunay_edge[2];
|
||||||
|
@ -311,7 +317,7 @@ bool Triangle::GetDelunayEdgeCCW(const Point& p) const
|
||||||
return delaunay_edge[1];
|
return delaunay_edge[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Triangle::GetDelunayEdgeCW(const Point& p) const
|
bool Triangle::GetDelunayEdgeCW(const Point& p)
|
||||||
{
|
{
|
||||||
if (&p == points_[0]) {
|
if (&p == points_[0]) {
|
||||||
return delaunay_edge[1];
|
return delaunay_edge[1];
|
||||||
|
@ -356,10 +362,7 @@ Triangle& Triangle::NeighborAcross(const Point& opoint)
|
||||||
|
|
||||||
void Triangle::DebugPrint()
|
void Triangle::DebugPrint()
|
||||||
{
|
{
|
||||||
using namespace std;
|
std::cout << *points_[0] << " " << *points_[1] << " " << *points_[2] << std::endl;
|
||||||
cout << points_[0]->x << "," << points_[0]->y << " ";
|
|
||||||
cout << points_[1]->x << "," << points_[1]->y << " ";
|
|
||||||
cout << points_[2]->x << "," << points_[2]->y << endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||||
* http://code.google.com/p/poly2tri/
|
* https://github.com/jhasse/poly2tri
|
||||||
*
|
*
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
@ -33,10 +33,10 @@
|
||||||
#ifndef SHAPES_H
|
#ifndef SHAPES_H
|
||||||
#define SHAPES_H
|
#define SHAPES_H
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace p2t {
|
namespace p2t {
|
||||||
|
|
||||||
|
@ -119,6 +119,8 @@ struct Point {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream&, const Point&);
|
||||||
|
|
||||||
// Represents a simple polygon's edge
|
// Represents a simple polygon's edge
|
||||||
struct Edge {
|
struct Edge {
|
||||||
|
|
||||||
|
@ -130,13 +132,13 @@ struct Edge {
|
||||||
if (p1.y > p2.y) {
|
if (p1.y > p2.y) {
|
||||||
q = &p1;
|
q = &p1;
|
||||||
p = &p2;
|
p = &p2;
|
||||||
} else if (p1.y == p2.y) {
|
} else if (std::abs(p1.y - p2.y) < 1e-10) {
|
||||||
if (p1.x > p2.x) {
|
if (p1.x > p2.x) {
|
||||||
q = &p1;
|
q = &p1;
|
||||||
p = &p2;
|
p = &p2;
|
||||||
} else if (p1.x == p2.x) {
|
} else if (std::abs(p1.x - p2.x) < 1e-10) {
|
||||||
// Repeat points
|
// 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(Edge& edge);
|
||||||
void MarkConstrainedEdge(Point* p, Point* q);
|
void MarkConstrainedEdge(Point* p, Point* q);
|
||||||
|
|
||||||
int Index(const Point* p) const;
|
int Index(const Point* p);
|
||||||
int EdgeIndex(const Point* p1, const Point* p2) const;
|
int EdgeIndex(const Point* p1, const Point* p2);
|
||||||
|
|
||||||
Triangle* NeighborCW(const Point& point);
|
Triangle* NeighborCW(const Point& point);
|
||||||
Triangle* NeighborCCW(const Point& point);
|
Triangle* NeighborCCW(const Point& point);
|
||||||
bool GetConstrainedEdgeCCW(const Point& p) const;
|
bool GetConstrainedEdgeCCW(const Point& p);
|
||||||
bool GetConstrainedEdgeCW(const Point& p) const;
|
bool GetConstrainedEdgeCW(const Point& p);
|
||||||
void SetConstrainedEdgeCCW(const Point& p, bool ce);
|
void SetConstrainedEdgeCCW(const Point& p, bool ce);
|
||||||
void SetConstrainedEdgeCW(const Point& p, bool ce);
|
void SetConstrainedEdgeCW(const Point& p, bool ce);
|
||||||
bool GetDelunayEdgeCCW(const Point& p) const;
|
bool GetDelunayEdgeCCW(const Point& p);
|
||||||
bool GetDelunayEdgeCW(const Point& p) const;
|
bool GetDelunayEdgeCW(const Point& p);
|
||||||
void SetDelunayEdgeCCW(const Point& p, bool e);
|
void SetDelunayEdgeCCW(const Point& p, bool e);
|
||||||
void SetDelunayEdgeCW(const Point& p, bool e);
|
void SetDelunayEdgeCW(const Point& p, bool e);
|
||||||
|
|
||||||
bool Contains(const Point* p) const;
|
bool Contains(const Point* p);
|
||||||
bool Contains(const Edge& e) const;
|
bool Contains(const Edge& e);
|
||||||
bool Contains(const Point* p, const Point* q) const;
|
bool Contains(const Point* p, const Point* q);
|
||||||
void Legalize(Point& point);
|
void Legalize(Point& point);
|
||||||
void Legalize(Point& opoint, Point& npoint);
|
void Legalize(Point& opoint, Point& npoint);
|
||||||
/**
|
/**
|
||||||
|
@ -198,7 +200,7 @@ void ClearNeighbor(const Triangle *triangle);
|
||||||
void ClearNeighbors();
|
void ClearNeighbors();
|
||||||
void ClearDelunayEdges();
|
void ClearDelunayEdges();
|
||||||
|
|
||||||
inline bool IsInterior() const;
|
inline bool IsInterior();
|
||||||
inline void IsInterior(bool b);
|
inline void IsInterior(bool b);
|
||||||
|
|
||||||
Triangle& NeighborAcross(const Point& opoint);
|
Triangle& NeighborAcross(const Point& opoint);
|
||||||
|
@ -293,22 +295,22 @@ inline Triangle* Triangle::GetNeighbor(int index)
|
||||||
return neighbors_[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];
|
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);
|
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);
|
return Contains(p) && Contains(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Triangle::IsInterior() const
|
inline bool Triangle::IsInterior()
|
||||||
{
|
{
|
||||||
return interior_;
|
return interior_;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||||
* http://code.google.com/p/poly2tri/
|
* https://github.com/jhasse/poly2tri
|
||||||
*
|
*
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
@ -37,8 +37,15 @@
|
||||||
#define _USE_MATH_DEFINES
|
#define _USE_MATH_DEFINES
|
||||||
#endif /* _USE_MATH_DEFINES */
|
#endif /* _USE_MATH_DEFINES */
|
||||||
|
|
||||||
|
#include "shapes.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <math.h>
|
|
||||||
|
// C99 removes M_PI from math.h
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846264338327
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace p2t {
|
namespace p2t {
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||||
* http://code.google.com/p/poly2tri/
|
* https://github.com/jhasse/poly2tri
|
||||||
*
|
*
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||||
* http://code.google.com/p/poly2tri/
|
* https://github.com/jhasse/poly2tri
|
||||||
*
|
*
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
@ -30,6 +30,8 @@
|
||||||
*/
|
*/
|
||||||
#include "advancing_front.h"
|
#include "advancing_front.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
namespace p2t {
|
namespace p2t {
|
||||||
|
|
||||||
AdvancingFront::AdvancingFront(Node& head, Node& tail)
|
AdvancingFront::AdvancingFront(Node& head, Node& tail)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||||
* http://code.google.com/p/poly2tri/
|
* https://github.com/jhasse/poly2tri
|
||||||
*
|
*
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||||
* http://code.google.com/p/poly2tri/
|
* https://github.com/jhasse/poly2tri
|
||||||
*
|
*
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||||
* http://code.google.com/p/poly2tri/
|
* https://github.com/jhasse/poly2tri
|
||||||
*
|
*
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||||
* http://code.google.com/p/poly2tri/
|
* https://github.com/jhasse/poly2tri
|
||||||
*
|
*
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
@ -28,19 +28,21 @@
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
#include <stdexcept>
|
|
||||||
#include "sweep.h"
|
#include "sweep.h"
|
||||||
#include "sweep_context.h"
|
#include "sweep_context.h"
|
||||||
#include "advancing_front.h"
|
#include "advancing_front.h"
|
||||||
#include "../common/utils.h"
|
#include "../common/utils.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace p2t {
|
namespace p2t {
|
||||||
|
|
||||||
// Triangulate simple polygon with holes
|
// Triangulate simple polygon with holes
|
||||||
void Sweep::Triangulate(SweepContext& tcx)
|
void Sweep::Triangulate(SweepContext& tcx)
|
||||||
{
|
{
|
||||||
tcx.InitTriangulation();
|
tcx.InitTriangulation();
|
||||||
tcx.CreateAdvancingFront(nodes_);
|
tcx.CreateAdvancingFront();
|
||||||
// Sweep points; build mesh
|
// Sweep points; build mesh
|
||||||
SweepPoints(tcx);
|
SweepPoints(tcx);
|
||||||
// Clean up
|
// Clean up
|
||||||
|
@ -699,13 +701,6 @@ void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t,
|
||||||
Triangle& ot = t->NeighborAcross(p);
|
Triangle& ot = t->NeighborAcross(p);
|
||||||
Point& op = *ot.OppositePoint(*t, 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)) {
|
if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) {
|
||||||
// Lets rotate shared edge one vertex CW
|
// Lets rotate shared edge one vertex CW
|
||||||
RotateTrianglePair(*t, p, ot, op);
|
RotateTrianglePair(*t, p, ot, op);
|
||||||
|
@ -772,13 +767,6 @@ void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle&
|
||||||
Triangle& ot = t.NeighborAcross(p);
|
Triangle& ot = t.NeighborAcross(p);
|
||||||
Point& op = *ot.OppositePoint(t, 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)) {
|
if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) {
|
||||||
// flip with new edge op->eq
|
// flip with new edge op->eq
|
||||||
FlipEdgeEvent(tcx, eq, op, &ot, op);
|
FlipEdgeEvent(tcx, eq, op, &ot, op);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||||
* http://code.google.com/p/poly2tri/
|
* https://github.com/jhasse/poly2tri
|
||||||
*
|
*
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||||
* http://code.google.com/p/poly2tri/
|
* https://github.com/jhasse/poly2tri
|
||||||
*
|
*
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
@ -120,10 +120,9 @@ Node& SweepContext::LocateNode(const Point& point)
|
||||||
return *front_->LocateNode(point.x);
|
return *front_->LocateNode(point.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SweepContext::CreateAdvancingFront(const std::vector<Node*>& nodes)
|
void SweepContext::CreateAdvancingFront()
|
||||||
{
|
{
|
||||||
|
|
||||||
(void) nodes;
|
|
||||||
// Initial triangle
|
// Initial triangle
|
||||||
Triangle* triangle = new Triangle(*points_[0], *tail_, *head_);
|
Triangle* triangle = new Triangle(*points_[0], *tail_, *head_);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
* Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors
|
||||||
* http://code.google.com/p/poly2tri/
|
* https://github.com/jhasse/poly2tri
|
||||||
*
|
*
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
@ -70,7 +70,7 @@ Node& LocateNode(const Point& point);
|
||||||
|
|
||||||
void RemoveNode(Node* node);
|
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
|
/// Try to map a node to all sides of this triangle that don't have a neighbor
|
||||||
void MapTriangleToNodes(Triangle& t);
|
void MapTriangleToNodes(Triangle& t);
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -66,21 +68,26 @@ void TPPLPoly::Triangle(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3) {
|
||||||
points[2] = p3;
|
points[2] = p3;
|
||||||
}
|
}
|
||||||
|
|
||||||
TPPLPoly::TPPLPoly(const TPPLPoly &src) {
|
TPPLPoly::TPPLPoly(const TPPLPoly &src) : TPPLPoly() {
|
||||||
hole = src.hole;
|
hole = src.hole;
|
||||||
numpoints = src.numpoints;
|
numpoints = src.numpoints;
|
||||||
points = new TPPLPoint[numpoints];
|
|
||||||
memcpy(points, src.points, numpoints*sizeof(TPPLPoint));
|
|
||||||
}
|
|
||||||
|
|
||||||
TPPLPoly& TPPLPoly::operator=(const TPPLPoly &src) {
|
if(numpoints > 0) {
|
||||||
if(&src != this) {
|
|
||||||
Clear();
|
|
||||||
hole = src.hole;
|
|
||||||
numpoints = src.numpoints;
|
|
||||||
points = new TPPLPoint[numpoints];
|
points = new TPPLPoint[numpoints];
|
||||||
memcpy(points, src.points, numpoints*sizeof(TPPLPoint));
|
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;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,16 +112,11 @@ void TPPLPoly::SetOrientation(int orientation) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TPPLPoly::Invert() {
|
void TPPLPoly::Invert() {
|
||||||
long i;
|
std::reverse(points, points + numpoints);
|
||||||
TPPLPoint *invpoints;
|
}
|
||||||
|
|
||||||
invpoints = new TPPLPoint[numpoints];
|
TPPLPartition::PartitionVertex::PartitionVertex() : previous(NULL), next(NULL) {
|
||||||
for(i=0;i<numpoints;i++) {
|
|
||||||
invpoints[i] = points[numpoints-i-1];
|
|
||||||
}
|
|
||||||
|
|
||||||
delete [] points;
|
|
||||||
points = invpoints;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TPPLPoint TPPLPartition::Normalize(const TPPLPoint &p) {
|
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
|
//removes holes from inpolys by merging them with non-holes
|
||||||
int TPPLPartition::RemoveHoles(list<TPPLPoly> *inpolys, list<TPPLPoly> *outpolys) {
|
int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
|
||||||
list<TPPLPoly> polys;
|
TPPLPolyList polys;
|
||||||
list<TPPLPoly>::iterator holeiter,polyiter,iter,iter2;
|
TPPLPolyList::iterator holeiter,polyiter,iter,iter2;
|
||||||
long i,i2,holepointindex,polypointindex = 0;
|
long i,i2,holepointindex,polypointindex;
|
||||||
TPPLPoint holepoint,polypoint,bestpolypoint;
|
TPPLPoint holepoint,polypoint,bestpolypoint;
|
||||||
TPPLPoint linep1,linep2;
|
TPPLPoint linep1,linep2;
|
||||||
TPPLPoint v1,v2;
|
TPPLPoint v1,v2;
|
||||||
|
@ -183,14 +185,14 @@ int TPPLPartition::RemoveHoles(list<TPPLPoly> *inpolys, list<TPPLPoly> *outpolys
|
||||||
|
|
||||||
//check for trivial case (no holes)
|
//check for trivial case (no holes)
|
||||||
hasholes = false;
|
hasholes = false;
|
||||||
for(iter = inpolys->begin(); iter!=inpolys->end(); ++iter) {
|
for(iter = inpolys->begin(); iter!=inpolys->end(); iter++) {
|
||||||
if(iter->IsHole()) {
|
if(iter->IsHole()) {
|
||||||
hasholes = true;
|
hasholes = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!hasholes) {
|
if(!hasholes) {
|
||||||
for(iter = inpolys->begin(); iter!=inpolys->end(); ++iter) {
|
for(iter = inpolys->begin(); iter!=inpolys->end(); iter++) {
|
||||||
outpolys->push_back(*iter);
|
outpolys->push_back(*iter);
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -201,7 +203,7 @@ int TPPLPartition::RemoveHoles(list<TPPLPoly> *inpolys, list<TPPLPoly> *outpolys
|
||||||
while(1) {
|
while(1) {
|
||||||
//find the hole point with the largest x
|
//find the hole point with the largest x
|
||||||
hasholes = false;
|
hasholes = false;
|
||||||
for(iter = polys.begin(); iter!=polys.end(); ++iter) {
|
for(iter = polys.begin(); iter!=polys.end(); iter++) {
|
||||||
if(!iter->IsHole()) continue;
|
if(!iter->IsHole()) continue;
|
||||||
|
|
||||||
if(!hasholes) {
|
if(!hasholes) {
|
||||||
|
@ -221,7 +223,7 @@ int TPPLPartition::RemoveHoles(list<TPPLPoly> *inpolys, list<TPPLPoly> *outpolys
|
||||||
holepoint = holeiter->GetPoint(holepointindex);
|
holepoint = holeiter->GetPoint(holepointindex);
|
||||||
|
|
||||||
pointfound = false;
|
pointfound = false;
|
||||||
for(iter = polys.begin(); iter!=polys.end(); ++iter) {
|
for(iter = polys.begin(); iter!=polys.end(); iter++) {
|
||||||
if(iter->IsHole()) continue;
|
if(iter->IsHole()) continue;
|
||||||
for(i=0; i < iter->GetNumPoints(); i++) {
|
for(i=0; i < iter->GetNumPoints(); i++) {
|
||||||
if(iter->GetPoint(i).x <= holepoint.x) continue;
|
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;
|
if(v2.x > v1.x) continue;
|
||||||
}
|
}
|
||||||
pointvisible = true;
|
pointvisible = true;
|
||||||
for(iter2 = polys.begin(); iter2!=polys.end(); ++iter2) {
|
for(iter2 = polys.begin(); iter2!=polys.end(); iter2++) {
|
||||||
if(iter2->IsHole()) continue;
|
if(iter2->IsHole()) continue;
|
||||||
for(i2=0; i2 < iter2->GetNumPoints(); i2++) {
|
for(i2=0; i2 < iter2->GetNumPoints(); i2++) {
|
||||||
linep1 = iter2->GetPoint(i2);
|
linep1 = iter2->GetPoint(i2);
|
||||||
|
@ -280,7 +282,7 @@ int TPPLPartition::RemoveHoles(list<TPPLPoly> *inpolys, list<TPPLPoly> *outpolys
|
||||||
polys.push_back(newpoly);
|
polys.push_back(newpoly);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(iter = polys.begin(); iter!=polys.end(); ++iter) {
|
for(iter = polys.begin(); iter!=polys.end(); iter++) {
|
||||||
outpolys->push_back(*iter);
|
outpolys->push_back(*iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,7 +337,7 @@ bool TPPLPartition::InCone(PartitionVertex *v, TPPLPoint &p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TPPLPartition::UpdateVertexReflexity(PartitionVertex *v) {
|
void TPPLPartition::UpdateVertexReflexity(PartitionVertex *v) {
|
||||||
PartitionVertex *v1,*v3;
|
PartitionVertex *v1 = NULL,*v3 = NULL;
|
||||||
v1 = v->previous;
|
v1 = v->previous;
|
||||||
v3 = v->next;
|
v3 = v->next;
|
||||||
v->isConvex = !IsReflex(v1->p,v->p,v3->p);
|
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) {
|
void TPPLPartition::UpdateVertex(PartitionVertex *v, PartitionVertex *vertices, long numvertices) {
|
||||||
long i;
|
long i;
|
||||||
PartitionVertex *v1,*v3;
|
PartitionVertex *v1 = NULL,*v3 = NULL;
|
||||||
TPPLPoint vec1,vec3;
|
TPPLPoint vec1,vec3;
|
||||||
|
|
||||||
v1 = v->previous;
|
v1 = v->previous;
|
||||||
|
@ -372,10 +374,12 @@ void TPPLPartition::UpdateVertex(PartitionVertex *v, PartitionVertex *vertices,
|
||||||
}
|
}
|
||||||
|
|
||||||
//triangulation by ear removal
|
//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;
|
long numvertices;
|
||||||
PartitionVertex *vertices;
|
PartitionVertex *vertices = NULL;
|
||||||
PartitionVertex *ear;
|
PartitionVertex *ear = NULL;
|
||||||
TPPLPoly triangle;
|
TPPLPoly triangle;
|
||||||
long i,j;
|
long i,j;
|
||||||
bool earfound;
|
bool earfound;
|
||||||
|
@ -446,21 +450,23 @@ int TPPLPartition::Triangulate_EC(TPPLPoly *poly, list<TPPLPoly> *triangles) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TPPLPartition::Triangulate_EC(list<TPPLPoly> *inpolys, list<TPPLPoly> *triangles) {
|
int TPPLPartition::Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles) {
|
||||||
list<TPPLPoly> outpolys;
|
TPPLPolyList outpolys;
|
||||||
list<TPPLPoly>::iterator iter;
|
TPPLPolyList::iterator iter;
|
||||||
|
|
||||||
if(!RemoveHoles(inpolys,&outpolys)) return 0;
|
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;
|
if(!Triangulate_EC(&(*iter),triangles)) return 0;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) {
|
||||||
list<TPPLPoly> triangles;
|
if(!poly->Valid()) return 0;
|
||||||
list<TPPLPoly>::iterator iter1,iter2;
|
|
||||||
TPPLPoly *poly1,*poly2;
|
TPPLPolyList triangles;
|
||||||
|
TPPLPolyList::iterator iter1,iter2;
|
||||||
|
TPPLPoly *poly1 = NULL,*poly2 = NULL;
|
||||||
TPPLPoly newpoly;
|
TPPLPoly newpoly;
|
||||||
TPPLPoint d1,d2,p1,p2,p3;
|
TPPLPoint d1,d2,p1,p2,p3;
|
||||||
long i11,i12,i21,i22,i13,i23,j,k;
|
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;
|
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);
|
poly1 = &(*iter1);
|
||||||
for(i11=0;i11<poly1->GetNumPoints();i11++) {
|
for(i11=0;i11<poly1->GetNumPoints();i11++) {
|
||||||
d1 = poly1->GetPoint(i11);
|
d1 = poly1->GetPoint(i11);
|
||||||
|
@ -494,7 +500,7 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
||||||
d2 = poly1->GetPoint(i12);
|
d2 = poly1->GetPoint(i12);
|
||||||
|
|
||||||
isdiagonal = false;
|
isdiagonal = false;
|
||||||
for(iter2 = iter1; iter2 != triangles.end(); ++iter2) {
|
for(iter2 = iter1; iter2 != triangles.end(); iter2++) {
|
||||||
if(iter1 == iter2) continue;
|
if(iter1 == iter2) continue;
|
||||||
poly2 = &(*iter2);
|
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);
|
parts->push_back(*iter1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TPPLPartition::ConvexPartition_HM(list<TPPLPoly> *inpolys, list<TPPLPoly> *parts) {
|
int TPPLPartition::ConvexPartition_HM(TPPLPolyList *inpolys, TPPLPolyList *parts) {
|
||||||
list<TPPLPoly> outpolys;
|
TPPLPolyList outpolys;
|
||||||
list<TPPLPoly>::iterator iter;
|
TPPLPolyList::iterator iter;
|
||||||
|
|
||||||
if(!RemoveHoles(inpolys,&outpolys)) return 0;
|
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;
|
if(!ConvexPartition_HM(&(*iter),parts)) return 0;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -571,14 +577,16 @@ int TPPLPartition::ConvexPartition_HM(list<TPPLPoly> *inpolys, list<TPPLPoly> *p
|
||||||
//minimum-weight polygon triangulation by dynamic programming
|
//minimum-weight polygon triangulation by dynamic programming
|
||||||
//O(n^3) time complexity
|
//O(n^3) time complexity
|
||||||
//O(n^2) space 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;
|
long i,j,k,gap,n;
|
||||||
DPState **dpstates;
|
DPState **dpstates = NULL;
|
||||||
TPPLPoint p1,p2,p3,p4;
|
TPPLPoint p1,p2,p3,p4;
|
||||||
long bestvertex;
|
long bestvertex;
|
||||||
tppl_float weight,minweight,d1,d2;
|
tppl_float weight,minweight,d1,d2;
|
||||||
Diagonal diagonal,newdiagonal;
|
Diagonal diagonal,newdiagonal;
|
||||||
list<Diagonal> diagonals;
|
DiagonalList diagonals;
|
||||||
TPPLPoly triangle;
|
TPPLPoly triangle;
|
||||||
int ret = 1;
|
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) {
|
void TPPLPartition::UpdateState(long a, long b, long w, long i, long j, DPState2 **dpstates) {
|
||||||
Diagonal newdiagonal;
|
Diagonal newdiagonal;
|
||||||
list<Diagonal> *pairs;
|
DiagonalList *pairs = NULL;
|
||||||
long w2;
|
long w2;
|
||||||
|
|
||||||
w2 = dpstates[a][b].weight;
|
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) {
|
void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) {
|
||||||
list<Diagonal> *pairs;
|
DiagonalList *pairs = NULL;
|
||||||
list<Diagonal>::iterator iter,lastiter;
|
DiagonalList::iterator iter,lastiter;
|
||||||
long top;
|
long top;
|
||||||
long w;
|
long w;
|
||||||
|
|
||||||
|
@ -742,7 +750,7 @@ void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPS
|
||||||
iter = pairs->end();
|
iter = pairs->end();
|
||||||
lastiter = pairs->end();
|
lastiter = pairs->end();
|
||||||
while(iter!=pairs->begin()) {
|
while(iter!=pairs->begin()) {
|
||||||
--iter;
|
iter--;
|
||||||
if(!IsReflex(vertices[iter->index2].p,vertices[j].p,vertices[k].p)) lastiter = iter;
|
if(!IsReflex(vertices[iter->index2].p,vertices[j].p,vertices[k].p)) lastiter = iter;
|
||||||
else break;
|
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) {
|
void TPPLPartition::TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) {
|
||||||
list<Diagonal> *pairs;
|
DiagonalList *pairs = NULL;
|
||||||
list<Diagonal>::iterator iter,lastiter;
|
DiagonalList::iterator iter,lastiter;
|
||||||
long top;
|
long top;
|
||||||
long w;
|
long w;
|
||||||
|
|
||||||
|
@ -778,7 +786,7 @@ void TPPLPartition::TypeB(long i, long j, long k, PartitionVertex *vertices, DPS
|
||||||
while(iter!=pairs->end()) {
|
while(iter!=pairs->end()) {
|
||||||
if(!IsReflex(vertices[i].p,vertices[j].p,vertices[iter->index1].p)) {
|
if(!IsReflex(vertices[i].p,vertices[j].p,vertices[iter->index1].p)) {
|
||||||
lastiter = iter;
|
lastiter = iter;
|
||||||
++iter;
|
iter++;
|
||||||
}
|
}
|
||||||
else break;
|
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);
|
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;
|
TPPLPoint p1,p2,p3,p4;
|
||||||
PartitionVertex *vertices;
|
PartitionVertex *vertices = NULL;
|
||||||
DPState2 **dpstates;
|
DPState2 **dpstates = NULL;
|
||||||
long i,j,k,n,gap;
|
long i,j,k,n,gap;
|
||||||
list<Diagonal> diagonals,diagonals2;
|
DiagonalList diagonals,diagonals2;
|
||||||
Diagonal diagonal,newdiagonal;
|
Diagonal diagonal,newdiagonal;
|
||||||
list<Diagonal> *pairs,*pairs2;
|
DiagonalList *pairs = NULL,*pairs2 = NULL;
|
||||||
list<Diagonal>::iterator iter,iter2;
|
DiagonalList::iterator iter,iter2;
|
||||||
int ret;
|
int ret;
|
||||||
TPPLPoly newpoly;
|
TPPLPoly newpoly;
|
||||||
list<long> indices;
|
vector<long> indices;
|
||||||
list<long>::iterator iiter;
|
vector<long>::iterator iiter;
|
||||||
bool ijreal,jkreal;
|
bool ijreal,jkreal;
|
||||||
|
|
||||||
n = poly->GetNumPoints();
|
n = poly->GetNumPoints();
|
||||||
|
@ -919,7 +929,7 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
||||||
}
|
}
|
||||||
if(!vertices[diagonal.index1].isConvex) {
|
if(!vertices[diagonal.index1].isConvex) {
|
||||||
iter = pairs->end();
|
iter = pairs->end();
|
||||||
--iter;
|
iter--;
|
||||||
j = iter->index2;
|
j = iter->index2;
|
||||||
newdiagonal.index1 = j;
|
newdiagonal.index1 = j;
|
||||||
newdiagonal.index2 = diagonal.index2;
|
newdiagonal.index2 = diagonal.index2;
|
||||||
|
@ -933,7 +943,7 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
iter2 = pairs2->end();
|
iter2 = pairs2->end();
|
||||||
--iter2;
|
iter2--;
|
||||||
if(iter->index1 != iter2->index1) pairs2->pop_back();
|
if(iter->index1 != iter2->index1) pairs2->pop_back();
|
||||||
else break;
|
else break;
|
||||||
}
|
}
|
||||||
|
@ -1003,7 +1013,7 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
||||||
pairs = &(dpstates[diagonal.index1][diagonal.index2].pairs);
|
pairs = &(dpstates[diagonal.index1][diagonal.index2].pairs);
|
||||||
if(!vertices[diagonal.index1].isConvex) {
|
if(!vertices[diagonal.index1].isConvex) {
|
||||||
iter = pairs->end();
|
iter = pairs->end();
|
||||||
--iter;
|
iter--;
|
||||||
j = iter->index2;
|
j = iter->index2;
|
||||||
if(iter->index1 != iter->index2) ijreal = false;
|
if(iter->index1 != iter->index2) ijreal = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1031,10 +1041,10 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
||||||
indices.push_back(j);
|
indices.push_back(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
indices.sort();
|
std::sort(indices.begin(), indices.end());
|
||||||
newpoly.Init((long)indices.size());
|
newpoly.Init((long)indices.size());
|
||||||
k=0;
|
k=0;
|
||||||
for(iiter = indices.begin();iiter!=indices.end(); ++iiter) {
|
for(iiter = indices.begin();iiter!=indices.end();iiter++) {
|
||||||
newpoly[k] = vertices[*iiter].p;
|
newpoly[k] = vertices[*iiter].p;
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
|
@ -1055,18 +1065,19 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, list<TPPLPoly> *parts) {
|
||||||
//the algorithm used here is outlined in the book
|
//the algorithm used here is outlined in the book
|
||||||
//"Computational Geometry: Algorithms and Applications"
|
//"Computational Geometry: Algorithms and Applications"
|
||||||
//by Mark de Berg, Otfried Cheong, Marc van Kreveld and Mark Overmars
|
//by Mark de Berg, Otfried Cheong, Marc van Kreveld and Mark Overmars
|
||||||
int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *monotonePolys) {
|
int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monotonePolys) {
|
||||||
list<TPPLPoly>::iterator iter;
|
TPPLPolyList::iterator iter;
|
||||||
MonotoneVertex *vertices;
|
MonotoneVertex *vertices = NULL;
|
||||||
long i,numvertices,vindex,vindex2,newnumvertices,maxnumvertices;
|
long i,numvertices,vindex,vindex2,newnumvertices,maxnumvertices;
|
||||||
long polystartindex, polyendindex;
|
long polystartindex, polyendindex;
|
||||||
TPPLPoly *poly;
|
TPPLPoly *poly = NULL;
|
||||||
MonotoneVertex *v,*v2,*vprev,*vnext;
|
MonotoneVertex *v = NULL,*v2 = NULL,*vprev = NULL,*vnext = NULL;
|
||||||
ScanLineEdge newedge;
|
ScanLineEdge newedge;
|
||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
numvertices = 0;
|
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();
|
numvertices += iter->GetNumPoints();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1075,7 +1086,7 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
||||||
newnumvertices = numvertices;
|
newnumvertices = numvertices;
|
||||||
|
|
||||||
polystartindex = 0;
|
polystartindex = 0;
|
||||||
for(iter = inpolys->begin(); iter != inpolys->end(); ++iter) {
|
for(iter = inpolys->begin(); iter != inpolys->end(); iter++) {
|
||||||
poly = &(*iter);
|
poly = &(*iter);
|
||||||
polyendindex = polystartindex + poly->GetNumPoints()-1;
|
polyendindex = polystartindex + poly->GetNumPoints()-1;
|
||||||
for(i=0;i<poly->GetNumPoints();i++) {
|
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;
|
set<ScanLineEdge>::iterator *edgeTreeIterators,edgeIter;
|
||||||
edgeTreeIterators = new set<ScanLineEdge>::iterator[maxnumvertices];
|
edgeTreeIterators = new set<ScanLineEdge>::iterator[maxnumvertices];
|
||||||
pair<set<ScanLineEdge>::iterator,bool> edgeTreeRet;
|
pair<set<ScanLineEdge>::iterator,bool> edgeTreeRet;
|
||||||
|
for(i = 0; i<numvertices; i++) edgeTreeIterators[i] = edgeTree.end();
|
||||||
|
|
||||||
//for each vertex
|
//for each vertex
|
||||||
for(i=0;i<numvertices;i++) {
|
for(i=0;i<numvertices;i++) {
|
||||||
|
@ -1152,16 +1164,15 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TPPL_VERTEXTYPE_END:
|
case TPPL_VERTEXTYPE_END:
|
||||||
|
if (edgeTreeIterators[v->previous] == edgeTree.end()) {
|
||||||
|
error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
//if helper(ei-1) is a merge vertex
|
//if helper(ei-1) is a merge vertex
|
||||||
if(vertextypes[helpers[v->previous]]==TPPL_VERTEXTYPE_MERGE) {
|
if(vertextypes[helpers[v->previous]]==TPPL_VERTEXTYPE_MERGE) {
|
||||||
//Insert the diagonal connecting vi to helper(ei-1) in D.
|
//Insert the diagonal connecting vi to helper(ei-1) in D.
|
||||||
AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous]);
|
AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous],
|
||||||
vertextypes[newnumvertices-2] = vertextypes[vindex];
|
vertextypes, edgeTreeIterators, &edgeTree, helpers);
|
||||||
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]];
|
|
||||||
}
|
}
|
||||||
//Delete ei-1 from T
|
//Delete ei-1 from T
|
||||||
edgeTree.erase(edgeTreeIterators[v->previous]);
|
edgeTree.erase(edgeTreeIterators[v->previous]);
|
||||||
|
@ -1176,15 +1187,10 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
||||||
error = true;
|
error = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
--edgeIter;
|
edgeIter--;
|
||||||
//Insert the diagonal connecting vi to helper(ej) in D.
|
//Insert the diagonal connecting vi to helper(ej) in D.
|
||||||
AddDiagonal(vertices,&newnumvertices,vindex,helpers[edgeIter->index]);
|
AddDiagonal(vertices,&newnumvertices,vindex,helpers[edgeIter->index],
|
||||||
vertextypes[newnumvertices-2] = vertextypes[vindex];
|
vertextypes, edgeTreeIterators, &edgeTree, helpers);
|
||||||
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]];
|
|
||||||
vindex2 = newnumvertices-2;
|
vindex2 = newnumvertices-2;
|
||||||
v2 = &(vertices[vindex2]);
|
v2 = &(vertices[vindex2]);
|
||||||
//helper(e j)<29>vi
|
//helper(e j)<29>vi
|
||||||
|
@ -1199,16 +1205,15 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TPPL_VERTEXTYPE_MERGE:
|
case TPPL_VERTEXTYPE_MERGE:
|
||||||
|
if (edgeTreeIterators[v->previous] == edgeTree.end()) {
|
||||||
|
error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
//if helper(ei-1) is a merge vertex
|
//if helper(ei-1) is a merge vertex
|
||||||
if(vertextypes[helpers[v->previous]]==TPPL_VERTEXTYPE_MERGE) {
|
if(vertextypes[helpers[v->previous]]==TPPL_VERTEXTYPE_MERGE) {
|
||||||
//Insert the diagonal connecting vi to helper(ei-1) in D.
|
//Insert the diagonal connecting vi to helper(ei-1) in D.
|
||||||
AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous]);
|
AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous],
|
||||||
vertextypes[newnumvertices-2] = vertextypes[vindex];
|
vertextypes, edgeTreeIterators, &edgeTree, helpers);
|
||||||
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]];
|
|
||||||
vindex2 = newnumvertices-2;
|
vindex2 = newnumvertices-2;
|
||||||
v2 = &(vertices[vindex2]);
|
v2 = &(vertices[vindex2]);
|
||||||
}
|
}
|
||||||
|
@ -1222,17 +1227,12 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
||||||
error = true;
|
error = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
--edgeIter;
|
edgeIter--;
|
||||||
//if helper(ej) is a merge vertex
|
//if helper(ej) is a merge vertex
|
||||||
if(vertextypes[helpers[edgeIter->index]]==TPPL_VERTEXTYPE_MERGE) {
|
if(vertextypes[helpers[edgeIter->index]]==TPPL_VERTEXTYPE_MERGE) {
|
||||||
//Insert the diagonal connecting vi to helper(e j) in D.
|
//Insert the diagonal connecting vi to helper(e j) in D.
|
||||||
AddDiagonal(vertices,&newnumvertices,vindex2,helpers[edgeIter->index]);
|
AddDiagonal(vertices,&newnumvertices,vindex2,helpers[edgeIter->index],
|
||||||
vertextypes[newnumvertices-2] = vertextypes[vindex2];
|
vertextypes, edgeTreeIterators, &edgeTree, helpers);
|
||||||
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]];
|
|
||||||
}
|
}
|
||||||
//helper(e j)<29>vi
|
//helper(e j)<29>vi
|
||||||
helpers[edgeIter->index] = vindex2;
|
helpers[edgeIter->index] = vindex2;
|
||||||
|
@ -1241,16 +1241,15 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
||||||
case TPPL_VERTEXTYPE_REGULAR:
|
case TPPL_VERTEXTYPE_REGULAR:
|
||||||
//if the interior of P lies to the right of vi
|
//if the interior of P lies to the right of vi
|
||||||
if(Below(v->p,vertices[v->previous].p)) {
|
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 helper(ei-1) is a merge vertex
|
||||||
if(vertextypes[helpers[v->previous]]==TPPL_VERTEXTYPE_MERGE) {
|
if(vertextypes[helpers[v->previous]]==TPPL_VERTEXTYPE_MERGE) {
|
||||||
//Insert the diagonal connecting vi to helper(ei-1) in D.
|
//Insert the diagonal connecting vi to helper(ei-1) in D.
|
||||||
AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous]);
|
AddDiagonal(vertices,&newnumvertices,vindex,helpers[v->previous],
|
||||||
vertextypes[newnumvertices-2] = vertextypes[vindex];
|
vertextypes, edgeTreeIterators, &edgeTree, helpers);
|
||||||
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]];
|
|
||||||
vindex2 = newnumvertices-2;
|
vindex2 = newnumvertices-2;
|
||||||
v2 = &(vertices[vindex2]);
|
v2 = &(vertices[vindex2]);
|
||||||
}
|
}
|
||||||
|
@ -1272,17 +1271,12 @@ int TPPLPartition::MonotonePartition(list<TPPLPoly> *inpolys, list<TPPLPoly> *mo
|
||||||
error = true;
|
error = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
--edgeIter;
|
edgeIter--;
|
||||||
//if helper(ej) is a merge vertex
|
//if helper(ej) is a merge vertex
|
||||||
if(vertextypes[helpers[edgeIter->index]]==TPPL_VERTEXTYPE_MERGE) {
|
if(vertextypes[helpers[edgeIter->index]]==TPPL_VERTEXTYPE_MERGE) {
|
||||||
//Insert the diagonal connecting vi to helper(e j) in D.
|
//Insert the diagonal connecting vi to helper(e j) in D.
|
||||||
AddDiagonal(vertices,&newnumvertices,vindex,helpers[edgeIter->index]);
|
AddDiagonal(vertices,&newnumvertices,vindex,helpers[edgeIter->index],
|
||||||
vertextypes[newnumvertices-2] = vertextypes[vindex];
|
vertextypes, edgeTreeIterators, &edgeTree, helpers);
|
||||||
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]];
|
|
||||||
}
|
}
|
||||||
//helper(e j)<29>vi
|
//helper(e j)<29>vi
|
||||||
helpers[edgeIter->index] = vindex;
|
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
|
//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;
|
long newindex1,newindex2;
|
||||||
|
|
||||||
newindex1 = *numvertices;
|
newindex1 = *numvertices;
|
||||||
|
@ -1364,6 +1361,18 @@ void TPPLPartition::AddDiagonal(MonotoneVertex *vertices, long *numvertices, lon
|
||||||
|
|
||||||
vertices[index2].next = newindex1;
|
vertices[index2].next = newindex1;
|
||||||
vertices[newindex1].previous = index2;
|
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) {
|
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
|
//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;
|
if(vertices[index1].p.y > vertices[index2].p.y) return true;
|
||||||
else if(vertices[index1].p.y == vertices[index2].p.y) {
|
else if(vertices[index1].p.y == vertices[index2].p.y) {
|
||||||
if(vertices[index1].p.x > vertices[index2].p.x) return true;
|
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
|
//triangulates monotone polygon
|
||||||
//O(n) time, O(n) space complexity
|
//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;
|
long i,i2,j,topindex,bottomindex,leftindex,rightindex,vindex;
|
||||||
TPPLPoint *points;
|
TPPLPoint *points = NULL;
|
||||||
long numpoints;
|
long numpoints;
|
||||||
TPPLPoly triangle;
|
TPPLPoly triangle;
|
||||||
|
|
||||||
numpoints = inPoly->GetNumPoints();
|
numpoints = inPoly->GetNumPoints();
|
||||||
points = inPoly->GetPoints();
|
points = inPoly->GetPoints();
|
||||||
|
|
||||||
//trivial calses
|
//trivial case
|
||||||
if(numpoints < 3) return 0;
|
|
||||||
if(numpoints == 3) {
|
if(numpoints == 3) {
|
||||||
triangles->push_back(*inPoly);
|
triangles->push_back(*inPoly);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
topindex = 0; bottomindex=0;
|
topindex = 0; bottomindex=0;
|
||||||
|
@ -1544,19 +1555,19 @@ int TPPLPartition::TriangulateMonotone(TPPLPoly *inPoly, list<TPPLPoly> *triangl
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TPPLPartition::Triangulate_MONO(list<TPPLPoly> *inpolys, list<TPPLPoly> *triangles) {
|
int TPPLPartition::Triangulate_MONO(TPPLPolyList *inpolys, TPPLPolyList *triangles) {
|
||||||
list<TPPLPoly> monotone;
|
TPPLPolyList monotone;
|
||||||
list<TPPLPoly>::iterator iter;
|
TPPLPolyList::iterator iter;
|
||||||
|
|
||||||
if(!MonotonePartition(inpolys,&monotone)) return 0;
|
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;
|
if(!TriangulateMonotone(&(*iter),triangles)) return 0;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TPPLPartition::Triangulate_MONO(TPPLPoly *poly, list<TPPLPoly> *triangles) {
|
int TPPLPartition::Triangulate_MONO(TPPLPoly *poly, TPPLPolyList *triangles) {
|
||||||
list<TPPLPoly> polys;
|
TPPLPolyList polys;
|
||||||
polys.push_back(*poly);
|
polys.push_back(*poly);
|
||||||
|
|
||||||
return Triangulate_MONO(&polys, triangles);
|
return Triangulate_MONO(&polys, triangles);
|
||||||
|
|
|
@ -18,9 +18,11 @@
|
||||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
//THE SOFTWARE.
|
//THE SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef POLYPARTITION_H
|
||||||
|
#define POLYPARTITION_H
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
using namespace std;
|
#include <set>
|
||||||
|
|
||||||
typedef double tppl_float;
|
typedef double tppl_float;
|
||||||
|
|
||||||
|
@ -31,6 +33,9 @@ typedef double tppl_float;
|
||||||
struct TPPLPoint {
|
struct TPPLPoint {
|
||||||
tppl_float x;
|
tppl_float x;
|
||||||
tppl_float y;
|
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 operator + (const TPPLPoint& p) const {
|
||||||
TPPLPoint r;
|
TPPLPoint r;
|
||||||
|
@ -71,15 +76,16 @@ struct TPPLPoint {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//Polygon implemented as an array of points with a 'hole' flag
|
//Polygon implemented as an array of points with a 'hole' flag
|
||||||
class TPPLPoly {
|
class TPPLPoly {
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
TPPLPoint *points;
|
TPPLPoint *points;
|
||||||
long numpoints;
|
long numpoints;
|
||||||
bool hole;
|
bool hole;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
//constructors/destructors
|
//constructors/destructors
|
||||||
TPPLPoly();
|
TPPLPoly();
|
||||||
|
@ -105,6 +111,10 @@ public:
|
||||||
return points[i];
|
return points[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TPPLPoint &GetPoint(long i) const {
|
||||||
|
return points[i];
|
||||||
|
}
|
||||||
|
|
||||||
TPPLPoint *GetPoints() {
|
TPPLPoint *GetPoints() {
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
@ -113,6 +123,10 @@ public:
|
||||||
return points[i];
|
return points[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TPPLPoint& operator[] (int i) const {
|
||||||
|
return points[i];
|
||||||
|
}
|
||||||
|
|
||||||
//clears the polygon points
|
//clears the polygon points
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
|
@ -137,10 +151,19 @@ public:
|
||||||
// TPPL_CCW : sets vertices in counter-clockwise order
|
// TPPL_CCW : sets vertices in counter-clockwise order
|
||||||
// TPPL_CW : sets vertices in clockwise order
|
// TPPL_CW : sets vertices in clockwise order
|
||||||
void SetOrientation(int orientation);
|
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 {
|
class TPPLPartition {
|
||||||
protected:
|
protected:
|
||||||
struct PartitionVertex {
|
struct PartitionVertex {
|
||||||
bool isActive;
|
bool isActive;
|
||||||
bool isConvex;
|
bool isConvex;
|
||||||
|
@ -150,6 +173,8 @@ protected:
|
||||||
tppl_float angle;
|
tppl_float angle;
|
||||||
PartitionVertex *previous;
|
PartitionVertex *previous;
|
||||||
PartitionVertex *next;
|
PartitionVertex *next;
|
||||||
|
|
||||||
|
PartitionVertex();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MonotoneVertex {
|
struct MonotoneVertex {
|
||||||
|
@ -162,7 +187,7 @@ protected:
|
||||||
MonotoneVertex *vertices;
|
MonotoneVertex *vertices;
|
||||||
public:
|
public:
|
||||||
VertexSorter(MonotoneVertex *v) : vertices(v) {}
|
VertexSorter(MonotoneVertex *v) : vertices(v) {}
|
||||||
bool operator() (long index1, long index2) const;
|
bool operator() (long index1, long index2);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Diagonal {
|
struct Diagonal {
|
||||||
|
@ -170,6 +195,12 @@ protected:
|
||||||
long index2;
|
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
|
//dynamic programming state for minimum-weight triangulation
|
||||||
struct DPState {
|
struct DPState {
|
||||||
bool visible;
|
bool visible;
|
||||||
|
@ -181,12 +212,12 @@ protected:
|
||||||
struct DPState2 {
|
struct DPState2 {
|
||||||
bool visible;
|
bool visible;
|
||||||
long weight;
|
long weight;
|
||||||
list<Diagonal> pairs;
|
DiagonalList pairs;
|
||||||
};
|
};
|
||||||
|
|
||||||
//edge that intersects the scanline
|
//edge that intersects the scanline
|
||||||
struct ScanLineEdge {
|
struct ScanLineEdge {
|
||||||
long index;
|
mutable long index;
|
||||||
TPPLPoint p1;
|
TPPLPoint p1;
|
||||||
TPPLPoint p2;
|
TPPLPoint p2;
|
||||||
|
|
||||||
|
@ -220,12 +251,14 @@ protected:
|
||||||
|
|
||||||
//helper functions for MonotonePartition
|
//helper functions for MonotonePartition
|
||||||
bool Below(TPPLPoint &p1, TPPLPoint &p2);
|
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
|
//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
|
//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
|
//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
|
// vertices of all hole polys have to be in clockwise order
|
||||||
// outpolys : a list of polygons without holes
|
// outpolys : a list of polygons without holes
|
||||||
//returns 1 on success, 0 on failure
|
//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
|
//triangulates a polygon by ear clipping
|
||||||
//time complexity O(n^2), n is the number of vertices
|
//time complexity O(n^2), n is the number of vertices
|
||||||
|
@ -247,7 +280,7 @@ public:
|
||||||
// vertices have to be in counter-clockwise order
|
// vertices have to be in counter-clockwise order
|
||||||
// triangles : a list of triangles (result)
|
// triangles : a list of triangles (result)
|
||||||
//returns 1 on success, 0 on failure
|
//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
|
//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
|
//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
|
// vertices of all hole polys have to be in clockwise order
|
||||||
// triangles : a list of triangles (result)
|
// triangles : a list of triangles (result)
|
||||||
//returns 1 on success, 0 on failure
|
//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
|
//creates an optimal polygon triangulation in terms of minimal edge length
|
||||||
//time complexity: O(n^3), n is the number of vertices
|
//time complexity: O(n^3), n is the number of vertices
|
||||||
|
@ -269,7 +302,7 @@ public:
|
||||||
// vertices have to be in counter-clockwise order
|
// vertices have to be in counter-clockwise order
|
||||||
// triangles : a list of triangles (result)
|
// triangles : a list of triangles (result)
|
||||||
//returns 1 on success, 0 on failure
|
//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
|
//triangulates a polygons by firstly partitioning it into monotone polygons
|
||||||
//time complexity: O(n*log(n)), n is the number of vertices
|
//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
|
// vertices have to be in counter-clockwise order
|
||||||
// triangles : a list of triangles (result)
|
// triangles : a list of triangles (result)
|
||||||
//returns 1 on success, 0 on failure
|
//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
|
//triangulates a list of polygons by firstly partitioning them into monotone polygons
|
||||||
//time complexity: O(n*log(n)), n is the number of vertices
|
//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
|
// vertices of all hole polys have to be in clockwise order
|
||||||
// triangles : a list of triangles (result)
|
// triangles : a list of triangles (result)
|
||||||
//returns 1 on success, 0 on failure
|
//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
|
//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
|
//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
|
// vertices of all hole polys have to be in clockwise order
|
||||||
// monotonePolys : a list of monotone polygons (result)
|
// monotonePolys : a list of monotone polygons (result)
|
||||||
//returns 1 on success, 0 on failure
|
//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
|
//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
|
//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
|
// vertices have to be in counter-clockwise order
|
||||||
// parts : resulting list of convex polygons
|
// parts : resulting list of convex polygons
|
||||||
//returns 1 on success, 0 on failure
|
//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
|
//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
|
//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
|
// vertices of all hole polys have to be in clockwise order
|
||||||
// parts : resulting list of convex polygons
|
// parts : resulting list of convex polygons
|
||||||
//returns 1 on success, 0 on failure
|
//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)
|
//optimal convex partitioning (in terms of number of resulting convex polygons)
|
||||||
//using the Keil-Snoeyink algorithm
|
//using the Keil-Snoeyink algorithm
|
||||||
|
@ -339,5 +372,8 @@ public:
|
||||||
// vertices have to be in counter-clockwise order
|
// vertices have to be in counter-clockwise order
|
||||||
// parts : resulting list of convex polygons
|
// parts : resulting list of convex polygons
|
||||||
//returns 1 on success, 0 on failure
|
//returns 1 on success, 0 on failure
|
||||||
int ConvexPartition_OPT(TPPLPoly *poly, list<TPPLPoly> *parts);
|
int ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.6)
|
||||||
|
|
||||||
include(PrecompiledHeader)
|
include(PrecompiledHeader)
|
||||||
|
|
||||||
add_library(libslic3r_gui STATIC
|
set(SLIC3R_GUI_SOURCES
|
||||||
pchheader.cpp
|
pchheader.cpp
|
||||||
pchheader.hpp
|
pchheader.hpp
|
||||||
GUI/AboutDialog.cpp
|
GUI/AboutDialog.cpp
|
||||||
|
@ -127,6 +127,12 @@ add_library(libslic3r_gui STATIC
|
||||||
Utils/HexFile.hpp
|
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)
|
target_link_libraries(libslic3r_gui libslic3r avrdude imgui)
|
||||||
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||||
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
|
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
|
||||||
|
|
|
@ -386,58 +386,6 @@ void GLVolume::render() const
|
||||||
::glPopMatrix();
|
::glPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLVolume::render_using_layer_height() const
|
|
||||||
{
|
|
||||||
if (!is_active)
|
|
||||||
return;
|
|
||||||
|
|
||||||
GLint current_program_id;
|
|
||||||
glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id);
|
|
||||||
|
|
||||||
if ((layer_height_texture_data.shader_id > 0) && (layer_height_texture_data.shader_id != current_program_id))
|
|
||||||
glUseProgram(layer_height_texture_data.shader_id);
|
|
||||||
|
|
||||||
GLint z_to_texture_row_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_to_texture_row") : -1;
|
|
||||||
GLint z_texture_row_to_normalized_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_texture_row_to_normalized") : -1;
|
|
||||||
GLint z_cursor_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor") : -1;
|
|
||||||
GLint z_cursor_band_width_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor_band_width") : -1;
|
|
||||||
GLint world_matrix_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "volume_world_matrix") : -1;
|
|
||||||
|
|
||||||
if (z_to_texture_row_id >= 0)
|
|
||||||
glUniform1f(z_to_texture_row_id, (GLfloat)layer_height_texture_z_to_row_id());
|
|
||||||
|
|
||||||
if (z_texture_row_to_normalized_id >= 0)
|
|
||||||
glUniform1f(z_texture_row_to_normalized_id, (GLfloat)(1.0f / layer_height_texture_height()));
|
|
||||||
|
|
||||||
if (z_cursor_id >= 0)
|
|
||||||
glUniform1f(z_cursor_id, (GLfloat)(layer_height_texture_data.print_object->model_object()->bounding_box().max(2) * layer_height_texture_data.z_cursor_relative));
|
|
||||||
|
|
||||||
if (z_cursor_band_width_id >= 0)
|
|
||||||
glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width);
|
|
||||||
|
|
||||||
if (world_matrix_id >= 0)
|
|
||||||
::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast<float>().data());
|
|
||||||
|
|
||||||
GLsizei w = (GLsizei)layer_height_texture_width();
|
|
||||||
GLsizei h = (GLsizei)layer_height_texture_height();
|
|
||||||
GLsizei half_w = w / 2;
|
|
||||||
GLsizei half_h = h / 2;
|
|
||||||
|
|
||||||
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, layer_height_texture_data.texture_id);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level0());
|
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1());
|
|
||||||
|
|
||||||
render();
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
|
|
||||||
if ((current_program_id > 0) && (layer_height_texture_data.shader_id != current_program_id))
|
|
||||||
glUseProgram(current_program_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) const
|
void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) const
|
||||||
{
|
{
|
||||||
if (!is_active)
|
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)
|
if (!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id)
|
||||||
return;
|
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_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));
|
GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first));
|
||||||
if (n_triangles + n_quads == 0)
|
if (n_triangles + n_quads == 0)
|
||||||
|
@ -558,44 +496,6 @@ void GLVolume::render_legacy() const
|
||||||
::glPopMatrix();
|
::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(
|
std::vector<int> GLVolumeCollection::load_object(
|
||||||
const ModelObject *model_object,
|
const ModelObject *model_object,
|
||||||
int obj_idx,
|
int obj_idx,
|
||||||
|
@ -603,19 +503,15 @@ std::vector<int> GLVolumeCollection::load_object(
|
||||||
const std::string &color_by,
|
const std::string &color_by,
|
||||||
bool use_VBOs)
|
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;
|
std::vector<int> volumes_idx;
|
||||||
for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx)
|
for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx)
|
||||||
for (int instance_idx : instance_idxs)
|
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;
|
return volumes_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GLVolumeCollection::load_object_volume(
|
int GLVolumeCollection::load_object_volume(
|
||||||
const ModelObject *model_object,
|
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 obj_idx,
|
||||||
int volume_idx,
|
int volume_idx,
|
||||||
int instance_idx,
|
int instance_idx,
|
||||||
|
@ -645,9 +541,7 @@ int GLVolumeCollection::load_object_volume(
|
||||||
color[2] = 1.0f;
|
color[2] = 1.0f;
|
||||||
}
|
}
|
||||||
color[3] = model_volume->is_model_part() ? 1.f : 0.5f; */
|
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;
|
color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
|
||||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
|
||||||
this->volumes.emplace_back(new GLVolume(color));
|
this->volumes.emplace_back(new GLVolume(color));
|
||||||
GLVolume &v = *this->volumes.back();
|
GLVolume &v = *this->volumes.back();
|
||||||
v.set_color_from_model_volume(model_volume);
|
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);
|
v.set_convex_hull(&model_volume->get_convex_hull(), false);
|
||||||
if (extruder_id != -1)
|
if (extruder_id != -1)
|
||||||
v.extruder_id = extruder_id;
|
v.extruder_id = extruder_id;
|
||||||
v.layer_height_texture = layer_height_texture;
|
|
||||||
}
|
}
|
||||||
v.is_modifier = ! model_volume->is_model_part();
|
v.is_modifier = ! model_volume->is_model_part();
|
||||||
v.shader_outside_printer_detection_enabled = 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);
|
return int(this->volumes.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
|
||||||
typedef std::pair<GLVolume*, double> GLVolumeWithZ;
|
typedef std::pair<GLVolume*, double> GLVolumeWithZ;
|
||||||
typedef std::vector<GLVolumeWithZ> GLVolumesWithZList;
|
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;
|
GLVolumesWithZList list;
|
||||||
|
list.reserve(volumes.size());
|
||||||
|
|
||||||
for (GLVolume* volume : volumes)
|
for (GLVolume* volume : volumes)
|
||||||
{
|
{
|
||||||
bool is_transparent = (volume->render_color[3] < 1.0f);
|
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::Transparent) && is_transparent) ||
|
||||||
(type == GLVolumeCollection::All))
|
(type == GLVolumeCollection::All)) &&
|
||||||
list.push_back(std::make_pair(volume, 0.0));
|
(! filter_func || filter_func(*volume)))
|
||||||
|
list.emplace_back(std::make_pair(volume, 0.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((type == GLVolumeCollection::Transparent) && (list.size() > 1))
|
if ((type == GLVolumeCollection::Transparent) && (list.size() > 1))
|
||||||
|
@ -826,19 +720,14 @@ GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollec
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool disable_cullface) const
|
void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool disable_cullface, std::function<bool(const GLVolume&)> filter_func) const
|
||||||
#else
|
|
||||||
void GLVolumeCollection::render_VBOs() const
|
|
||||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
|
||||||
{
|
{
|
||||||
::glEnable(GL_BLEND);
|
::glEnable(GL_BLEND);
|
||||||
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
::glCullFace(GL_BACK);
|
::glCullFace(GL_BACK);
|
||||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
|
||||||
if (disable_cullface)
|
if (disable_cullface)
|
||||||
::glDisable(GL_CULL_FACE);
|
::glDisable(GL_CULL_FACE);
|
||||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
|
||||||
|
|
||||||
::glEnableClientState(GL_VERTEX_ARRAY);
|
::glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
::glEnableClientState(GL_NORMAL_ARRAY);
|
::glEnableClientState(GL_NORMAL_ARRAY);
|
||||||
|
@ -861,28 +750,11 @@ void GLVolumeCollection::render_VBOs() const
|
||||||
if (z_range_id != -1)
|
if (z_range_id != -1)
|
||||||
::glUniform2fv(z_range_id, 1, (const GLfloat*)z_range);
|
::glUniform2fv(z_range_id, 1, (const GLfloat*)z_range);
|
||||||
|
|
||||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, filter_func);
|
||||||
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type);
|
for (GLVolumeWithZ& volume : to_render) {
|
||||||
for (GLVolumeWithZ& volume : to_render)
|
|
||||||
{
|
|
||||||
if (volume.first->layer_height_texture_data.can_use())
|
|
||||||
volume.first->generate_layer_height_texture(volume.first->layer_height_texture_data.print_object, false);
|
|
||||||
else
|
|
||||||
volume.first->set_render_color();
|
volume.first->set_render_color();
|
||||||
|
|
||||||
volume.first->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id);
|
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_ARRAY_BUFFER, 0);
|
||||||
::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||||
|
@ -890,54 +762,36 @@ void GLVolumeCollection::render_VBOs() const
|
||||||
::glDisableClientState(GL_VERTEX_ARRAY);
|
::glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
::glDisableClientState(GL_NORMAL_ARRAY);
|
::glDisableClientState(GL_NORMAL_ARRAY);
|
||||||
|
|
||||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
|
||||||
if (disable_cullface)
|
if (disable_cullface)
|
||||||
::glEnable(GL_CULL_FACE);
|
::glEnable(GL_CULL_FACE);
|
||||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
|
||||||
|
|
||||||
::glDisable(GL_BLEND);
|
::glDisable(GL_BLEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
|
||||||
void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface) const
|
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);
|
glEnable(GL_BLEND);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
glCullFace(GL_BACK);
|
glCullFace(GL_BACK);
|
||||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
|
||||||
if (disable_cullface)
|
if (disable_cullface)
|
||||||
::glDisable(GL_CULL_FACE);
|
::glDisable(GL_CULL_FACE);
|
||||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
|
||||||
|
|
||||||
glEnableClientState(GL_VERTEX_ARRAY);
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
glEnableClientState(GL_NORMAL_ARRAY);
|
glEnableClientState(GL_NORMAL_ARRAY);
|
||||||
|
|
||||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, std::function<bool(const GLVolume&)>());
|
||||||
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type);
|
|
||||||
for (GLVolumeWithZ& volume : to_render)
|
for (GLVolumeWithZ& volume : to_render)
|
||||||
{
|
{
|
||||||
volume.first->set_render_color();
|
volume.first->set_render_color();
|
||||||
volume.first->render_legacy();
|
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_VERTEX_ARRAY);
|
||||||
glDisableClientState(GL_NORMAL_ARRAY);
|
glDisableClientState(GL_NORMAL_ARRAY);
|
||||||
|
|
||||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
|
||||||
if (disable_cullface)
|
if (disable_cullface)
|
||||||
::glEnable(GL_CULL_FACE);
|
::glEnable(GL_CULL_FACE);
|
||||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
|
||||||
|
|
||||||
glDisable(GL_BLEND);
|
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;
|
GUI::GLCanvas3DManager _3DScene::s_canvas_mgr;
|
||||||
|
|
||||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
|
||||||
GLModel::GLModel()
|
GLModel::GLModel()
|
||||||
: m_useVBOs(false)
|
: m_useVBOs(false)
|
||||||
#if ENABLE_PRINT_BED_MODELS
|
#if ENABLE_PRINT_BED_MODELS
|
||||||
|
@ -2089,7 +1942,6 @@ bool GLCurvedArrow::on_init(bool useVBOs)
|
||||||
m_volume.finalize_geometry(m_useVBOs);
|
m_volume.finalize_geometry(m_useVBOs);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
|
||||||
|
|
||||||
#if ENABLE_PRINT_BED_MODELS
|
#if ENABLE_PRINT_BED_MODELS
|
||||||
bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs)
|
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);
|
model = Model::read_from_file(filename);
|
||||||
}
|
}
|
||||||
catch (std::exception &e)
|
catch (std::exception & /* ex */)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2126,7 +1978,7 @@ bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs)
|
||||||
else
|
else
|
||||||
m_volume.indexed_vertex_array.load_mesh_flat_shading(mesh);
|
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);
|
set_color(color, 4);
|
||||||
|
|
||||||
m_volume.bounding_box = m_volume.indexed_vertex_array.bounding_box();
|
m_volume.bounding_box = m_volume.indexed_vertex_array.bounding_box();
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
#include "slic3r/GUI/GLCanvas3DManager.hpp"
|
#include "slic3r/GUI/GLCanvas3DManager.hpp"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
class Print;
|
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 {
|
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:
|
public:
|
||||||
static const float SELECTED_COLOR[4];
|
static const float SELECTED_COLOR[4];
|
||||||
static const float HOVER_COLOR[4];
|
static const float HOVER_COLOR[4];
|
||||||
|
@ -416,48 +375,12 @@ public:
|
||||||
|
|
||||||
void set_range(coordf_t low, coordf_t high);
|
void set_range(coordf_t low, coordf_t high);
|
||||||
void render() const;
|
void render() const;
|
||||||
void render_using_layer_height() const;
|
|
||||||
void render_VBOs(int color_id, int detection_id, int worldmatrix_id) const;
|
void render_VBOs(int color_id, int detection_id, int worldmatrix_id) const;
|
||||||
void render_legacy() const;
|
void render_legacy() const;
|
||||||
|
|
||||||
void finalize_geometry(bool use_VBOs) { this->indexed_vertex_array.finalize_geometry(use_VBOs); }
|
void finalize_geometry(bool use_VBOs) { this->indexed_vertex_array.finalize_geometry(use_VBOs); }
|
||||||
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
|
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
|
||||||
|
|
||||||
/************************************************ Layer height texture ****************************************************/
|
|
||||||
std::shared_ptr<LayersTexture> layer_height_texture;
|
|
||||||
// Data to render this volume using the layer height texture
|
|
||||||
LayerHeightTextureData layer_height_texture_data;
|
|
||||||
|
|
||||||
bool has_layer_height_texture() const
|
|
||||||
{ return this->layer_height_texture.get() != nullptr; }
|
|
||||||
size_t layer_height_texture_width() const
|
|
||||||
{ return (this->layer_height_texture.get() == nullptr) ? 0 : this->layer_height_texture->width; }
|
|
||||||
size_t layer_height_texture_height() const
|
|
||||||
{ return (this->layer_height_texture.get() == nullptr) ? 0 : this->layer_height_texture->height; }
|
|
||||||
size_t layer_height_texture_cells() const
|
|
||||||
{ return (this->layer_height_texture.get() == nullptr) ? 0 : this->layer_height_texture->cells; }
|
|
||||||
void* layer_height_texture_data_ptr_level0() const {
|
|
||||||
return (layer_height_texture.get() == nullptr) ? 0 :
|
|
||||||
(void*)layer_height_texture->data.data();
|
|
||||||
}
|
|
||||||
void* layer_height_texture_data_ptr_level1() const {
|
|
||||||
return (layer_height_texture.get() == nullptr) ? 0 :
|
|
||||||
(void*)(layer_height_texture->data.data() + layer_height_texture->width * layer_height_texture->height * 4);
|
|
||||||
}
|
|
||||||
double layer_height_texture_z_to_row_id() const;
|
|
||||||
void generate_layer_height_texture(const PrintObject *print_object, bool force);
|
|
||||||
|
|
||||||
void set_layer_height_texture_data(unsigned int texture_id, unsigned int shader_id, const PrintObject* print_object, float z_cursor_relative, float edit_band_width)
|
|
||||||
{
|
|
||||||
layer_height_texture_data.texture_id = texture_id;
|
|
||||||
layer_height_texture_data.shader_id = shader_id;
|
|
||||||
layer_height_texture_data.print_object = print_object;
|
|
||||||
layer_height_texture_data.z_cursor_relative = z_cursor_relative;
|
|
||||||
layer_height_texture_data.edit_band_width = edit_band_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset_layer_height_texture_data() { layer_height_texture_data.reset(); }
|
|
||||||
|
|
||||||
void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; }
|
void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -465,7 +388,6 @@ typedef std::vector<GLVolume*> GLVolumePtrs;
|
||||||
|
|
||||||
class GLVolumeCollection
|
class GLVolumeCollection
|
||||||
{
|
{
|
||||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
|
||||||
public:
|
public:
|
||||||
enum ERenderType : unsigned char
|
enum ERenderType : unsigned char
|
||||||
{
|
{
|
||||||
|
@ -475,7 +397,6 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
|
||||||
// min and max vertex of the print box volume
|
// min and max vertex of the print box volume
|
||||||
float print_box_min[3];
|
float print_box_min[3];
|
||||||
float print_box_max[3];
|
float print_box_max[3];
|
||||||
|
@ -498,7 +419,6 @@ public:
|
||||||
|
|
||||||
int load_object_volume(
|
int load_object_volume(
|
||||||
const ModelObject *model_object,
|
const ModelObject *model_object,
|
||||||
std::shared_ptr<LayersTexture> &layer_height_texture,
|
|
||||||
int obj_idx,
|
int obj_idx,
|
||||||
int volume_idx,
|
int volume_idx,
|
||||||
int instance_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);
|
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.
|
// Render the volumes by OpenGL.
|
||||||
#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
|
void render_VBOs(ERenderType type, bool disable_cullface, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
|
||||||
void render_VBOs(ERenderType type, bool disable_cullface) const;
|
|
||||||
void render_legacy(ERenderType type, bool disable_cullface) 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,
|
// Finalize the initialization of the geometry & indices,
|
||||||
// upload the geometry and indices to OpenGL VBO objects
|
// upload the geometry and indices to OpenGL VBO objects
|
||||||
|
@ -563,7 +478,6 @@ private:
|
||||||
GLVolumeCollection& operator=(const GLVolumeCollection &);
|
GLVolumeCollection& operator=(const GLVolumeCollection &);
|
||||||
};
|
};
|
||||||
|
|
||||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
|
||||||
class GLModel
|
class GLModel
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
@ -630,7 +544,6 @@ public:
|
||||||
protected:
|
protected:
|
||||||
virtual bool on_init(bool useVBOs);
|
virtual bool on_init(bool useVBOs);
|
||||||
};
|
};
|
||||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
|
||||||
|
|
||||||
#if ENABLE_PRINT_BED_MODELS
|
#if ENABLE_PRINT_BED_MODELS
|
||||||
class GLBed : public GLModel
|
class GLBed : public GLModel
|
||||||
|
|
|
@ -115,7 +115,7 @@ AboutDialog::AboutDialog()
|
||||||
vsizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3);
|
vsizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3);
|
||||||
|
|
||||||
// this->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this);
|
// 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);
|
SetSizer(main_sizer);
|
||||||
main_sizer->SetSizeHints(this);
|
main_sizer->SetSizeHints(this);
|
||||||
|
|
|
@ -59,6 +59,11 @@ void AppConfig::set_defaults()
|
||||||
if (get("use_legacy_opengl").empty())
|
if (get("use_legacy_opengl").empty())
|
||||||
set("use_legacy_opengl", "0");
|
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())
|
if (get("remember_output_path").empty())
|
||||||
set("remember_output_path", "1");
|
set("remember_output_path", "1");
|
||||||
|
|
||||||
|
|
|
@ -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
|
bool default_variant = true; // Mark the first variant as default in the GUI
|
||||||
for (const auto &variant : model.variants) {
|
for (const auto &variant : model.variants) {
|
||||||
const auto label = wxString::Format("%s %s %s %s", variant.name, _(L("mm")), _(L("nozzle")),
|
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;
|
default_variant = false;
|
||||||
auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name);
|
auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name);
|
||||||
const size_t idx = cboxes.size();
|
const size_t idx = cboxes.size();
|
||||||
|
@ -803,7 +803,7 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
||||||
// Public
|
// Public
|
||||||
|
|
||||||
ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) :
|
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(new priv(this))
|
||||||
{
|
{
|
||||||
p->run_reason = reason;
|
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.
|
// A different naming convention is used for the Wizard on Windows vs. OSX & GTK.
|
||||||
#if WIN32
|
#if WIN32
|
||||||
static const wxString config_wizard_name = L("Configuration Wizard");
|
static const wxString config_wizard_name = L("Configuration Wizard");
|
||||||
|
static const wxString config_wizard_name_menu = L("Configuration &Wizard");
|
||||||
#else
|
#else
|
||||||
static const wxString config_wizard_name = L("Configuration Assistant");
|
static const wxString config_wizard_name = L("Configuration Assistant");
|
||||||
|
static const wxString config_wizard_name_menu = L("Configuration &Assistant");
|
||||||
#endif
|
#endif
|
||||||
return config_wizard_name;
|
return from_menu ? config_wizard_name_menu : config_wizard_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ public:
|
||||||
// Run the Wizard. Return whether it was completed.
|
// Run the Wizard. Return whether it was completed.
|
||||||
bool run(PresetBundle *preset_bundle, const PresetUpdater *updater);
|
bool run(PresetBundle *preset_bundle, const PresetUpdater *updater);
|
||||||
|
|
||||||
static const wxString& name();
|
static const wxString& name(const bool from_menu = false);
|
||||||
private:
|
private:
|
||||||
struct priv;
|
struct priv;
|
||||||
std::unique_ptr<priv> p;
|
std::unique_ptr<priv> p;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,7 +2,9 @@
|
||||||
#define slic3r_GLCanvas3D_hpp_
|
#define slic3r_GLCanvas3D_hpp_
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "libslic3r/Technologies.hpp"
|
||||||
#include "3DScene.hpp"
|
#include "3DScene.hpp"
|
||||||
#include "GLToolbar.hpp"
|
#include "GLToolbar.hpp"
|
||||||
#include "Event.hpp"
|
#include "Event.hpp"
|
||||||
|
@ -20,6 +22,9 @@ class wxTimerEvent;
|
||||||
class wxPaintEvent;
|
class wxPaintEvent;
|
||||||
class wxGLCanvas;
|
class wxGLCanvas;
|
||||||
|
|
||||||
|
// Support for Retina OpenGL on Mac OS
|
||||||
|
#define ENABLE_RETINA_GL __APPLE__
|
||||||
|
|
||||||
class GLUquadric;
|
class GLUquadric;
|
||||||
typedef class GLUquadric GLUquadricObj;
|
typedef class GLUquadric GLUquadricObj;
|
||||||
|
|
||||||
|
@ -29,11 +34,17 @@ class GLShader;
|
||||||
class ExPolygon;
|
class ExPolygon;
|
||||||
class BackgroundSlicingProcess;
|
class BackgroundSlicingProcess;
|
||||||
class GCodePreviewData;
|
class GCodePreviewData;
|
||||||
|
struct SlicingParameters;
|
||||||
|
enum LayerHeightEditActionType : unsigned int;
|
||||||
|
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
class GLGizmoBase;
|
class GLGizmoBase;
|
||||||
|
|
||||||
|
#if ENABLE_RETINA_GL
|
||||||
|
class RetinaHelper;
|
||||||
|
#endif
|
||||||
|
|
||||||
class GeometryBuffer
|
class GeometryBuffer
|
||||||
{
|
{
|
||||||
std::vector<float> m_vertices;
|
std::vector<float> m_vertices;
|
||||||
|
@ -53,16 +64,20 @@ class Size
|
||||||
{
|
{
|
||||||
int m_width;
|
int m_width;
|
||||||
int m_height;
|
int m_height;
|
||||||
|
float m_scale_factor;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Size();
|
Size();
|
||||||
Size(int width, int height);
|
Size(int width, int height, float scale_factor = 1.0);
|
||||||
|
|
||||||
int get_width() const;
|
int get_width() const;
|
||||||
void set_width(int width);
|
void set_width(int width);
|
||||||
|
|
||||||
int get_height() const;
|
int get_height() const;
|
||||||
void set_height(int height);
|
void set_height(int height);
|
||||||
|
|
||||||
|
int get_scale_factor() const;
|
||||||
|
void set_scale_factor(int height);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Rect
|
class Rect
|
||||||
|
@ -87,6 +102,9 @@ public:
|
||||||
|
|
||||||
float get_bottom() const;
|
float get_bottom() const;
|
||||||
void set_bottom(float bottom);
|
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);
|
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_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent);
|
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_REMOVE_OBJECT, SimpleEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent);
|
||||||
|
@ -171,7 +188,7 @@ class GLCanvas3D
|
||||||
std::string get_type_as_string() const;
|
std::string get_type_as_string() const;
|
||||||
|
|
||||||
float get_theta() const { return m_theta; }
|
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; }
|
const Vec3d& get_target() const { return m_target; }
|
||||||
void set_target(const Vec3d& target, GLCanvas3D& canvas);
|
void set_target(const Vec3d& target, GLCanvas3D& canvas);
|
||||||
|
@ -205,6 +222,8 @@ class GLCanvas3D
|
||||||
mutable GLBed m_model;
|
mutable GLBed m_model;
|
||||||
#endif // ENABLE_PRINT_BED_MODELS
|
#endif // ENABLE_PRINT_BED_MODELS
|
||||||
|
|
||||||
|
mutable float m_scale_factor;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Bed();
|
Bed();
|
||||||
|
|
||||||
|
@ -220,9 +239,9 @@ class GLCanvas3D
|
||||||
Point point_projection(const Point& point) const;
|
Point point_projection(const Point& point) const;
|
||||||
|
|
||||||
#if ENABLE_PRINT_BED_MODELS
|
#if ENABLE_PRINT_BED_MODELS
|
||||||
void render(float theta, bool useVBOs) const;
|
void render(float theta, bool useVBOs, float scale_factor) const;
|
||||||
#else
|
#else
|
||||||
void render(float theta) const;
|
void render(float theta, float scale_factor) const;
|
||||||
#endif // ENABLE_PRINT_BED_MODELS
|
#endif // ENABLE_PRINT_BED_MODELS
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -293,12 +312,45 @@ class GLCanvas3D
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static const float THICKNESS_BAR_WIDTH;
|
||||||
|
static const float THICKNESS_RESET_BUTTON_HEIGHT;
|
||||||
|
|
||||||
bool m_use_legacy_opengl;
|
bool m_use_legacy_opengl;
|
||||||
bool m_enabled;
|
bool m_enabled;
|
||||||
Shader m_shader;
|
Shader m_shader;
|
||||||
unsigned int m_z_texture_id;
|
unsigned int m_z_texture_id;
|
||||||
mutable GLTexture m_tooltip_texture;
|
mutable GLTexture m_tooltip_texture;
|
||||||
mutable GLTexture m_reset_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:
|
public:
|
||||||
EState state;
|
EState state;
|
||||||
|
@ -306,12 +358,14 @@ class GLCanvas3D
|
||||||
float strength;
|
float strength;
|
||||||
int last_object_id;
|
int last_object_id;
|
||||||
float last_z;
|
float last_z;
|
||||||
unsigned int last_action;
|
LayerHeightEditActionType last_action;
|
||||||
|
|
||||||
LayersEditing();
|
LayersEditing();
|
||||||
~LayersEditing();
|
~LayersEditing();
|
||||||
|
|
||||||
bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename);
|
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;
|
bool is_allowed() const;
|
||||||
void set_use_legacy_opengl(bool use_legacy_opengl);
|
void set_use_legacy_opengl(bool use_legacy_opengl);
|
||||||
|
@ -319,11 +373,12 @@ class GLCanvas3D
|
||||||
bool is_enabled() const;
|
bool is_enabled() const;
|
||||||
void set_enabled(bool enabled);
|
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;
|
void adjust_layer_height_profile();
|
||||||
|
void accept_changes(GLCanvas3D& canvas);
|
||||||
int get_shader_program_id() const;
|
void reset_layer_height_profile(GLCanvas3D& canvas);
|
||||||
|
|
||||||
static float get_cursor_z_relative(const GLCanvas3D& canvas);
|
static float get_cursor_z_relative(const GLCanvas3D& canvas);
|
||||||
static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y);
|
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_bar_rect_viewport(const GLCanvas3D& canvas);
|
||||||
static Rect get_reset_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:
|
private:
|
||||||
bool _is_initialized() const;
|
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_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_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_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const;
|
||||||
void _render_profile(const PrintObject& print_object, 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
|
struct Mouse
|
||||||
|
@ -492,10 +554,10 @@ public:
|
||||||
#if ENABLE_RENDER_SELECTION_CENTER
|
#if ENABLE_RENDER_SELECTION_CENTER
|
||||||
GLUquadricObj* m_quadric;
|
GLUquadricObj* m_quadric;
|
||||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
|
||||||
mutable GLArrow m_arrow;
|
mutable GLArrow m_arrow;
|
||||||
mutable GLCurvedArrow m_curved_arrow;
|
mutable GLCurvedArrow m_curved_arrow;
|
||||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
|
||||||
|
mutable float m_scale_factor;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Selection();
|
Selection();
|
||||||
|
@ -504,9 +566,7 @@ public:
|
||||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
|
||||||
void set_volumes(GLVolumePtrs* volumes);
|
void set_volumes(GLVolumePtrs* volumes);
|
||||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
|
||||||
bool init(bool useVBOs);
|
bool init(bool useVBOs);
|
||||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
|
||||||
|
|
||||||
Model* get_model() const { return m_model; }
|
Model* get_model() const { return m_model; }
|
||||||
void set_model(Model* model);
|
void set_model(Model* model);
|
||||||
|
@ -580,13 +640,11 @@ public:
|
||||||
|
|
||||||
void erase();
|
void erase();
|
||||||
|
|
||||||
void render() const;
|
void render(float scale_factor = 1.0) const;
|
||||||
#if ENABLE_RENDER_SELECTION_CENTER
|
#if ENABLE_RENDER_SELECTION_CENTER
|
||||||
void render_center() const;
|
void render_center() const;
|
||||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
|
||||||
void render_sidebar_hints(const std::string& sidebar_field) const;
|
void render_sidebar_hints(const std::string& sidebar_field) const;
|
||||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
|
||||||
|
|
||||||
bool requires_local_axes() const;
|
bool requires_local_axes() const;
|
||||||
|
|
||||||
|
@ -604,7 +662,6 @@ public:
|
||||||
void _render_selected_volumes() const;
|
void _render_selected_volumes() const;
|
||||||
void _render_synchronized_volumes() const;
|
void _render_synchronized_volumes() const;
|
||||||
void _render_bounding_box(const BoundingBoxf3& box, float* color) 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_position_hints(const std::string& sidebar_field) const;
|
||||||
void _render_sidebar_rotation_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;
|
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_rotation_hint(Axis axis) const;
|
||||||
void _render_sidebar_scale_hint(Axis axis) const;
|
void _render_sidebar_scale_hint(Axis axis) const;
|
||||||
void _render_sidebar_size_hint(Axis axis, double length) const;
|
void _render_sidebar_size_hint(Axis axis, double length) const;
|
||||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
enum SyncRotationType {
|
||||||
void _synchronize_unselected_instances(bool including_z = false);
|
// Do not synchronize rotation. Either not rotating at all, or rotating by world Z axis.
|
||||||
|
SYNC_ROTATION_NONE = 0,
|
||||||
|
// Synchronize fully. Used from "place on bed" feature.
|
||||||
|
SYNC_ROTATION_FULL = 1,
|
||||||
|
// Synchronize after rotation by an axis not parallel with Z.
|
||||||
|
SYNC_ROTATION_GENERAL = 2,
|
||||||
|
};
|
||||||
|
void _synchronize_unselected_instances(SyncRotationType sync_rotation_type);
|
||||||
void _synchronize_unselected_volumes();
|
void _synchronize_unselected_volumes();
|
||||||
#if ENABLE_ENSURE_ON_BED_WHILE_SCALING
|
|
||||||
void _ensure_on_bed();
|
void _ensure_on_bed();
|
||||||
#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClippingPlane
|
class ClippingPlane
|
||||||
|
@ -649,10 +711,6 @@ public:
|
||||||
private:
|
private:
|
||||||
class Gizmos
|
class Gizmos
|
||||||
{
|
{
|
||||||
static const float OverlayIconsScale;
|
|
||||||
static const float OverlayBorder;
|
|
||||||
static const float OverlayGapY;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum EType : unsigned char
|
enum EType : unsigned char
|
||||||
{
|
{
|
||||||
|
@ -673,6 +731,10 @@ private:
|
||||||
BackgroundTexture m_background_texture;
|
BackgroundTexture m_background_texture;
|
||||||
EType m_current;
|
EType m_current;
|
||||||
|
|
||||||
|
float m_overlay_icons_scale;
|
||||||
|
float m_overlay_border;
|
||||||
|
float m_overlay_gap_y;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Gizmos();
|
Gizmos();
|
||||||
~Gizmos();
|
~Gizmos();
|
||||||
|
@ -682,6 +744,8 @@ private:
|
||||||
bool is_enabled() const;
|
bool is_enabled() const;
|
||||||
void set_enabled(bool enable);
|
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);
|
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 GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection);
|
||||||
void update_on_off_state(const Selection& selection);
|
void update_on_off_state(const Selection& selection);
|
||||||
|
@ -749,7 +813,7 @@ private:
|
||||||
struct Triangles
|
struct Triangles
|
||||||
{
|
{
|
||||||
Pointf3s object;
|
Pointf3s object;
|
||||||
Pointf3s suppports;
|
Pointf3s supports;
|
||||||
};
|
};
|
||||||
typedef std::map<unsigned int, Triangles> ObjectIdToTrianglesMap;
|
typedef std::map<unsigned int, Triangles> ObjectIdToTrianglesMap;
|
||||||
double z;
|
double z;
|
||||||
|
@ -771,7 +835,7 @@ private:
|
||||||
public:
|
public:
|
||||||
WarningTexture();
|
WarningTexture();
|
||||||
|
|
||||||
bool generate(const std::string& msg);
|
bool generate(const std::string& msg, const GLCanvas3D& canvas);
|
||||||
|
|
||||||
void render(const GLCanvas3D& canvas) const;
|
void render(const GLCanvas3D& canvas) const;
|
||||||
};
|
};
|
||||||
|
@ -801,6 +865,9 @@ private:
|
||||||
|
|
||||||
wxGLCanvas* m_canvas;
|
wxGLCanvas* m_canvas;
|
||||||
wxGLContext* m_context;
|
wxGLContext* m_context;
|
||||||
|
#if ENABLE_RETINA_GL
|
||||||
|
std::unique_ptr<RetinaHelper> m_retina_helper;
|
||||||
|
#endif
|
||||||
bool m_in_render;
|
bool m_in_render;
|
||||||
LegendTexture m_legend_texture;
|
LegendTexture m_legend_texture;
|
||||||
WarningTexture m_warning_texture;
|
WarningTexture m_warning_texture;
|
||||||
|
@ -821,7 +888,7 @@ private:
|
||||||
|
|
||||||
mutable GLVolumeCollection m_volumes;
|
mutable GLVolumeCollection m_volumes;
|
||||||
Selection m_selection;
|
Selection m_selection;
|
||||||
DynamicPrintConfig* m_config;
|
const DynamicPrintConfig* m_config;
|
||||||
Model* m_model;
|
Model* m_model;
|
||||||
BackgroundSlicingProcess *m_process;
|
BackgroundSlicingProcess *m_process;
|
||||||
|
|
||||||
|
@ -860,9 +927,7 @@ public:
|
||||||
GLCanvas3D(wxGLCanvas* canvas);
|
GLCanvas3D(wxGLCanvas* canvas);
|
||||||
~GLCanvas3D();
|
~GLCanvas3D();
|
||||||
|
|
||||||
#if ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
void set_context(wxGLContext* context) { m_context = context; }
|
void set_context(wxGLContext* context) { m_context = context; }
|
||||||
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
|
|
||||||
wxGLCanvas* get_wxglcanvas() { return m_canvas; }
|
wxGLCanvas* get_wxglcanvas() { return m_canvas; }
|
||||||
|
|
||||||
|
@ -871,17 +936,13 @@ public:
|
||||||
bool init(bool useVBOs, bool use_legacy_opengl);
|
bool init(bool useVBOs, bool use_legacy_opengl);
|
||||||
void post_event(wxEvent &&event);
|
void post_event(wxEvent &&event);
|
||||||
|
|
||||||
#if !ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
bool set_current();
|
|
||||||
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
|
|
||||||
void set_as_dirty();
|
void set_as_dirty();
|
||||||
|
|
||||||
unsigned int get_volumes_count() const;
|
unsigned int get_volumes_count() const;
|
||||||
void reset_volumes();
|
void reset_volumes();
|
||||||
int check_volumes_outside_state() const;
|
int check_volumes_outside_state() const;
|
||||||
|
|
||||||
void set_config(DynamicPrintConfig* config);
|
void set_config(const DynamicPrintConfig* config);
|
||||||
void set_process(BackgroundSlicingProcess* process);
|
void set_process(BackgroundSlicingProcess* process);
|
||||||
void set_model(Model* model);
|
void set_model(Model* model);
|
||||||
|
|
||||||
|
@ -1005,6 +1066,8 @@ public:
|
||||||
|
|
||||||
void handle_sidebar_focus_event(const std::string& opt_key, bool focus_on);
|
void handle_sidebar_focus_event(const std::string& opt_key, bool focus_on);
|
||||||
|
|
||||||
|
void update_ui_from_settings();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _is_shown_on_screen() const;
|
bool _is_shown_on_screen() const;
|
||||||
#if !ENABLE_REWORKED_BED_SHAPE_CHANGE
|
#if !ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||||
|
@ -1013,9 +1076,7 @@ private:
|
||||||
|
|
||||||
bool _init_toolbar();
|
bool _init_toolbar();
|
||||||
|
|
||||||
#if ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
bool _set_current();
|
bool _set_current();
|
||||||
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
void _resize(unsigned int w, unsigned int h);
|
void _resize(unsigned int w, unsigned int h);
|
||||||
|
|
||||||
BoundingBoxf3 _max_bounding_box() const;
|
BoundingBoxf3 _max_bounding_box() const;
|
||||||
|
@ -1023,7 +1084,6 @@ private:
|
||||||
void _zoom_to_bounding_box(const BoundingBoxf3& bbox);
|
void _zoom_to_bounding_box(const BoundingBoxf3& bbox);
|
||||||
float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const;
|
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 _refresh_if_shown_on_screen();
|
||||||
|
|
||||||
void _camera_tranform() const;
|
void _camera_tranform() const;
|
||||||
|
@ -1038,7 +1098,6 @@ private:
|
||||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||||
void _render_warning_texture() const;
|
void _render_warning_texture() const;
|
||||||
void _render_legend_texture() const;
|
void _render_legend_texture() const;
|
||||||
void _render_layer_editing_overlay() const;
|
|
||||||
void _render_volumes(bool fake_colors) const;
|
void _render_volumes(bool fake_colors) const;
|
||||||
void _render_current_gizmo() const;
|
void _render_current_gizmo() const;
|
||||||
void _render_gizmos_overlay() const;
|
void _render_gizmos_overlay() const;
|
||||||
|
@ -1048,14 +1107,11 @@ private:
|
||||||
void _render_camera_target() const;
|
void _render_camera_target() const;
|
||||||
#endif // ENABLE_SHOW_CAMERA_TARGET
|
#endif // ENABLE_SHOW_CAMERA_TARGET
|
||||||
void _render_sla_slices() const;
|
void _render_sla_slices() const;
|
||||||
#if ENABLE_SIDEBAR_VISUAL_HINTS
|
|
||||||
void _render_selection_sidebar_hints() const;
|
void _render_selection_sidebar_hints() const;
|
||||||
#endif // ENABLE_SIDEBAR_VISUAL_HINTS
|
|
||||||
|
|
||||||
void _update_volumes_hover_state() const;
|
void _update_volumes_hover_state() const;
|
||||||
void _update_gizmos_data();
|
void _update_gizmos_data();
|
||||||
|
|
||||||
float _get_layers_editing_cursor_z_relative() const;
|
|
||||||
void _perform_layer_editing_action(wxMouseEvent* evt = nullptr);
|
void _perform_layer_editing_action(wxMouseEvent* evt = nullptr);
|
||||||
|
|
||||||
// Convert the screen space coordinate to an object space coordinate.
|
// Convert the screen space coordinate to an object space coordinate.
|
||||||
|
|
|
@ -113,16 +113,13 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten
|
||||||
GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::MS_Unknown;
|
GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::MS_Unknown;
|
||||||
|
|
||||||
GLCanvas3DManager::GLCanvas3DManager()
|
GLCanvas3DManager::GLCanvas3DManager()
|
||||||
#if ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
: m_context(nullptr)
|
: m_context(nullptr)
|
||||||
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
, m_gl_initialized(false)
|
, m_gl_initialized(false)
|
||||||
, m_use_legacy_opengl(false)
|
, m_use_legacy_opengl(false)
|
||||||
, m_use_VBOs(false)
|
, m_use_VBOs(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
GLCanvas3DManager::~GLCanvas3DManager()
|
GLCanvas3DManager::~GLCanvas3DManager()
|
||||||
{
|
{
|
||||||
if (m_context != nullptr)
|
if (m_context != nullptr)
|
||||||
|
@ -131,7 +128,6 @@ GLCanvas3DManager::~GLCanvas3DManager()
|
||||||
m_context = nullptr;
|
m_context = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
|
|
||||||
bool GLCanvas3DManager::add(wxGLCanvas* canvas)
|
bool GLCanvas3DManager::add(wxGLCanvas* canvas)
|
||||||
{
|
{
|
||||||
|
@ -147,7 +143,6 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas)
|
||||||
|
|
||||||
canvas3D->bind_event_handlers();
|
canvas3D->bind_event_handlers();
|
||||||
|
|
||||||
#if ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
if (m_context == nullptr)
|
if (m_context == nullptr)
|
||||||
{
|
{
|
||||||
m_context = new wxGLContext(canvas);
|
m_context = new wxGLContext(canvas);
|
||||||
|
@ -156,7 +151,6 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas3D->set_context(m_context);
|
canvas3D->set_context(m_context);
|
||||||
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
|
|
||||||
m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D));
|
m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D));
|
||||||
|
|
||||||
|
|
|
@ -51,9 +51,7 @@ class GLCanvas3DManager
|
||||||
typedef std::map<wxGLCanvas*, GLCanvas3D*> CanvasesMap;
|
typedef std::map<wxGLCanvas*, GLCanvas3D*> CanvasesMap;
|
||||||
|
|
||||||
CanvasesMap m_canvases;
|
CanvasesMap m_canvases;
|
||||||
#if ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
wxGLContext* m_context;
|
wxGLContext* m_context;
|
||||||
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
GLInfo m_gl_info;
|
GLInfo m_gl_info;
|
||||||
bool m_gl_initialized;
|
bool m_gl_initialized;
|
||||||
bool m_use_legacy_opengl;
|
bool m_use_legacy_opengl;
|
||||||
|
@ -62,9 +60,7 @@ class GLCanvas3DManager
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GLCanvas3DManager();
|
GLCanvas3DManager();
|
||||||
#if ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
~GLCanvas3DManager();
|
~GLCanvas3DManager();
|
||||||
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
|
|
||||||
|
|
||||||
bool add(wxGLCanvas* canvas);
|
bool add(wxGLCanvas* canvas);
|
||||||
bool remove(wxGLCanvas* canvas);
|
bool remove(wxGLCanvas* canvas);
|
||||||
|
|
|
@ -409,9 +409,6 @@ void GLGizmoRotate::on_render(const GLCanvas3D::Selection& selection) const
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const BoundingBoxf3& box = selection.get_bounding_box();
|
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;
|
std::string axis;
|
||||||
switch (m_axis)
|
switch (m_axis)
|
||||||
|
@ -421,13 +418,9 @@ void GLGizmoRotate::on_render(const GLCanvas3D::Selection& selection) const
|
||||||
case Z: { axis = "Z"; break; }
|
case Z: { axis = "Z"; break; }
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_WORLD_ROTATIONS
|
|
||||||
if (!m_dragging && (m_hover_id == 0))
|
if (!m_dragging && (m_hover_id == 0))
|
||||||
set_tooltip(axis);
|
set_tooltip(axis);
|
||||||
else if (m_dragging)
|
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");
|
set_tooltip(axis + ": " + format((float)Geometry::rad2deg(m_angle), 4) + "\u00B0");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -571,11 +564,7 @@ void GLGizmoRotate::render_angle() const
|
||||||
|
|
||||||
void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const
|
void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const
|
||||||
{
|
{
|
||||||
#if ENABLE_WORLD_ROTATIONS
|
|
||||||
double grabber_radius = (double)m_radius * (1.0 + (double)GrabberOffset);
|
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].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0);
|
||||||
m_grabbers[0].angles(2) = m_angle;
|
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)
|
if (m_hover_id != -1)
|
||||||
{
|
{
|
||||||
|
assert(m_planes_valid);
|
||||||
m_normal = m_planes[m_hover_id].normal;
|
m_normal = m_planes[m_hover_id].normal;
|
||||||
m_starting_center = selection.get_bounding_box().center();
|
m_starting_center = selection.get_bounding_box().center();
|
||||||
}
|
}
|
||||||
|
@ -1457,6 +1447,8 @@ void GLGizmoFlatten::on_render(const GLCanvas3D::Selection& selection) const
|
||||||
::glPushMatrix();
|
::glPushMatrix();
|
||||||
::glMultMatrixd(m.data());
|
::glMultMatrixd(m.data());
|
||||||
::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z());
|
::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)
|
for (int i = 0; i < (int)m_planes.size(); ++i)
|
||||||
{
|
{
|
||||||
if (i == m_hover_id)
|
if (i == m_hover_id)
|
||||||
|
@ -1489,6 +1481,8 @@ void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selectio
|
||||||
::glPushMatrix();
|
::glPushMatrix();
|
||||||
::glMultMatrixd(m.data());
|
::glMultMatrixd(m.data());
|
||||||
::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z());
|
::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)
|
for (int i = 0; i < (int)m_planes.size(); ++i)
|
||||||
{
|
{
|
||||||
::glColor3f(1.0f, 1.0f, picking_color_component(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)
|
void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
|
||||||
{
|
{
|
||||||
m_starting_center = Vec3d::Zero();
|
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;
|
m_model_object = model_object;
|
||||||
|
|
||||||
if (object_changed && is_plane_update_necessary())
|
|
||||||
update_planes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoFlatten::update_planes()
|
void GLGizmoFlatten::update_planes()
|
||||||
|
@ -1526,10 +1520,9 @@ void GLGizmoFlatten::update_planes()
|
||||||
vol_ch.transform(vol->get_matrix());
|
vol_ch.transform(vol->get_matrix());
|
||||||
ch.merge(vol_ch);
|
ch.merge(vol_ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
ch = ch.convex_hull_3d();
|
ch = ch.convex_hull_3d();
|
||||||
m_planes.clear();
|
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.
|
// Following constants are used for discarding too small polygons.
|
||||||
const float minimal_area = 5.f; // in square mm (world coordinates)
|
const float minimal_area = 5.f; // in square mm (world coordinates)
|
||||||
|
@ -1586,15 +1579,17 @@ void GLGizmoFlatten::update_planes()
|
||||||
m_planes.pop_back();
|
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:
|
// 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) {
|
for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) {
|
||||||
Pointf3s& polygon = m_planes[polygon_id].vertices;
|
Pointf3s& polygon = m_planes[polygon_id].vertices;
|
||||||
const Vec3d& normal = m_planes[polygon_id].normal;
|
const Vec3d& normal = m_planes[polygon_id].normal;
|
||||||
|
|
||||||
// let's transform the normal accodring to the instance matrix:
|
// transform the normal according 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))));
|
|
||||||
Vec3d normal_transformed = t.get_matrix() * normal;
|
Vec3d normal_transformed = t.get_matrix() * normal;
|
||||||
|
|
||||||
// We are going to rotate about z and y to flatten the plane
|
// 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)
|
// Transform back to 3D (and also back to mesh coordinates)
|
||||||
polygon = transform(polygon, inst_matrix.inverse() * m.inverse());
|
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):
|
// 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_volumes_types.push_back(vol->type());
|
||||||
}
|
}
|
||||||
m_first_instance_scale = m_model_object->instances.front()->get_scaling_factor();
|
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())
|
if (m_state != On || !m_model_object || m_model_object->instances.empty())
|
||||||
return false;
|
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;
|
return true;
|
||||||
|
|
||||||
// We want to recalculate when the scale changes - some planes could (dis)appear.
|
// 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;
|
return true;
|
||||||
|
|
||||||
for (unsigned int i=0; i < m_model_object->volumes.size(); ++i)
|
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("Keep lower part")), m_keep_lower);
|
||||||
m_imgui->checkbox(_(L("Rotate lower part upwards")), m_rotate_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")));
|
const bool cut_clicked = m_imgui->button(_(L("Perform cut")));
|
||||||
|
m_imgui->disabled_end();
|
||||||
|
|
||||||
m_imgui->end();
|
m_imgui->end();
|
||||||
|
|
||||||
if (cut_clicked) {
|
if (cut_clicked && (m_keep_upper || m_keep_lower)) {
|
||||||
perform_cut(selection);
|
perform_cut(selection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -405,8 +405,10 @@ private:
|
||||||
std::vector<Transform3d> m_volumes_matrices;
|
std::vector<Transform3d> m_volumes_matrices;
|
||||||
std::vector<ModelVolume::Type> m_volumes_types;
|
std::vector<ModelVolume::Type> m_volumes_types;
|
||||||
Vec3d m_first_instance_scale;
|
Vec3d m_first_instance_scale;
|
||||||
|
Vec3d m_first_instance_mirror;
|
||||||
|
|
||||||
std::vector<PlaneData> m_planes;
|
std::vector<PlaneData> m_planes;
|
||||||
|
bool m_planes_valid = false;
|
||||||
mutable Vec3d m_starting_center;
|
mutable Vec3d m_starting_center;
|
||||||
const ModelObject* m_model_object = nullptr;
|
const ModelObject* m_model_object = nullptr;
|
||||||
std::vector<const Transform3d*> instances_matrices;
|
std::vector<const Transform3d*> instances_matrices;
|
||||||
|
|
|
@ -468,12 +468,12 @@ float GLToolbar::get_width_horizontal() const
|
||||||
|
|
||||||
float GLToolbar::get_width_vertical() 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
|
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
|
float GLToolbar::get_height_vertical() const
|
||||||
|
@ -483,33 +483,36 @@ float GLToolbar::get_height_vertical() const
|
||||||
|
|
||||||
float GLToolbar::get_main_size() 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)
|
for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i)
|
||||||
{
|
{
|
||||||
if (m_items[i]->is_separator())
|
if (m_items[i]->is_separator())
|
||||||
size += m_layout.separator_size;
|
size += m_layout.separator_size * m_layout.icons_scale;
|
||||||
else
|
else
|
||||||
size += (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale;
|
size += (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_items.size() > 1)
|
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;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent)
|
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 zoom = parent.get_camera_zoom();
|
||||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
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();
|
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);
|
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_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
|
||||||
float scaled_separator_size = m_layout.separator_size * inv_zoom;
|
float scaled_separator_size = m_layout.separator_size * factor;
|
||||||
float scaled_gap_size = m_layout.gap_size * inv_zoom;
|
float scaled_gap_size = m_layout.gap_size * factor;
|
||||||
float scaled_border = m_layout.border * inv_zoom;
|
float scaled_border = m_layout.border * factor;
|
||||||
|
|
||||||
float separator_stride = scaled_separator_size + scaled_gap_size;
|
float separator_stride = scaled_separator_size + scaled_gap_size;
|
||||||
float icon_stride = scaled_icons_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)
|
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 zoom = parent.get_camera_zoom();
|
||||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
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();
|
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);
|
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_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
|
||||||
float scaled_separator_size = m_layout.separator_size * inv_zoom;
|
float scaled_separator_size = m_layout.separator_size * factor;
|
||||||
float scaled_gap_size = m_layout.gap_size * inv_zoom;
|
float scaled_gap_size = m_layout.gap_size * factor;
|
||||||
float scaled_border = m_layout.border * inv_zoom;
|
float scaled_border = m_layout.border * factor;
|
||||||
|
|
||||||
float separator_stride = scaled_separator_size + scaled_gap_size;
|
float separator_stride = scaled_separator_size + scaled_gap_size;
|
||||||
float icon_stride = scaled_icons_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
|
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 zoom = parent.get_camera_zoom();
|
||||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
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();
|
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);
|
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_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
|
||||||
float scaled_separator_size = m_layout.separator_size * inv_zoom;
|
float scaled_separator_size = m_layout.separator_size * factor;
|
||||||
float scaled_gap_size = m_layout.gap_size * inv_zoom;
|
float scaled_gap_size = m_layout.gap_size * factor;
|
||||||
float scaled_border = m_layout.border * inv_zoom;
|
float scaled_border = m_layout.border * factor;
|
||||||
|
|
||||||
float separator_stride = scaled_separator_size + scaled_gap_size;
|
float separator_stride = scaled_separator_size + scaled_gap_size;
|
||||||
float icon_stride = scaled_icons_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
|
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 zoom = parent.get_camera_zoom();
|
||||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
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();
|
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);
|
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_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
|
||||||
float scaled_separator_size = m_layout.separator_size * inv_zoom;
|
float scaled_separator_size = m_layout.separator_size * factor;
|
||||||
float scaled_gap_size = m_layout.gap_size * inv_zoom;
|
float scaled_gap_size = m_layout.gap_size * factor;
|
||||||
float scaled_border = m_layout.border * inv_zoom;
|
float scaled_border = m_layout.border * factor;
|
||||||
|
|
||||||
float separator_stride = scaled_separator_size + scaled_gap_size;
|
float separator_stride = scaled_separator_size + scaled_gap_size;
|
||||||
float icon_stride = scaled_icons_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 zoom = parent.get_camera_zoom();
|
||||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
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_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
|
||||||
float scaled_separator_size = m_layout.separator_size * inv_zoom;
|
float scaled_separator_size = m_layout.separator_size * factor;
|
||||||
float scaled_gap_size = m_layout.gap_size * inv_zoom;
|
float scaled_gap_size = m_layout.gap_size * factor;
|
||||||
float scaled_border = m_layout.border * inv_zoom;
|
float scaled_border = m_layout.border * factor;
|
||||||
float scaled_width = get_width() * inv_zoom;
|
float scaled_width = get_width() * inv_zoom;
|
||||||
float scaled_height = get_height() * 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 zoom = parent.get_camera_zoom();
|
||||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
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_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * factor;
|
||||||
float scaled_separator_size = m_layout.separator_size * inv_zoom;
|
float scaled_separator_size = m_layout.separator_size * factor;
|
||||||
float scaled_gap_size = m_layout.gap_size * inv_zoom;
|
float scaled_gap_size = m_layout.gap_size * factor;
|
||||||
float scaled_border = m_layout.border * inv_zoom;
|
float scaled_border = m_layout.border * factor;
|
||||||
float scaled_width = get_width() * inv_zoom;
|
float scaled_width = get_width() * inv_zoom;
|
||||||
float scaled_height = get_height() * inv_zoom;
|
float scaled_height = get_height() * inv_zoom;
|
||||||
|
|
||||||
|
|
|
@ -245,8 +245,6 @@ void show_info(wxWindow* parent, const wxString& message, const wxString& title)
|
||||||
|
|
||||||
void warning_catcher(wxWindow* parent, const wxString& message)
|
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);
|
wxMessageDialog msg(parent, message, _(L("Warning")), wxOK | wxICON_WARNING);
|
||||||
msg.ShowModal();
|
msg.ShowModal();
|
||||||
}
|
}
|
||||||
|
@ -348,50 +346,6 @@ bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height
|
||||||
return true;
|
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()
|
void about()
|
||||||
{
|
{
|
||||||
AboutDialog dlg;
|
AboutDialog dlg;
|
||||||
|
|
|
@ -70,11 +70,6 @@ boost::filesystem::path into_path(const wxString &str);
|
||||||
// Returns the dimensions of the screen on which the main frame is displayed
|
// Returns the dimensions of the screen on which the main frame is displayed
|
||||||
bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height);
|
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
|
// Display an About dialog
|
||||||
extern void about();
|
extern void about();
|
||||||
// Ask the destop to open the datadir using the default file explorer.
|
// Ask the destop to open the datadir using the default file explorer.
|
||||||
|
|
|
@ -143,56 +143,33 @@ bool GUI_App::OnInit()
|
||||||
init_fonts();
|
init_fonts();
|
||||||
|
|
||||||
// application frame
|
// application frame
|
||||||
std::cerr << "Creating main frame..." << std::endl;
|
|
||||||
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
||||||
wxImage::AddHandler(new wxPNGHandler());
|
wxImage::AddHandler(new wxPNGHandler());
|
||||||
mainframe = new MainFrame();
|
mainframe = new MainFrame();
|
||||||
sidebar().obj_list()->init_objects(); // propagate model objects to object list
|
sidebar().obj_list()->init_objects(); // propagate model objects to object list
|
||||||
// update_mode(); // do that later
|
// update_mode(); // !!! do that later
|
||||||
SetTopWindow(mainframe);
|
SetTopWindow(mainframe);
|
||||||
|
|
||||||
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
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)
|
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())
|
if (app_config->dirty())
|
||||||
app_config->save();
|
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)
|
if (this->plater() != nullptr)
|
||||||
this->obj_manipul()->update_if_dirty();
|
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, ...)
|
// 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->slic3r_update_notify();
|
||||||
}
|
}
|
||||||
preset_updater->sync(preset_bundle);
|
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()
|
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();
|
mainframe = new MainFrame();
|
||||||
sidebar().obj_list()->init_objects(); // propagate model objects to object list
|
sidebar().obj_list()->init_objects(); // propagate model objects to object list
|
||||||
// update_mode(); // do that later
|
|
||||||
if (topwindow) {
|
if (topwindow) {
|
||||||
SetTopWindow(mainframe);
|
SetTopWindow(mainframe);
|
||||||
topwindow->Destroy();
|
topwindow->Destroy();
|
||||||
|
@ -304,17 +285,7 @@ void GUI_App::recreate_GUI()
|
||||||
|
|
||||||
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
|
||||||
|
|
||||||
CallAfter([this]() {
|
load_current_presets();
|
||||||
// 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
|
|
||||||
});
|
|
||||||
|
|
||||||
mainframe->Show(true);
|
mainframe->Show(true);
|
||||||
|
|
||||||
|
@ -370,6 +341,33 @@ void GUI_App::update_ui_from_settings()
|
||||||
mainframe->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)
|
void GUI_App::load_project(wxWindow *parent, wxString& input_file)
|
||||||
{
|
{
|
||||||
input_file.Clear();
|
input_file.Clear();
|
||||||
|
@ -394,55 +392,6 @@ void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files)
|
||||||
dialog.GetPaths(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
|
// select language from the list of installed languages
|
||||||
bool GUI_App::select_language( wxArrayString & names,
|
bool GUI_App::select_language( wxArrayString & names,
|
||||||
wxArrayLong & identifiers)
|
wxArrayLong & identifiers)
|
||||||
|
@ -582,10 +531,12 @@ void GUI_App::update_mode()
|
||||||
|
|
||||||
const ConfigOptionMode mode = wxGetApp().get_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().set_mode_value(mode);
|
||||||
// sidebar().show_buttons(mode == comExpert);
|
// sidebar().show_buttons(mode == comExpert);
|
||||||
|
obj_list()->unselect_objects();
|
||||||
obj_list()->update_selections();
|
obj_list()->update_selections();
|
||||||
|
obj_list()->update_object_menu();
|
||||||
|
|
||||||
sidebar().update_mode_sizer(mode);
|
sidebar().update_mode_sizer(mode);
|
||||||
|
|
||||||
|
@ -602,15 +553,15 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
||||||
auto local_menu = new wxMenu();
|
auto local_menu = new wxMenu();
|
||||||
wxWindowID config_id_base = wxWindow::NewControlId((int)ConfigMenuCnt);
|
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);
|
const auto config_wizard_tooltip = wxString::Format(_(L("Run %s")), config_wizard_name);
|
||||||
// Cmd+, is standard on OS X - what about other operating systems?
|
// 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 + 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 + 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 + 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->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates")));
|
||||||
local_menu->AppendSeparator();
|
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();
|
local_menu->AppendSeparator();
|
||||||
auto mode_menu = new wxMenu();
|
auto mode_menu = new wxMenu();
|
||||||
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _(L("Simple")), _(L("Simple View Mode")));
|
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);
|
mode_menu->Check(config_id_base + ConfigMenuModeSimple + get_mode(), true);
|
||||||
local_menu->AppendSubMenu(mode_menu, _(L("Mode")), _(L("Slic3r View Mode")));
|
local_menu->AppendSubMenu(mode_menu, _(L("Mode")), _(L("Slic3r View Mode")));
|
||||||
local_menu->AppendSeparator();
|
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->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
|
// 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")));
|
// 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;
|
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
|
// Update UI / Tabs to reflect changes in the currently loaded presets
|
||||||
void GUI_App::load_current_presets()
|
void GUI_App::load_current_presets()
|
||||||
{
|
{
|
||||||
|
@ -789,6 +733,48 @@ wxNotebook* GUI_App::tab_panel() const
|
||||||
return mainframe->m_tabpanel;
|
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
|
// static method accepting a wxWindow object as first parameter
|
||||||
// void warning_catcher{
|
// void warning_catcher{
|
||||||
// my($self, $message_dialog) = @_;
|
// my($self, $message_dialog) = @_;
|
||||||
|
|
|
@ -73,11 +73,6 @@ class GUI_App : public wxApp
|
||||||
{
|
{
|
||||||
bool app_conf_exists{ false };
|
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_modified;
|
||||||
wxColour m_color_label_sys;
|
wxColour m_color_label_sys;
|
||||||
wxColour m_color_label_default;
|
wxColour m_color_label_default;
|
||||||
|
@ -121,12 +116,9 @@ public:
|
||||||
// wxMessageDialog* message_dialog,
|
// wxMessageDialog* message_dialog,
|
||||||
const std::string& err);
|
const std::string& err);
|
||||||
// void notify(/*message*/);
|
// 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 persist_window_geometry(wxTopLevelWindow *window);
|
||||||
void window_pos_restore(wxTopLevelWindow* window, const std::string &name);
|
void update_ui_from_settings();
|
||||||
void window_pos_sanitize(wxTopLevelWindow* window);
|
|
||||||
|
|
||||||
bool select_language(wxArrayString & names, wxArrayLong & identifiers);
|
bool select_language(wxArrayString & names, wxArrayLong & identifiers);
|
||||||
bool load_language();
|
bool load_language();
|
||||||
|
@ -141,7 +133,6 @@ public:
|
||||||
void add_config_menu(wxMenuBar *menu);
|
void add_config_menu(wxMenuBar *menu);
|
||||||
bool check_unsaved_changes();
|
bool check_unsaved_changes();
|
||||||
bool checked_tab(Tab* tab);
|
bool checked_tab(Tab* tab);
|
||||||
void delete_tab_from_list(Tab* tab);
|
|
||||||
void load_current_presets();
|
void load_current_presets();
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
@ -172,6 +163,10 @@ public:
|
||||||
|
|
||||||
PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); }
|
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)
|
DECLARE_APP(GUI_App)
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue