mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-15 09:32:09 +00:00
Includes (but is not limited to) the following commits: SPE-2218 - libvgcode - Fixed color of wipe moves for speed range view SPE-2218 - libvgcode - Fixed detection of fan speed range SPE-2218 - libvgcode - Fixed detection of temperature range SPE-2218 - libvgcode - Fixed colors for Actual volumetric flow rate view SPE-2214 - Fixed detection of toolpaths bounding box in GCodeViewer SPE-2206 - Modified LibBGCode.cmake to get latest version of libbgcode which fixed parsing of gcode lines G4 libvgcode - Fixed potential out of bound access in ViewerImpl::update_view_full_range() Tech ENABLE_GL_CORE_PROFILE set as default Tech ENABLE_OPENGL_ES replaced by build option SLIC3R_OPENGL_ES libvgcode - Precompiler definition of ENABLE_OPENGL_ES moved into CMakeLists.txt Added missing include libvgcode - Textures setup modified to work when building using emscripten libvgcode - small optimization libvgcode - fixed OpenGLWrapper::unload_opengl() libvgcode - CMakeLists.txt modified to work with emscripten libvgcode - Replace 'glVertexAttribIPointer()' with 'glVertexAttribPointer()' in SegmentTemplate::init() for OpenGL ES libvgcode - Replace 'xor' with '^' Bitset.hpp libvgcode - Newer glad library for OpenGL 4.6 and OpenGL ES 3.0 libvgcode - Alternate fix in method ViewerImpl::update_heights_widths() for OpenGL ES libvgcode - Fixes in glAssertRecentCallImpl() libvgcode - Fixes in method ViewerImpl::update_heights_widths() for OpenGL ES Fixed ES shaders so they work with OpenGL ES 3.0 libvgcode - Use multiple plain textures in place of texture buffers for OpenGL ES libvgcode - Use plain textures in place of texture buffers for OpenGL ES (partial implementation using one texture per buffer) libvgcode - refactoring of class OpenGLWrapper libvgcode - small refactoring in shaders libvgcode - replacement of glMapBuffer() call for OpenGL ES Fixed warning libvgcode - Changes into CMakeLists.txt Fixed debug export of gcode data to be configuration indipendent Disabled tech ENABLE_NEW_GCODE_VIEWER_DEBUG Removed obsolete tech ENABLE_GCODE_VIEWER_DATA_CHECKING Code cleanup and techs removal - completed Code cleanup and techs removal - step 1 SPE-1872: Implemented G2/G3 lines discretization for gcfMarlinFirmware firmware flavour SPE-1872: Corrections into GCodeProcessor::process_G2_G3() to match firmware code SPE-1872: Actual speed profile - Further enhancements of imgui debug window SPE-1872: Actual speed profile - Rework in its calculation + enhanced imgui debug window SPE-1872: New imgui widget to show actual speed profile SPE-1872: Fixed actual speed for seam moves and at extrusion/travel/wipe start SPE-1872: Fixed rendering of wipe moves when actual speed view is selected SPE-1872: Actual speed profile extended to travel and wipe moves SPE-1872: Fixes in function recalculate_trapezoids() and method GCodeProcessor::TimeMachine::calculate_time() to smooth actual speed profile SPE-1872: Added debug graphic to show move actual speed profile SPE-1872: libvgcode library: replace volumetric flow rate data with mm3_per_mm to reduce memory usage SPE-1872: Added visualization of actual volumetric flow rate SPE-1872: Fixes in calculating actual speed SPE-1872: Added visualization of actual speed in gcode preview SPE-2124: Added command line option 'opengl-aa' to allow the user to turn on the automatic selection of max number of supported samples for OpenGL antialising #12117: Reduced moire patterns by using the highest number of samples available for multisampling New gcode visualization integration - Partially enabled imgui debug window New gcode visualization integration - Fixed center of gravity calculation and rendering New gcode visualization library - Interface for estimated times New gcode visualization library - Tool marker rendering New gcode visualization library - Axes aligned bounding boxes calculation Removed obsolete debug code New gcode visualization library - Added statistic of used memory New gcode visualization library - Separation of tool colors and color changes colors to simplify client code Added missing include New gcode visualization library - Added print color changes detection New gcode visualization library - Modified OpenGL ES context detection New gcode visualization library - Another makefile cleanup New gcode visualization library - Makefiles cleanup New gcode visualization library - Added suppression of error 'This function or variable may be unsafe' in VS2022 New gcode visualization library - Compatibility with OpenGL ES New gcode visualization library - Interface cleanup and documentation New gcode visualization library - Extended interface to give access to estimated times New gcode visualization integration - Toggling of top layer only view state New gcode visualization integration - Removed imperial units from tool position data Small refactoring New gcode visualization library - Custom values for travel and wipe moves radius New gcode visualization library - Allow customization of range colors New gcode visualization library - Partial update of interface comments/documentation New gcode visualization integration - Follow-up of 35ee55e29bb231fd01a2eb71ae293832a37ca65d - Better fix for toolpaths visible range when toggling options' visibility New gcode visualization integration - Fixed toolpaths reset New gcode visualization library - Fixed method set_option_color() New gcode visualization library - Fixed method ViewerImpl::set_extrusion_role_color() New gcode visualization library - Added methods to release gpu resources on demand. New gcode visualization library - Travel and wipe moves as options New gcode visualization integration - Fixed toolpaths visible range when toggling options' visibility New gcode visualization integration - Fixed management of gcode preview view type New gcode visualization - Fixed wrong include New gcode visualization - Added missing headers New gcode visualization - Refactoring + added missing headers New gcode visualization - New code set as standalone library + embed glad library to load OpenGL functions New gcode visualization - Fixed errors and warnings when building the new code as a standalone library New gcode visualization integration - Fixed layers ordering in pre-gcode preview New gcode visualization integration - Fixed objects' tool colors in pre-gcode preview Code cleanup New gcode visualization integration - Tool position properties data window New gcode visualization integration - Fixed in export toolpaths to obj New gcode visualization - Inlining in source code Refactoring New gcode visualization integration - Export toolpaths to obj Some refactoring and warning fix New gcode visualization integration - Customizable travel moves colors New gcode visualization integration - Customizable options colors New gcode visualization integration - Customizable extrusion roles colors New gcode visualization integration - Fixed pre-gcode preview layers times New gcode visualization integration - Modify pre-gcode preview to use the new toolpaths renderer, objects WIP New gcode visualization - Modify pre-gcode preview to use the new toolpaths renderer, WIP (brim/skirt/wipe tower) New gcode visualization integration - Do not reset visible range when toggling options/roles visibility New gcode visualization - Fixed color of first vertex of top layer (when top layer only option is enabled) New gcode visualization - Customizable travels and wipes segment radius New gcode visualization integration - Removed tech ENABLE_GCODE_VIEWER_STATISTICS New gcode visualization integration - Added check of OpenGL version New gcode visualization integration - Removed GCodeProcessorResult::spiral_vase_layers Another bunch of warnings fixes Fixed warnings New gcode visualization integration - Removal of old visualization Fixed includes New gcode visualization integration - File structure of new code separated in api + src New gcode visualization integration - View ranges management moved to new visualizer New gcode visualization integration - Fixed top layer only visualization for MMU printers New gcode visualization integration - Removed dependency on imgui from new visualizer Some refactoring New gcode visualization integration - Removed dependency on Slic3r::GCodeProcessorResult from new visualizer New gcode visualization integration - Moves' data conversion moved to client side New gcode visualization: layers times refactoring A bunch of fixes for the new gcode visualization New gcode visualization: render in gray color layers containing pause print or custom gcode options when in Color Print view New gcode visualization integration - Tool colors New gcode visualization integration - Layers times New gcode visualization integration - Travels and Extrusion roles times Fixed detection of start/end of contiguous extrusion paths New gcode visualization integration - Extrusion roles New gcode visualization integration - Colors New gcode visualization integration - Tool position Center of gravity and tool marker may both be rendered with fixed screen size and a scaling factor Fixed rendering of options in new gcode visualization Tool marker NOT rendered by the new visualization code Center of gravity marker NOT rendered by the new visualization code Fixed toolpaths_cog shaders Tool position window for new gcode visualization Top layer only coloring for neww gcode visualization Refactoring in preview's new visualization Hidden imgui debug dialog for new visualization in preview Synchronization of moves between old and new visualization Fixed missing gcode window in new visualization Rendering of debug imgui dialog moved from class libvgcode::Viewer to class libvgcode::Toolpaths + warnings fixing Some functionality moved from class libvgcode::Viewer to class libvgcode::Toolpaths Some refactoring and cleanup Refatoring of PathVertex and Toolpaths::load() SPE-1982: Tech ENABLE_NEW_GCODE_VIEWER - 1st installment of new toolpaths rendering code (WIP)
409 lines
20 KiB
C++
409 lines
20 KiB
C++
#include "BuildVolume.hpp"
|
|
#include "ClipperUtils.hpp"
|
|
#include "TriangleMesh.hpp"
|
|
#include "Geometry/ConvexHull.hpp"
|
|
#include "GCode/GCodeProcessor.hpp"
|
|
#include "Point.hpp"
|
|
|
|
#include <boost/log/trivial.hpp>
|
|
|
|
namespace Slic3r {
|
|
|
|
BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double printable_height) : m_bed_shape(printable_area), m_max_print_height(printable_height)
|
|
{
|
|
assert(printable_height >= 0);
|
|
|
|
m_polygon = Polygon::new_scale(printable_area);
|
|
assert(m_polygon.is_counter_clockwise());
|
|
|
|
// Calcuate various metrics of the input polygon.
|
|
m_convex_hull = Geometry::convex_hull(m_polygon.points);
|
|
m_bbox = get_extents(m_convex_hull);
|
|
m_area = m_polygon.area();
|
|
|
|
BoundingBoxf bboxf = get_extents(printable_area);
|
|
m_bboxf = BoundingBoxf3{ to_3d(bboxf.min, 0.), to_3d(bboxf.max, printable_height) };
|
|
|
|
if (printable_area.size() >= 4 && std::abs((m_area - double(m_bbox.size().x()) * double(m_bbox.size().y()))) < sqr(SCALED_EPSILON)) {
|
|
// Square print bed, use the bounding box for collision detection.
|
|
m_type = BuildVolume_Type::Rectangle;
|
|
m_circle.center = 0.5 * (m_bbox.min.cast<double>() + m_bbox.max.cast<double>());
|
|
m_circle.radius = 0.5 * m_bbox.size().cast<double>().norm();
|
|
} else if (printable_area.size() > 3) {
|
|
// Circle was discretized, formatted into text with limited accuracy, thus the circle was deformed.
|
|
// RANSAC is slightly more accurate than the iterative Taubin / Newton method with such an input.
|
|
// m_circle = Geometry::circle_taubin_newton(printable_area);
|
|
m_circle = Geometry::circle_ransac(printable_area);
|
|
bool is_circle = true;
|
|
#ifndef NDEBUG
|
|
// Measuring maximum absolute error of interpolating an input polygon with circle.
|
|
double max_error = 0;
|
|
#endif // NDEBUG
|
|
Vec2d prev = printable_area.back();
|
|
for (const Vec2d &p : printable_area) {
|
|
#ifndef NDEBUG
|
|
max_error = std::max(max_error, std::abs((p - m_circle.center).norm() - m_circle.radius));
|
|
#endif // NDEBUG
|
|
if (// Polygon vertices must lie very close the circle.
|
|
std::abs((p - m_circle.center).norm() - m_circle.radius) > 0.005 ||
|
|
// Midpoints of polygon edges must not undercat more than 3mm. This corresponds to 72 edges per circle generated by BedShapePanel::update_shape().
|
|
m_circle.radius - (0.5 * (prev + p) - m_circle.center).norm() > 3.) {
|
|
is_circle = false;
|
|
break;
|
|
}
|
|
prev = p;
|
|
}
|
|
if (is_circle) {
|
|
m_type = BuildVolume_Type::Circle;
|
|
m_circle.center = scaled<double>(m_circle.center);
|
|
m_circle.radius = scaled<double>(m_circle.radius);
|
|
}
|
|
}
|
|
|
|
if (printable_area.size() >= 3 && m_type == BuildVolume_Type::Invalid) {
|
|
// Circle check is not used for Convex / Custom shapes, fill it with something reasonable.
|
|
m_circle = Geometry::smallest_enclosing_circle_welzl(m_convex_hull.points);
|
|
m_type = (m_convex_hull.area() - m_area) < sqr(SCALED_EPSILON) ? BuildVolume_Type::Convex : BuildVolume_Type::Custom;
|
|
// Initialize the top / bottom decomposition for inside convex polygon check. Do it with two different epsilons applied.
|
|
auto convex_decomposition = [](const Polygon &in, double epsilon) {
|
|
Polygon src = expand(in, float(epsilon)).front();
|
|
std::vector<Vec2d> pts;
|
|
pts.reserve(src.size());
|
|
for (const Point &pt : src.points)
|
|
pts.emplace_back(unscaled<double>(pt.cast<double>().eval()));
|
|
return Geometry::decompose_convex_polygon_top_bottom(pts);
|
|
};
|
|
m_top_bottom_convex_hull_decomposition_scene = convex_decomposition(m_convex_hull, SceneEpsilon);
|
|
m_top_bottom_convex_hull_decomposition_bed = convex_decomposition(m_convex_hull, BedEpsilon);
|
|
}
|
|
|
|
BOOST_LOG_TRIVIAL(debug) << "BuildVolume printable_area clasified as: " << this->type_name();
|
|
}
|
|
|
|
#if 0
|
|
// Tests intersections of projected triangles, not just their vertices against a bounding box.
|
|
// This test also correctly evaluates collision of a non-convex object with the bounding box.
|
|
// Not used, slower than simple bounding box collision check and nobody complained about the inaccuracy of the simple test.
|
|
static inline BuildVolume::ObjectState rectangle_test(const indexed_triangle_set &its, const Transform3f &trafo, const Vec2f min, const Vec2f max, const float max_z)
|
|
{
|
|
bool inside = false;
|
|
bool outside = false;
|
|
|
|
auto sign = [](const Vec3f& pt) -> char { return pt.z() > 0 ? 1 : pt.z() < 0 ? -1 : 0; };
|
|
|
|
// Returns true if both inside and outside are set, thus early exit.
|
|
auto test_intersection = [&inside, &outside, min, max, max_z](const Vec3f& p1, const Vec3f& p2, const Vec3f& p3) -> bool {
|
|
// First test whether the triangle is completely inside or outside the bounding box.
|
|
Vec3f pmin = p1.cwiseMin(p2).cwiseMin(p3);
|
|
Vec3f pmax = p1.cwiseMax(p2).cwiseMax(p3);
|
|
bool tri_inside = false;
|
|
bool tri_outside = false;
|
|
if (pmax.x() < min.x() || pmin.x() > max.x() || pmax.y() < min.y() || pmin.y() > max.y()) {
|
|
// Separated by one of the rectangle sides.
|
|
tri_outside = true;
|
|
} else if (pmin.x() >= min.x() && pmax.x() <= max.x() && pmin.y() >= min.y() && pmax.y() <= max.y()) {
|
|
// Fully inside the rectangle.
|
|
tri_inside = true;
|
|
} else {
|
|
// Bounding boxes overlap. Test triangle sides against the bbox corners.
|
|
Vec2f v1(- p2.y() + p1.y(), p2.x() - p1.x());
|
|
Vec2f v2(- p2.y() + p2.y(), p3.x() - p2.x());
|
|
Vec2f v3(- p1.y() + p3.y(), p1.x() - p3.x());
|
|
bool ccw = cross2(v1, v2) > 0;
|
|
for (const Vec2f &p : { Vec2f{ min.x(), min.y() }, Vec2f{ min.x(), max.y() }, Vec2f{ max.x(), min.y() }, Vec2f{ max.x(), max.y() } }) {
|
|
auto dot = v1.dot(p);
|
|
if (ccw ? dot >= 0 : dot <= 0)
|
|
tri_inside = true;
|
|
else
|
|
tri_outside = true;
|
|
}
|
|
}
|
|
inside |= tri_inside;
|
|
outside |= tri_outside;
|
|
return inside && outside;
|
|
};
|
|
|
|
// Edge crosses the z plane. Calculate intersection point with the plane.
|
|
auto clip_edge = [](const Vec3f &p1, const Vec3f &p2) -> Vec3f {
|
|
const float t = (world_min_z - p1.z()) / (p2.z() - p1.z());
|
|
return { p1.x() + (p2.x() - p1.x()) * t, p1.y() + (p2.y() - p1.y()) * t, world_min_z };
|
|
};
|
|
|
|
// Clip at (p1, p2), p3 must be on the clipping plane.
|
|
// Returns true if both inside and outside are set, thus early exit.
|
|
auto clip_and_test1 = [&test_intersection, &clip_edge](const Vec3f &p1, const Vec3f &p2, const Vec3f &p3, bool p1above) -> bool {
|
|
Vec3f pa = clip_edge(p1, p2);
|
|
return p1above ? test_intersection(p1, pa, p3) : test_intersection(pa, p2, p3);
|
|
};
|
|
|
|
// Clip at (p1, p2) and (p2, p3).
|
|
// Returns true if both inside and outside are set, thus early exit.
|
|
auto clip_and_test2 = [&test_intersection, &clip_edge](const Vec3f &p1, const Vec3f &p2, const Vec3f &p3, bool p2above) -> bool {
|
|
Vec3f pa = clip_edge(p1, p2);
|
|
Vec3f pb = clip_edge(p2, p3);
|
|
return p2above ? test_intersection(pa, p2, pb) : test_intersection(p1, pa, p3) || test_intersection(p3, pa, pb);
|
|
};
|
|
|
|
for (const stl_triangle_vertex_indices &tri : its.indices) {
|
|
const Vec3f pts[3] = { trafo * its.vertices[tri(0)], trafo * its.vertices[tri(1)], trafo * its.vertices[tri(2)] };
|
|
char signs[3] = { sign(pts[0]), sign(pts[1]), sign(pts[2]) };
|
|
bool clips[3] = { signs[0] * signs[1] == -1, signs[1] * signs[2] == -1, signs[2] * signs[0] == -1 };
|
|
if (clips[0]) {
|
|
if (clips[1]) {
|
|
// Clipping at (pt0, pt1) and (pt1, pt2).
|
|
if (clip_and_test2(pts[0], pts[1], pts[2], signs[1] > 0))
|
|
break;
|
|
} else if (clips[2]) {
|
|
// Clipping at (pt0, pt1) and (pt0, pt2).
|
|
if (clip_and_test2(pts[2], pts[0], pts[1], signs[0] > 0))
|
|
break;
|
|
} else {
|
|
// Clipping at (pt0, pt1), pt2 must be on the clipping plane.
|
|
if (clip_and_test1(pts[0], pts[1], pts[2], signs[0] > 0))
|
|
break;
|
|
}
|
|
} else if (clips[1]) {
|
|
if (clips[2]) {
|
|
// Clipping at (pt1, pt2) and (pt0, pt2).
|
|
if (clip_and_test2(pts[0], pts[1], pts[2], signs[1] > 0))
|
|
break;
|
|
} else {
|
|
// Clipping at (pt1, pt2), pt0 must be on the clipping plane.
|
|
if (clip_and_test1(pts[1], pts[2], pts[0], signs[1] > 0))
|
|
break;
|
|
}
|
|
} else if (clips[2]) {
|
|
// Clipping at (pt0, pt2), pt1 must be on the clipping plane.
|
|
if (clip_and_test1(pts[2], pts[0], pts[1], signs[2] > 0))
|
|
break;
|
|
} else if (signs[0] >= 0 && signs[1] >= 0 && signs[2] >= 0) {
|
|
// The triangle is above or on the clipping plane.
|
|
if (test_intersection(pts[0], pts[1], pts[2]))
|
|
break;
|
|
}
|
|
}
|
|
return inside ? (outside ? BuildVolume::ObjectState::Colliding : BuildVolume::ObjectState::Inside) : BuildVolume::ObjectState::Outside;
|
|
}
|
|
#endif
|
|
|
|
// Trim the input transformed triangle mesh with print bed and test the remaining vertices with is_inside callback.
|
|
// Return inside / colliding / outside state.
|
|
template<typename InsideFn>
|
|
BuildVolume::ObjectState object_state_templ(const indexed_triangle_set &its, const Transform3f &trafo, bool may_be_below_bed, bool convex, InsideFn is_inside)
|
|
{
|
|
size_t num_inside = 0;
|
|
size_t num_above = 0;
|
|
bool inside = false;
|
|
bool outside = false;
|
|
static constexpr const auto world_min_z = float(-BuildVolume::SceneEpsilon);
|
|
|
|
if (may_be_below_bed)
|
|
{
|
|
// Slower test, needs to clip the object edges with the print bed plane.
|
|
// 1) Allocate transformed vertices with their position with respect to print bed surface.
|
|
std::vector<char> sides;
|
|
sides.reserve(its.vertices.size());
|
|
|
|
const auto sign = [](const stl_vertex& pt) { return pt.z() > world_min_z ? 1 : pt.z() < world_min_z ? -1 : 0; };
|
|
|
|
bool below_outside = false;
|
|
|
|
for (const stl_vertex &v : its.vertices) {
|
|
stl_vertex pt = trafo * v;
|
|
const int s = sign(pt);
|
|
sides.emplace_back(s);
|
|
if (s >= 0) {
|
|
// Vertex above or on print bed surface. Test whether it is inside the build volume.
|
|
++ num_above;
|
|
if (is_inside(pt))
|
|
++ num_inside;
|
|
} else if (convex && !below_outside) {
|
|
pt.z() = 0;
|
|
if (!is_inside(pt))
|
|
below_outside = true;
|
|
}
|
|
}
|
|
|
|
if (num_above == 0)
|
|
// Special case, the object is completely below the print bed, thus it is outside,
|
|
// however we want to allow an object to be still printable if some of its parts are completely below the print bed.
|
|
return BuildVolume::ObjectState::Below;
|
|
|
|
// 2) Calculate intersections of triangle edges with the build surface.
|
|
inside = num_inside > 0;
|
|
outside = num_inside < num_above;
|
|
// Orca: for convex shape, if everything inside then don't bother check intersection
|
|
if (num_above < its.vertices.size() && !(inside && outside) && (!(inside && !below_outside) || !convex)) {
|
|
// Not completely above the build surface and status may still change by testing edges intersecting the build platform.
|
|
for (const stl_triangle_vertex_indices &tri : its.indices) {
|
|
const int s[3] = { sides[tri(0)], sides[tri(1)], sides[tri(2)] };
|
|
if (std::min(s[0], std::min(s[1], s[2])) < 0 && std::max(s[0], std::max(s[1], s[2])) > 0) {
|
|
// Some edge of this triangle intersects the build platform. Calculate the intersection.
|
|
int iprev = 2;
|
|
for (int iedge = 0; iedge < 3; ++ iedge) {
|
|
if (s[iprev] * s[iedge] == -1) {
|
|
// edge intersects the build surface. Calculate intersection point.
|
|
const stl_vertex p1 = trafo * its.vertices[tri(iprev)];
|
|
const stl_vertex p2 = trafo * its.vertices[tri(iedge)];
|
|
assert(sign(p1) == s[iprev]);
|
|
assert(sign(p2) == s[iedge]);
|
|
assert(p1.z() * p2.z() < 0);
|
|
// Edge crosses the z plane. Calculate intersection point with the plane.
|
|
const float t = (world_min_z - p1.z()) / (p2.z() - p1.z());
|
|
(is_inside(Vec3f(p1.x() + (p2.x() - p1.x()) * t, p1.y() + (p2.y() - p1.y()) * t, world_min_z)) ? inside : outside) = true;
|
|
}
|
|
iprev = iedge;
|
|
}
|
|
if (inside && outside)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Much simpler and faster code, not clipping the object with the print bed.
|
|
assert(! may_be_below_bed);
|
|
num_above = its.vertices.size();
|
|
for (const stl_vertex &v : its.vertices) {
|
|
const stl_vertex pt = trafo * v;
|
|
assert(pt.z() >= world_min_z);
|
|
if (is_inside(pt))
|
|
++ num_inside;
|
|
}
|
|
inside = num_inside > 0;
|
|
outside = num_inside < num_above;
|
|
}
|
|
|
|
return inside ? (outside ? BuildVolume::ObjectState::Colliding : BuildVolume::ObjectState::Inside) : BuildVolume::ObjectState::Outside;
|
|
}
|
|
|
|
BuildVolume::ObjectState BuildVolume::object_state(const indexed_triangle_set& its, const Transform3f& trafo, bool may_be_below_bed, bool ignore_bottom) const
|
|
{
|
|
switch (m_type) {
|
|
case BuildVolume_Type::Rectangle:
|
|
{
|
|
BoundingBox3Base<Vec3d> build_volume = this->bounding_volume().inflated(SceneEpsilon);
|
|
if (m_max_print_height == 0.0)
|
|
build_volume.max.z() = std::numeric_limits<double>::max();
|
|
if (ignore_bottom)
|
|
build_volume.min.z() = -std::numeric_limits<double>::max();
|
|
BoundingBox3Base<Vec3f> build_volumef(build_volume.min.cast<float>(), build_volume.max.cast<float>());
|
|
// The following test correctly interprets intersection of a non-convex object with a rectangular build volume.
|
|
//return rectangle_test(its, trafo, to_2d(build_volume.min), to_2d(build_volume.max), build_volume.max.z());
|
|
//FIXME This test does NOT correctly interprets intersection of a non-convex object with a rectangular build volume.
|
|
return object_state_templ(its, trafo, may_be_below_bed, true, [build_volumef](const Vec3f &pt) { return build_volumef.contains(pt); });
|
|
}
|
|
case BuildVolume_Type::Circle:
|
|
{
|
|
Geometry::Circlef circle { unscaled<float>(m_circle.center), unscaled<float>(m_circle.radius + SceneEpsilon) };
|
|
return m_max_print_height == 0.0 ?
|
|
object_state_templ(its, trafo, may_be_below_bed, true, [circle](const Vec3f& pt) { return circle.contains(to_2d(pt)); }) :
|
|
object_state_templ(its, trafo, may_be_below_bed, true, [circle, z = m_max_print_height + SceneEpsilon](const Vec3f &pt) { return pt.z() < z && circle.contains(to_2d(pt)); });
|
|
}
|
|
case BuildVolume_Type::Convex:
|
|
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
|
|
case BuildVolume_Type::Custom:
|
|
return m_max_print_height == 0.0 ?
|
|
object_state_templ(its, trafo, may_be_below_bed, m_type == BuildVolume_Type::Convex, [this](const Vec3f &pt) { return Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_scene, to_2d(pt).cast<double>()); }) :
|
|
object_state_templ(its, trafo, may_be_below_bed, m_type == BuildVolume_Type::Convex, [this, z = m_max_print_height + SceneEpsilon](const Vec3f &pt) { return pt.z() < z && Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_scene, to_2d(pt).cast<double>()); });
|
|
case BuildVolume_Type::Invalid:
|
|
default:
|
|
return ObjectState::Inside;
|
|
}
|
|
}
|
|
|
|
BuildVolume::ObjectState BuildVolume::volume_state_bbox(const BoundingBoxf3& volume_bbox, bool ignore_bottom) const
|
|
{
|
|
assert(m_type == BuildVolume_Type::Rectangle);
|
|
BoundingBox3Base<Vec3d> build_volume = this->bounding_volume().inflated(SceneEpsilon);
|
|
if (m_max_print_height == 0.0)
|
|
build_volume.max.z() = std::numeric_limits<double>::max();
|
|
if (ignore_bottom)
|
|
build_volume.min.z() = -std::numeric_limits<double>::max();
|
|
return build_volume.max.z() <= - SceneEpsilon ? ObjectState::Below :
|
|
build_volume.contains(volume_bbox) ? ObjectState::Inside :
|
|
build_volume.intersects(volume_bbox) ? ObjectState::Colliding : ObjectState::Outside;
|
|
}
|
|
|
|
bool BuildVolume::all_paths_inside(const GCodeProcessorResult& paths, const BoundingBoxf3& paths_bbox, bool ignore_bottom) const
|
|
{
|
|
auto move_valid = [](const GCodeProcessorResult::MoveVertex &move) {
|
|
return move.type == EMoveType::Extrude && move.extrusion_role != erCustom && move.width != 0.f && move.height != 0.f;
|
|
};
|
|
static constexpr const double epsilon = BedEpsilon;
|
|
|
|
switch (m_type) {
|
|
case BuildVolume_Type::Rectangle:
|
|
{
|
|
BoundingBox3Base<Vec3d> build_volume = this->bounding_volume().inflated(epsilon);
|
|
if (m_max_print_height == 0.0)
|
|
build_volume.max.z() = std::numeric_limits<double>::max();
|
|
if (ignore_bottom)
|
|
build_volume.min.z() = -std::numeric_limits<double>::max();
|
|
return build_volume.contains(paths_bbox);
|
|
}
|
|
case BuildVolume_Type::Circle:
|
|
{
|
|
const Vec2f c = unscaled<float>(m_circle.center);
|
|
const float r = unscaled<double>(m_circle.radius) + epsilon;
|
|
const float r2 = sqr(r);
|
|
return m_max_print_height == 0.0 ?
|
|
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, c, r2](const GCodeProcessorResult::MoveVertex &move)
|
|
{ return ! move_valid(move) || (to_2d(move.position) - c).squaredNorm() <= r2; }) :
|
|
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, c, r2, z = m_max_print_height + epsilon](const GCodeProcessorResult::MoveVertex& move)
|
|
{ return ! move_valid(move) || ((to_2d(move.position) - c).squaredNorm() <= r2 && move.position.z() <= z); });
|
|
}
|
|
case BuildVolume_Type::Convex:
|
|
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
|
|
case BuildVolume_Type::Custom:
|
|
return m_max_print_height == 0.0 ?
|
|
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, this](const GCodeProcessorResult::MoveVertex &move)
|
|
{ return ! move_valid(move) || Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_bed, to_2d(move.position).cast<double>()); }) :
|
|
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, this, z = m_max_print_height + epsilon](const GCodeProcessorResult::MoveVertex &move)
|
|
{ return ! move_valid(move) || (Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_bed, to_2d(move.position).cast<double>()) && move.position.z() <= z); });
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
template<typename Fn>
|
|
inline bool all_inside_vertices_normals_interleaved(const std::vector<float> &paths, Fn fn)
|
|
{
|
|
for (auto it = paths.begin(); it != paths.end(); ) {
|
|
it += 3;
|
|
if (! fn({ *it, *(it + 1), *(it + 2) }))
|
|
return false;
|
|
it += 3;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string_view BuildVolume::type_name(BuildVolume_Type type)
|
|
{
|
|
using namespace std::literals;
|
|
switch (type) {
|
|
case BuildVolume_Type::Invalid: return "Invalid"sv;
|
|
case BuildVolume_Type::Rectangle: return "Rectangle"sv;
|
|
case BuildVolume_Type::Circle: return "Circle"sv;
|
|
case BuildVolume_Type::Convex: return "Convex"sv;
|
|
case BuildVolume_Type::Custom: return "Custom"sv;
|
|
}
|
|
// make visual studio happy
|
|
assert(false);
|
|
return {};
|
|
}
|
|
|
|
indexed_triangle_set BuildVolume::bounding_mesh(bool scale) const
|
|
{
|
|
auto max_pt3 = m_bboxf.max;
|
|
if (scale) {
|
|
return its_make_cube(scale_(max_pt3.x()), scale_(max_pt3.y()), scale_(max_pt3.z()));
|
|
}
|
|
else {
|
|
return its_make_cube(max_pt3.x(), max_pt3.y(), max_pt3.z());
|
|
}
|
|
}
|
|
|
|
} // namespace Slic3r
|