ENH: dual_extruder: add logic to process extruder_printable_area

JIRA: STUDIO-7498
Change-Id: I1cf53db93acf41b06cb1b9569a0679487c9f1e41
(cherry picked from commit e5be69dedd1ba6dc289a14b89598c9a6101dacb3)
This commit is contained in:
lane.wei
2024-08-30 21:13:16 +08:00
committed by Noisyfox
parent e433e49e2f
commit f702ad9fd2
20 changed files with 339 additions and 76 deletions

View File

@@ -9,7 +9,7 @@
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)
BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double printable_height, const std::vector<std::vector<Vec2d>> &extruder_areas) : m_bed_shape(printable_area), m_max_print_height(printable_height), m_extruder_shapes(extruder_areas)
{
assert(printable_height >= 0);
@@ -77,6 +77,69 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
m_top_bottom_convex_hull_decomposition_bed = convex_decomposition(m_convex_hull, BedEpsilon);
}
if (m_extruder_shapes.size() > 0)
{
for (unsigned int index = 0; index < m_extruder_shapes.size(); index++)
{
std::vector<Vec2d>& extruder_shape = m_extruder_shapes[index];
BuildExtruderVolume extruder_volume;
if (extruder_shape == printable_area) {
extruder_volume.same_with_bed = true;
extruder_volume.type = m_type;
extruder_volume.bbox = m_bbox;
extruder_volume.bboxf = m_bboxf;
extruder_volume.circle = m_circle;
}
else {
Polygon poly = Polygon::new_scale(extruder_shape);
double poly_area = poly.area();
extruder_volume.bbox = get_extents(poly);
BoundingBoxf temp_bboxf = get_extents(extruder_shape);
extruder_volume.bboxf = BoundingBoxf3{ to_3d(temp_bboxf.min, 0.), to_3d(temp_bboxf.max, printable_height) };
if (extruder_shape.size() >= 4 && std::abs((poly_area - double(extruder_volume.bbox.size().x()) * double(extruder_volume.bbox.size().y()))) < sqr(SCALED_EPSILON))
{
extruder_volume.type = Type::Rectangle;
extruder_volume.circle.center = 0.5 * (extruder_volume.bbox.min.cast<double>() + extruder_volume.bbox.max.cast<double>());
extruder_volume.circle.radius = 0.5 * extruder_volume.bbox.size().cast<double>().norm();
}
else if (extruder_shape.size() > 3) {
extruder_volume.circle = Geometry::circle_ransac(extruder_shape);
bool is_circle = true;
Vec2d prev = extruder_shape.back();
for (const Vec2d &p : extruder_shape) {
if (// Polygon vertices must lie very close the circle.
std::abs((p - extruder_volume.circle.center).norm() - extruder_volume.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().
extruder_volume.circle.radius - (0.5 * (prev + p) -extruder_volume.circle.center).norm() > 3.) {
is_circle = false;
break;
}
prev = p;
}
if (is_circle) {
extruder_volume.type = Type::Circle;
extruder_volume.circle.center = scaled<double>(extruder_volume.circle.center);
extruder_volume.circle.radius = scaled<double>(extruder_volume.circle.radius);
}
}
if (m_type == Type::Invalid) {
//not supported currently, use the same as bed
extruder_volume.same_with_bed = true;
extruder_volume.type = m_type;
extruder_volume.bbox = m_bbox;
extruder_volume.bboxf = m_bboxf;
extruder_volume.circle = m_circle;
}
m_extruder_volumes.push_back(std::move(extruder_volume));
}
}
}
BOOST_LOG_TRIVIAL(debug) << "BuildVolume printable_area clasified as: " << this->type_name();
}
@@ -197,7 +260,7 @@ BuildVolume::ObjectState object_state_templ(const indexed_triangle_set &its, con
bool outside = false;
static constexpr const auto world_min_z = float(-BuildVolume::SceneEpsilon);
if (may_be_below_bed)
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.
@@ -260,7 +323,7 @@ BuildVolume::ObjectState object_state_templ(const indexed_triangle_set &its, con
}
}
}
else
else
{
// Much simpler and faster code, not clipping the object with the print bed.
assert(! may_be_below_bed);
@@ -297,14 +360,14 @@ BuildVolume::ObjectState BuildVolume::object_state(const indexed_triangle_set& i
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 ?
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 ?
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:
@@ -322,10 +385,100 @@ BuildVolume::ObjectState BuildVolume::volume_state_bbox(const BoundingBoxf3& vol
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.contains(volume_bbox) ? ObjectState::Inside :
build_volume.intersects(volume_bbox) ? ObjectState::Colliding : ObjectState::Outside;
}
const BuildVolume::BuildExtruderVolume& BuildVolume::get_extruder_area_volume(int index) const
{
assert(index >= 0 && index < m_extruder_volumes.size());
return m_extruder_volumes[index];
}
BuildVolume::ObjectState BuildVolume::check_object_state_with_extruder_area(const indexed_triangle_set &its, const Transform3f &trafo, int index) const
{
const BuildExtruderVolume& extruder_volume = get_extruder_area_volume(index);
ObjectState return_state = ObjectState::Inside;
if (!extruder_volume.same_with_bed) {
switch (extruder_volume.type) {
case Type::Rectangle:
{
BoundingBox3Base<Vec3d> build_volume = extruder_volume.bboxf.inflated(SceneEpsilon);
if (m_max_print_height == 0.0)
build_volume.max.z() = std::numeric_limits<double>::max();
BoundingBox3Base<Vec3f> build_volumef(build_volume.min.cast<float>(), build_volume.max.cast<float>());
return_state = object_state_templ(its, trafo, false, [build_volumef](const Vec3f &pt) { return build_volumef.contains(pt); });
break;
}
case Type::Circle:
{
Geometry::Circlef circle { unscaled<float>(extruder_volume.circle.center), unscaled<float>(extruder_volume.circle.radius + SceneEpsilon) };
return_state = (m_max_print_height == 0.0) ?
object_state_templ(its, trafo, false, [circle](const Vec3f &pt) { return circle.contains(to_2d(pt)); }) :
object_state_templ(its, trafo, false, [circle, z = m_max_print_height + SceneEpsilon](const Vec3f &pt) { return pt.z() < z && circle.contains(to_2d(pt)); });
break;
}
case Type::Invalid:
default:
break;
}
}
if (return_state != ObjectState::Inside)
return_state = ObjectState::Limited;
return return_state;
}
BuildVolume::ObjectState BuildVolume::check_object_state_with_extruder_areas(const indexed_triangle_set &its, const Transform3f &trafo, std::vector<bool>& inside_extruders) const
{
ObjectState result = ObjectState::Inside;
int extruder_area_count = get_extruder_area_count();
inside_extruders.resize(extruder_area_count, true);
for (int index = 0; index < extruder_area_count; index++)
{
ObjectState state = check_object_state_with_extruder_area(its, trafo, index);
if (state == ObjectState::Limited) {
inside_extruders[index] = false;
result = ObjectState::Limited;
}
}
return result;
}
BuildVolume::ObjectState BuildVolume::check_volume_bbox_state_with_extruder_area(const BoundingBoxf3& volume_bbox, int index) const
{
const BuildExtruderVolume& extruder_volume = get_extruder_area_volume(index);
if (extruder_volume.same_with_bed || extruder_volume.bboxf.contains(volume_bbox))
return ObjectState::Inside;
else
return ObjectState::Limited;
}
BuildVolume::ObjectState BuildVolume::check_volume_bbox_state_with_extruder_areas(const BoundingBoxf3& volume_bbox, std::vector<bool>& inside_extruders) const
{
ObjectState result = ObjectState::Inside;
int extruder_area_count = get_extruder_area_count();
inside_extruders.resize(extruder_area_count, true);
for (int index = 0; index < extruder_area_count; index++)
{
ObjectState state = check_volume_bbox_state_with_extruder_area(volume_bbox, index);
if (state == ObjectState::Limited) {
inside_extruders[index] = false;
result = ObjectState::Limited;
}
}
return result;
}
bool BuildVolume::all_paths_inside(const GCodeProcessorResult& paths, const BoundingBoxf3& paths_bbox, bool ignore_bottom) const
{
auto move_valid = [](const GCodeProcessorResult::MoveVertex &move) {
@@ -348,7 +501,7 @@ bool BuildVolume::all_paths_inside(const GCodeProcessorResult& paths, const Boun
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 ?
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)
@@ -358,7 +511,7 @@ bool BuildVolume::all_paths_inside(const GCodeProcessorResult& paths, const Boun
//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)
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); });

View File

@@ -29,16 +29,24 @@ class BuildVolume
{
public:
struct BuildExtruderVolume {
bool same_with_bed{false};
Type type{Type::Invalid};
BoundingBox bbox;
BoundingBoxf3 bboxf;
Geometry::Circled circle;
};
// Initialized to empty, all zeros, Invalid.
BuildVolume() {}
// Initialize from PrintConfig::printable_area and PrintConfig::printable_height
BuildVolume(const std::vector<Vec2d> &printable_area, const double printable_height);
BuildVolume(const std::vector<Vec2d> &printable_area, const double printable_height, const std::vector<std::vector<Vec2d>> &extruder_areas);
// Source data, unscaled coordinates.
const std::vector<Vec2d>& printable_area() const { return m_bed_shape; }
double printable_height() const { return m_max_print_height; }
const std::vector<std::vector<Vec2d>>& extruder_areas() const { return m_extruder_shapes; }
// Derived data
BuildVolume_Type type() const { return m_type; }
// Format the type for console output.
@@ -69,9 +77,11 @@ public:
Colliding,
// Outside of the build volume means the object is ignored: Not printed and no error is shown.
Outside,
// Completely below the print bed. The same as Outside, but an object with one printable part below the print bed
// Completely below the print bed. The same as Outside, but an object with one printable part below the print bed
// and at least one part above the print bed is still printable.
Below,
//in Limited area
Limited
};
// 1) Tests called on the plater.
@@ -94,12 +104,22 @@ public:
// Called on initial G-code preview on OpenGL vertex buffer interleaved normals and vertices.
bool all_paths_inside_vertices_and_normals_interleaved(const std::vector<float>& paths, const Eigen::AlignedBox<float, 3>& bbox, bool ignore_bottom = true) const;
int get_extruder_area_count() const { return m_extruder_volumes.size(); }
const BuildExtruderVolume& get_extruder_area_volume(int index) const;
ObjectState check_object_state_with_extruder_area(const indexed_triangle_set &its, const Transform3f &trafo, int index) const;
ObjectState check_object_state_with_extruder_areas(const indexed_triangle_set &its, const Transform3f &trafo, std::vector<bool>& inside_extruders) const;
ObjectState check_volume_bbox_state_with_extruder_area(const BoundingBoxf3& volume_bbox, int index) const;
ObjectState check_volume_bbox_state_with_extruder_areas(const BoundingBoxf3& volume_bbox, std::vector<bool>& inside_extruders) const;
const std::pair<std::vector<Vec2d>, std::vector<Vec2d>>& top_bottom_convex_hull_decomposition_scene() const { return m_top_bottom_convex_hull_decomposition_scene; }
const std::pair<std::vector<Vec2d>, std::vector<Vec2d>>& top_bottom_convex_hull_decomposition_bed() const { return m_top_bottom_convex_hull_decomposition_bed; }
private:
// Source definition of the print bed geometry (PrintConfig::printable_area)
std::vector<Vec2d> m_bed_shape;
//BBS: extruder shapes
std::vector<std::vector<Vec2d>> m_extruder_shapes;
std::vector<BuildExtruderVolume> m_extruder_volumes;
// Source definition of the print volume height (PrintConfig::printable_height)
double m_max_print_height { 0.f };

View File

@@ -201,6 +201,7 @@ class Print;
Pointfs printable_area;
//BBS: add bed exclude area
Pointfs bed_exclude_area;
std::vector<Pointfs> extruder_areas;
//BBS: add toolpath_outside
bool toolpath_outside;
//BBS: add object_label_enabled

View File

@@ -3349,8 +3349,23 @@ ModelInstanceEPrintVolumeState ModelInstance::calc_print_volume_state(const Buil
BuildVolume::ObjectState state;
if (!build_volume.bounding_volume2d().inflated(BuildVolume::SceneEpsilon).overlap(bbox2d))
state = BuildVolume::ObjectState::Outside;
else
state = build_volume.object_state(vol->mesh().its, matrix.cast<float>(), true /* may be below print bed */);
else {
switch(build_volume.type())
{
case BuildVolume_Type::Rectangle:
{
state = build_volume.volume_state_bbox(bboxt);
break;
}
case BuildVolume_Type::Circle:
case BuildVolume_Type::Convex:
case BuildVolume_Type::Custom:
default:
state = build_volume.object_state(vol->mesh().its, matrix.cast<float>(), true /* may be below print bed */);
break;
}
}
if (state == BuildVolume::ObjectState::Inside)
// Volume is completely inside.
inside_outside |= INSIDE;

View File

@@ -1227,6 +1227,7 @@ inline const ModelVolume* model_volume_find_by_id(const ModelVolumePtrs &model_v
enum ModelInstanceEPrintVolumeState : unsigned char
{
ModelInstancePVS_Inside,
ModelInstancePVS_Limited,
ModelInstancePVS_Partly_Outside,
ModelInstancePVS_Fully_Outside,
ModelInstanceNum_BedStates

View File

@@ -4020,7 +4020,7 @@ void generate_tree_support_3D(PrintObject &print_object, TreeSupport* tree_suppo
Points bedpts = tree_support->m_machine_border.contour.points;
Pointfs bedptsf;
std::transform(bedpts.begin(), bedpts.end(), std::back_inserter(bedptsf), [](const Point &p) { return unscale(p); });
BuildVolume build_volume{ bedptsf, tree_support->m_print_config->printable_height };
BuildVolume build_volume{ bedptsf, tree_support->m_print_config->printable_height, {}};
TreeSupport3D::generate_support_areas(*print_object.print(), tree_support, build_volume, { idx }, throw_on_cancel);
}