mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-26 20:32:06 +00:00
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:
@@ -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); });
|
||||
|
||||
@@ -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 };
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user