mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-16 19:28:14 -06:00

* FIX: the logic of buried points that were not buried JIRA: none Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com> Change-Id: Id95174659c5fce7feba409eb5e14916608745fa4 * ci: update network module based on commit bc7ca98 Change-Id: I923526f0bf9ce5a288144fa1f9b0f2fc640f41b7 * Fix Firefox Co-authored-by: hadess <hadess@hadess.net> * FIX: cali: custom created filament from AMS displayed as incompatible jira: new remove the condition: is_system Change-Id: Ib1366966bbdbe01bc9e2483d9914d270ebefa976 * FIX: duplicated items in comboBox at calibration completed page jira: new Change-Id: I4749a2206df16c438e0d3098e36274b2a20f313e * ENH:update support for P1S plus jira:[for p1s plus] Change-Id: Id577d4e94e2162cb0045d261dfaa5f396ecded2f * ENH: CLI: add mk information support JIRA: no jira Change-Id: Idd89b143d439de50d9f52eb8aec95b262d66875d * ENH:calibration support p1p plus jira:[plus] Change-Id: Ia290d3a8a8b9adaac7a6ee26d9a8b5ea0c1b3aee * FIX: add log for base_id and filament_id github: #3087 Change-Id: Iebfbd0f224fce49f33fc81c71e6108f6e3abb5ff * FIX: sync whole preset vendor directory Change-Id: I191dbe979a87ff35d38cab1149b7975664344838 Jira: STUDIO-5534 (cherry picked from commit 628866608116336453804aa1217dd55db04d47ad) * FIX: use t_utc for debug only Change-Id: Ia05d8969d4de3dd38908980d6e17a3ebb11ca279 Github 3045 Change-Id: I77935df53bbf2772b1146e5c330c537165a3a2e6 * FIX:make sort_volumes right Jira: STUDIO-5645 Change-Id: If324c9115bfaaf0c1b7b4be7c7ee96ba6b8ac890 * ENH:keep an unload logic jira:[for unload] Change-Id: Id30ec71ffa5b2dac89346ea47ca48a62479e3ab1 * FIX: several problems with mesh boolean 1. Cut with multiple volumes are OK now. 2. Close mesh boolean fail error with new object or open object 3. Fix wrong name and config of boolean resulting object github: #3118 jira: none Change-Id: If2c9dbfb36cbdfe4917a2371217923891bb7909c (cherry picked from commit 982c0ecb92cf7c2b5ae5972ab900a6b10e7dda50) * NEW:limit the length of project name jira:[project name] Change-Id: I955620f7073b3b7fda280d1118524f561d047751 * ENH:adjusting the warning level of timelpase jira:[STUDIO-5662] Change-Id: I4902b22d316f5e09a97a62c88b8a98e55c405434 * FIX: 3mf specification: change namespace form slic3rpe to BambuStudio Jira: XXXX Change-Id: Id705affc875ef23fdf2ac6d79f0cb0aafc4f7050 * NEW: Open MakerWorld With BambuStudio GetParam JIRA: none Change-Id: I0d65b364f1cd2d634a88882ab072c3e61ea89167 (cherry picked from commit 8eaf45e5359439a7c796fd79876c86775abcf48e) * FIX: Filament issue generated when creating a printer Jira: XXXX Change-Id: I976770b69b47641bd54aa7a9c56fba7f58d1ab68 (cherry picked from commit ba42188b93c58b3954234d72acdd9769a68e3d3c) * FIX: Blank page appears when editing presets Jira: 5563 Change-Id: I4c49e05515b1beff55991e92f8079c4499c27eab (cherry picked from commit e86517d290f4cd0765a230d811b0ddf2c9f34c17) * FIX: context menu didn't update UI jira: STUDIO-5691 Change-Id: Ia66b8623d832eba805aff5320941233a68ff258b * FIX: crash of "filling bed" "get_arrange_settings() const" gets trapped in infinite recursive calling. Now we delete this function. jira: STUDIO-5688 Change-Id: Ia39974734bb37b2a2f06b5bf78185f01be726872 * FIX: boolean hangs in the middle of color painting Can't do splits in combine_mesh_fff, as do_boolean of mcut will split meshes. jira: STUDIO-5693 Change-Id: Idddb7d20dd7ca386c39ddd3d87d9defc3136aa5d (cherry picked from commit 6c67d015941458e37faaf0015b6509b5a0eadc0e) * Fix: Fix a number of compilation problems issues found when using gcc version 13.2.0 (GCC) in a Flatpak sandbox github : https://github.com/bambulab/BambuStudio/issues/3074 github pull request: https://github.com/bambulab/BambuStudio/pull/3096 Change-Id: I08aeac593eb1ce7675894df72e8489200bae713d (cherry picked from commit 069d133d66bfa682de4a860e379d5dc16b3d907c) * fix: macos icns issue when icon was not attached github pull request:https://github.com/bambulab/BambuStudio/pull/3116 Change-Id: I49072ad49f3af7669a6d307c791594ade210da50 (cherry picked from commit c977e5582e3a30ad16dd267810037423aad9a53c) * FIX: Add flush_length for change_filament_gcode Change-Id: I30f4b97d3d61c2a57f0e92f157cbd31c38aa7265 Jira: XXXX (cherry picked from commit 92eb2bac977a0c4095b316cbbc6580fb5228b710) * FIX: edit preset dialog can't close on mac Jira: 5696 Change-Id: Ib33dfd07cc588ddd3805e3490a4d8c36dcd890ac * ENH: add dev_ota_version in ssdp JIRA: STUDIO-5740 Change-Id: Ic80e6d4b9bf82813fdc4a76604a3d36213d12b03 Signed-off-by: Stone Li <stone.li@bambulab.com> * NEW:Adapt to multicolour and gradient colour JIRA:xxxx Change-Id: I8084cab603d5681cbcaf2d6f5e0d7ad5419cb2af * NEW:Adaptation of semi transparent materials JIRA: XXXX Change-Id: Ie32d8ce67c37b85eb6d7d6594cb514a696307e68 * FIX: disable flush options if prime tower is unchecked jira: STUDIO-5639 Change-Id: I25081584d430bc7d062e14bcc2cdbf7522cf9d99 * ENH: refine GetVersion for HMS query JIRA: STUDIO-5763 Change-Id: Ia3ccc07d79cc0736eb12e9782da50211abb74772 Signed-off-by: Stone Li <stone.li@bambulab.com> * FIX: Prefer old selection when sync AMS not compatible Change-Id: I6b18db51887132a997cf78d70fff9a92e23bc44a Jira: STUDIO-5416 (cherry picked from commit 077fae29823cf4f3071d408b1b40f55ee0cb33c6) * FIX: The flushing was not auto-calc when sync ams list JIRA: STUDIO-5551 1. flushing volume auto-calc when sync ams list 2. flushing volume takes the larger calculation value when filament has multi-colors Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com> Change-Id: I72e6f9780ea56103a44c2da6068440a4615c254d * FIX:fixed invalid links jira:[fixed link] Change-Id: I036a38b6e8e94da762f93805bd7be706538771fe * FIX: Prompt to delete problematic presets Jira: XXXX Change-Id: Ic43f7bb782794d7ab0b6acbffbb5d73e94f6ed73 * FIX:fixed incorrect HMS content jira:[STUDIO-5818] Change-Id: Ia2896d6f0ab1ffedbc850e54168acece8e47bdbb * FIX:external transparent material display error JIRA: STUDIO-5845 Change-Id: I0a4f05ac5d5c0ac49d85a704ee65a7221c5f1e1d * FIX: [5846] Custom Filament Page show System Filament Simultaneously solve: When downloading Preset from the cloud, the filament_id of the preset in m_preset is null. Jira: 5846 Change-Id: I6ba1b46fe92e345614b6a4af3fffa87d81fa2456 * FIX:A1 and p1 series do not support custom materials JIRA:XXXX Change-Id: Ib0459273d1f9a7152a5563757204634a8d0cd6f5 * FIX: exception when comparing profiles jira:[NEW] Signed-off-by: XunZhangBambu <xun.zhang@bambulab.com> Change-Id: I946b5fcd35f779d271df2b3de731fdcada5aab29 (cherry picked from commit 00e739570812e5c4be3e0f7702ce8c72c0f9e72b) * FIX: hide_id_middle_string Change-Id: I28f32ec526b443d31d7992971b80ab1cb737deb6 Github: STUDIO-5825 * ENH: modify some logs level JIRA: STUDIO-5958 Change-Id: I5a8592dfb8ffa9a81952535cb30944f867aa0e22 Signed-off-by: Stone Li <stone.li@bambulab.com> * NEW:build plate marker detect Change-Id: I70f03efea688bb6ce71c3f5990bb3c50605ab184 * FIX: Studio UI Freeze when saving user preset github: #3335 Change-Id: Idaf53f673a3e46408826c06bdde2c592395d358b * update bbl plugin version * fix build errors * update bbl profiles * update color --------- Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com> Signed-off-by: Stone Li <stone.li@bambulab.com> Co-authored-by: Kunlong Ma <kunlong.ma@bambulab.com> Co-authored-by: gerrit <gerrit@bambulab.com> Co-authored-by: liz.li <liz.li@bambulab.com> Co-authored-by: tao wang <tao.wang@bambulab.com> Co-authored-by: lane.wei <lane.wei@bambulab.com> Co-authored-by: maosheng.wei <maosheng.wei@bambulab.com> Co-authored-by: chunmao.guo <chunmao.guo@bambulab.com> Co-authored-by: zhou.xu <zhou.xu@bambulab.com> Co-authored-by: Arthur <arthur.tang@bambulab.com> Co-authored-by: Bastien Nocera <hadess@hadess.net> Co-authored-by: zhimin.zeng <zhimin.zeng@bambulab.com> Co-authored-by: hu.wang <hu.wang@bambulab.com> Co-authored-by: Stone Li <stone.li@bambulab.com> Co-authored-by: XunZhangBambu <xun.zhang@bambulab.com>
434 lines
22 KiB
C++
434 lines
22 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);
|
|
|
|
// 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, 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; };
|
|
|
|
for (const stl_vertex &v : its.vertices) {
|
|
const 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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
if (num_above < its.vertices.size() && ! (inside && outside)) {
|
|
// 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, [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, [circle](const Vec3f &pt) { return circle.contains(to_2d(pt)); }) :
|
|
object_state_templ(its, trafo, may_be_below_bed, [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, [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, [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;
|
|
}
|
|
|
|
bool BuildVolume::all_paths_inside_vertices_and_normals_interleaved(const std::vector<float>& paths, const Eigen::AlignedBox<float, 3>& paths_bbox, bool ignore_bottom) const
|
|
{
|
|
assert(paths.size() % 6 == 0);
|
|
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.min().cast<double>()) && build_volume.contains(paths_bbox.max().cast<double>());
|
|
}
|
|
case BuildVolume_Type::Circle:
|
|
{
|
|
const Vec2f c = unscaled<float>(m_circle.center);
|
|
const float r = unscaled<double>(m_circle.radius) + float(epsilon);
|
|
const float r2 = sqr(r);
|
|
return m_max_print_height == 0.0 ?
|
|
all_inside_vertices_normals_interleaved(paths, [c, r2](Vec3f p) { return (to_2d(p) - c).squaredNorm() <= r2; }) :
|
|
all_inside_vertices_normals_interleaved(paths, [c, r2, z = m_max_print_height + epsilon](Vec3f p) { return (to_2d(p) - c).squaredNorm() <= r2 && p.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 ?
|
|
all_inside_vertices_normals_interleaved(paths, [this](Vec3f p) { return Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_bed, to_2d(p).cast<double>()); }) :
|
|
all_inside_vertices_normals_interleaved(paths, [this, z = m_max_print_height + epsilon](Vec3f p) { return Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_bed, to_2d(p).cast<double>()) && p.z() <= z; });
|
|
default:
|
|
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
|