Skip to content

Commit

Permalink
Port Paint Brim Gizmo from BBS (#8433)
Browse files Browse the repository at this point in the history
This PR ported the Brim Ear gizmo from BambuStudio. Thanks BambuLab!

For compatibility reason, the existing auto-detected brim ears is kept.
To use this new brim ear gizmo, you will have to change the brim style
to "Painted".


![image](https://github.com/user-attachments/assets/5ff93f49-3fd2-46d4-9af4-240b7fb45991)


Fix #6774
  • Loading branch information
SoftFever authored Feb 18, 2025
2 parents 00a3e78 + 2bc9c8d commit df902d1
Show file tree
Hide file tree
Showing 32 changed files with 1,775 additions and 72 deletions.
19 changes: 19 additions & 0 deletions resources/images/toolbar_brimears.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions resources/images/toolbar_brimears_dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
81 changes: 60 additions & 21 deletions src/libslic3r/Brim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -806,16 +806,16 @@ double configBrimWidthByVolumeGroups(double adhesion, double maxSpeed, const std

// Generate ears
// Ported from SuperSlicer: https://github.com/supermerill/SuperSlicer/blob/45d0532845b63cd5cefe7de7dc4ef0e0ed7e030a/src/libslic3r/Brim.cpp#L1116
static ExPolygons make_brim_ears(ExPolygons& obj_expoly, coord_t size_ear, coord_t ear_detection_length,
coordf_t brim_ears_max_angle, bool is_outer_brim) {
static ExPolygons make_brim_ears_auto(const ExPolygons& obj_expoly, coord_t size_ear, coord_t ear_detection_length,
coordf_t brim_ears_max_angle, bool is_outer_brim) {
ExPolygons mouse_ears_ex;
if (size_ear <= 0) {
return mouse_ears_ex;
}
// Detect places to put ears
const coordf_t angle_threshold = (180 - brim_ears_max_angle) * PI / 180.0;
Points pt_ears;
for (ExPolygon &poly : obj_expoly) {
for (const ExPolygon &poly : obj_expoly) {
Polygon decimated_polygon = poly.contour;
if (ear_detection_length > 0) {
// decimate polygon
Expand All @@ -835,8 +835,8 @@ static ExPolygons make_brim_ears(ExPolygons& obj_expoly, coord_t size_ear, coord
// Then add ears
// create ear pattern
Polygon point_round;
for (size_t i = 0; i < POLY_SIDES; i++) {
double angle = (2.0 * PI * i) / POLY_SIDES;
for (size_t i = 0; i < POLY_SIDE_COUNT; i++) {
double angle = (2.0 * PI * i) / POLY_SIDE_COUNT;
point_round.points.emplace_back(size_ear * cos(angle), size_ear * sin(angle));
}

Expand All @@ -850,6 +850,41 @@ static ExPolygons make_brim_ears(ExPolygons& obj_expoly, coord_t size_ear, coord
return mouse_ears_ex;
}

static ExPolygons make_brim_ears(const PrintObject* object, const double& flowWidth, float brim_offset, Flow &flow, bool is_outer_brim)
{
ExPolygons mouse_ears_ex;
BrimPoints brim_ear_points = object->model_object()->brim_points;
if (brim_ear_points.size() <= 0) {
return mouse_ears_ex;
}
const Geometry::Transformation& trsf = object->model_object()->instances[0]->get_transformation();
Transform3d model_trsf = trsf.get_matrix_no_offset();
const Point &center_offset = object->center_offset();
model_trsf = model_trsf.pretranslate(Vec3d(- unscale<double>(center_offset.x()), - unscale<double>(center_offset.y()), 0));
for (auto &pt : brim_ear_points) {
Vec3f world_pos = pt.transform(trsf.get_matrix());
if ( world_pos.z() > 0) continue;
Polygon point_round;
float brim_width = floor(scale_(pt.head_front_radius) / flowWidth / 2) * flowWidth * 2;
if (is_outer_brim) {
double flowWidthScale = flowWidth / SCALING_FACTOR;
brim_width = floor(brim_width / flowWidthScale / 2) * flowWidthScale * 2;
}
coord_t size_ear = (brim_width - brim_offset - flow.scaled_spacing());
for (size_t i = 0; i < POLY_SIDE_COUNT; i++) {
double angle = (2.0 * PI * i) / POLY_SIDE_COUNT;
point_round.points.emplace_back(size_ear * cos(angle), size_ear * sin(angle));
}
mouse_ears_ex.emplace_back();
mouse_ears_ex.back().contour = point_round;
Vec3f pos = pt.transform(model_trsf);
int32_t pt_x = scale_(pos.x());
int32_t pt_y = scale_(pos.y());
mouse_ears_ex.back().contour.translate(Point(pt_x, pt_y));
}
return mouse_ears_ex;
}

//BBS: create all brims
static ExPolygons outer_inner_brim_area(const Print& print,
const float no_brim_offset, std::map<ObjectID, ExPolygons>& brimAreaMap,
Expand Down Expand Up @@ -888,9 +923,10 @@ static ExPolygons outer_inner_brim_area(const Print& print,
const float scaled_additional_brim_width = scale_(floor(5 / flowWidth / 2) * flowWidth * 2);
const float scaled_half_min_adh_length = scale_(1.1);
bool has_brim_auto = object->config().brim_type == btAutoBrim;
const bool use_brim_ears = object->config().brim_type == btEar;
const bool has_inner_brim = brim_type == btInnerOnly || brim_type == btOuterAndInner || use_brim_ears;
const bool has_outer_brim = brim_type == btOuterOnly || brim_type == btOuterAndInner || brim_type == btAutoBrim || use_brim_ears;
const bool use_auto_brim_ears = object->config().brim_type == btEar;
const bool use_brim_ears = object->config().brim_type == btPainted;
const bool has_inner_brim = brim_type == btInnerOnly || brim_type == btOuterAndInner || use_auto_brim_ears || use_brim_ears;
const bool has_outer_brim = brim_type == btOuterOnly || brim_type == btOuterAndInner || brim_type == btAutoBrim || use_auto_brim_ears || use_brim_ears;
coord_t ear_detection_length = scale_(object->config().brim_ears_detection_length.value);
coordf_t brim_ears_max_angle = object->config().brim_ears_max_angle.value;

Expand Down Expand Up @@ -951,27 +987,30 @@ static ExPolygons outer_inner_brim_area(const Print& print,
if (has_outer_brim) {
// BBS: inner and outer boundary are offset from the same polygon incase of round off error.
auto innerExpoly = offset_ex(ex_poly.contour, brim_offset, jtRound, SCALED_RESOLUTION);
auto &clipExpoly = innerExpoly;

ExPolygons outerExpoly;
if (use_brim_ears) {
outerExpoly = make_brim_ears(object, flowWidth, brim_offset, flow, true);
//outerExpoly = offset_ex(outerExpoly, brim_width_mod, jtRound, SCALED_RESOLUTION);
} else if (use_auto_brim_ears) {
coord_t size_ear = (brim_width_mod - brim_offset - flow.scaled_spacing());
append(brim_area_object, diff_ex(make_brim_ears(innerExpoly, size_ear, ear_detection_length, brim_ears_max_angle, true), clipExpoly));
} else {
// Normal brims
append(brim_area_object, diff_ex(offset_ex(innerExpoly, brim_width_mod, jtRound, SCALED_RESOLUTION), clipExpoly));
outerExpoly = make_brim_ears_auto(innerExpoly, size_ear, ear_detection_length, brim_ears_max_angle, true);
}else {
outerExpoly = offset_ex(innerExpoly, brim_width_mod, jtRound, SCALED_RESOLUTION);
}
append(brim_area_object, diff_ex(outerExpoly, innerExpoly));
}
if (has_inner_brim) {
auto outerExpoly = offset_ex(ex_poly_holes_reversed, -brim_offset);
auto clipExpoly = offset_ex(ex_poly_holes_reversed, -brim_width - brim_offset);

ExPolygons outerExpoly;
auto innerExpoly = offset_ex(ex_poly_holes_reversed, -brim_width - brim_offset);
if (use_brim_ears) {
outerExpoly = make_brim_ears(object, flowWidth, brim_offset, flow, false);
} else if (use_auto_brim_ears) {
coord_t size_ear = (brim_width - brim_offset - flow.scaled_spacing());
append(brim_area_object, diff_ex(make_brim_ears(outerExpoly, size_ear, ear_detection_length, brim_ears_max_angle, false), clipExpoly));
} else {
// Normal brims
append(brim_area_object, diff_ex(outerExpoly, clipExpoly));
outerExpoly = make_brim_ears_auto(offset_ex(ex_poly_holes_reversed, -brim_offset), size_ear, ear_detection_length, brim_ears_max_angle, false);
}else {
outerExpoly = offset_ex(ex_poly_holes_reversed, -brim_offset);
}
append(brim_area_object, diff_ex(outerExpoly, innerExpoly));
}
if (!has_inner_brim) {
// BBS: brim should be apart from holes
Expand Down
63 changes: 63 additions & 0 deletions src/libslic3r/BrimEarsPoint.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#ifndef BRIMEARSPOINT_HPP
#define BRIMEARSPOINT_HPP

#include <libslic3r/Point.hpp>


namespace Slic3r {

// An enum to keep track of where the current points on the ModelObject came from.
enum class PointsStatus {
NoPoints, // No points were generated so far.
Generating, // The autogeneration algorithm triggered, but not yet finished.
AutoGenerated, // Points were autogenerated (i.e. copied from the backend).
UserModified // User has done some edits.
};

struct BrimPoint
{
Vec3f pos;
float head_front_radius;

BrimPoint()
: pos(Vec3f::Zero()), head_front_radius(0.f)
{}

BrimPoint(float pos_x,
float pos_y,
float pos_z,
float head_radius)
: pos(pos_x, pos_y, pos_z)
, head_front_radius(head_radius)
{}

BrimPoint(Vec3f position, float head_radius)
: pos(position)
, head_front_radius(head_radius)
{}

Vec3f transform(const Transform3d &trsf)
{
Vec3d result = trsf * pos.cast<double>();
return result.cast<float>();
}

bool operator==(const BrimPoint &sp) const
{
float rdiff = std::abs(head_front_radius - sp.head_front_radius);
return (pos == sp.pos) && rdiff < float(EPSILON);
}

bool operator!=(const BrimPoint &sp) const { return !(sp == (*this)); }
template<class Archive> void serialize(Archive &ar)
{
ar(pos, head_front_radius);
}
};

using BrimPoints = std::vector<BrimPoint>;

}


#endif // BRIMEARSPOINT_HPP
1 change: 1 addition & 0 deletions src/libslic3r/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ set(lisbslic3r_sources
FaceDetector.hpp
Brim.cpp
Brim.hpp
BrimEarsPoint.hpp
BuildVolume.cpp
BuildVolume.hpp
Circle.cpp
Expand Down
9 changes: 9 additions & 0 deletions src/libslic3r/ExPolygon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ bool overlaps(const ExPolygons& expolys1, const ExPolygons& expolys2)
return false;
}

bool overlaps(const ExPolygons& expolys, const ExPolygon& expoly)
{
for (const ExPolygon& el : expolys) {
if (el.overlaps(expoly))
return true;
}
return false;
}

Point projection_onto(const ExPolygons& polygons, const Point& from)
{
Point projected_pt;
Expand Down
1 change: 1 addition & 0 deletions src/libslic3r/ExPolygon.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ inline ExPolygons expolygons_simplify(const ExPolygons &expolys, double toleranc
bool expolygons_match(const ExPolygon &l, const ExPolygon &r);

bool overlaps(const ExPolygons& expolys1, const ExPolygons& expolys2);
bool overlaps(const ExPolygons& expolys, const ExPolygon& expoly);

Point projection_onto(const ExPolygons& expolys, const Point& pt);

Expand Down
Loading

0 comments on commit df902d1

Please sign in to comment.