NEW: add brim ear gizmo

jira: STUDIO-7378
Change-Id: Ib6b5dbd3b113981612b05e01e59f62054ff0b654
(cherry picked from commit 37120830a2f958f464719db1f5d0180882bc4095)
This commit is contained in:
Mack 2024-08-01 18:22:24 +08:00 committed by Lane.Wei
parent 84e7063c54
commit 92c85a13d0
24 changed files with 1425 additions and 55 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 194 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 149 KiB

View File

@ -804,6 +804,42 @@ double configBrimWidthByVolumeGroups(double adhension, double maxSpeed, const st
return brim_width; return brim_width;
} }
static ExPolygons make_brim_ears(const PrintObject* object, 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(true);
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) {
flowWidth = flowWidth / SCALING_FACTOR;
brim_width = floor(brim_width / flowWidth / 2) * flowWidth * 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 //BBS: create all brims
static ExPolygons outer_inner_brim_area(const Print& print, static ExPolygons outer_inner_brim_area(const Print& print,
const float no_brim_offset, std::map<ObjectID, ExPolygons>& brimAreaMap, const float no_brim_offset, std::map<ObjectID, ExPolygons>& brimAreaMap,
@ -812,6 +848,7 @@ static ExPolygons outer_inner_brim_area(const Print& print,
std::vector<unsigned int>& printExtruders) std::vector<unsigned int>& printExtruders)
{ {
unsigned int support_material_extruder = printExtruders.front() + 1; unsigned int support_material_extruder = printExtruders.front() + 1;
Flow flow = print.brim_flow();
ExPolygons brim_area; ExPolygons brim_area;
ExPolygons no_brim_area; ExPolygons no_brim_area;
@ -841,7 +878,6 @@ static ExPolygons outer_inner_brim_area(const Print& print,
} }
hole_index = -1; hole_index = -1;
}; };
for (unsigned int extruderNo : printExtruders) { for (unsigned int extruderNo : printExtruders) {
++extruderNo; ++extruderNo;
for (const auto& objectWithExtruder : objPrintVec) { for (const auto& objectWithExtruder : objPrintVec) {
@ -854,6 +890,11 @@ 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_additional_brim_width = scale_(floor(5 / flowWidth / 2) * flowWidth * 2);
const float scaled_half_min_adh_length = scale_(1.1); const float scaled_half_min_adh_length = scale_(1.1);
bool has_brim_auto = object->config().brim_type == btAutoBrim; bool has_brim_auto = object->config().brim_type == btAutoBrim;
bool use_brim_ears = object->config().brim_type == btBrimEars;
if (object->model_object()->brim_points.size()>0 && has_brim_auto)
use_brim_ears = true;
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;
ExPolygons brim_area_object; ExPolygons brim_area_object;
ExPolygons no_brim_area_object; ExPolygons no_brim_area_object;
@ -919,7 +960,7 @@ static ExPolygons outer_inner_brim_area(const Print& print,
Polygons ex_poly_holes_reversed = ex_poly.holes; Polygons ex_poly_holes_reversed = ex_poly.holes;
polygons_reverse(ex_poly_holes_reversed); polygons_reverse(ex_poly_holes_reversed);
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) { if (has_outer_brim) {
// BBS: to find whether an island is in a hole of its object // BBS: to find whether an island is in a hole of its object
int contour_hole_index = -1; int contour_hole_index = -1;
@ -927,12 +968,18 @@ static ExPolygons outer_inner_brim_area(const Print& print,
// BBS: inner and outer boundary are offset from the same polygon incase of round off error. // 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 innerExpoly = offset_ex(ex_poly.contour, brim_offset, jtRound, SCALED_RESOLUTION);
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 {
outerExpoly = offset_ex(innerExpoly, brim_width_mod, jtRound, SCALED_RESOLUTION);
}
if (contour_hole_index < 0) if (contour_hole_index < 0) {
append(brim_area_object, diff_ex(offset_ex(innerExpoly, brim_width_mod, jtRound, SCALED_RESOLUTION), innerExpoly)); append(brim_area_object, diff_ex(outerExpoly, innerExpoly));
else }else {
{ ExPolygons brimBeforeClip = diff_ex(outerExpoly, innerExpoly);
ExPolygons brimBeforeClip = diff_ex(offset_ex(innerExpoly, brim_width_mod, jtRound, SCALED_RESOLUTION), innerExpoly);
// BBS: an island's brim should not be outside of its belonging hole // BBS: an island's brim should not be outside of its belonging hole
Polygons selectedHole = { holes_area[contour_hole_index] }; Polygons selectedHole = { holes_area[contour_hole_index] };
@ -940,16 +987,23 @@ static ExPolygons outer_inner_brim_area(const Print& print,
append(brim_area_object, clippedBrim); append(brim_area_object, clippedBrim);
} }
} }
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner) { if (has_inner_brim) {
append(brim_area_object, diff_ex(offset_ex(ex_poly_holes_reversed, -brim_offset), 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 {
outerExpoly = offset_ex(ex_poly_holes_reversed, -brim_offset);
}
append(brim_area_object, diff_ex(outerExpoly, innerExpoly));
} }
if (brim_type != BrimType::btInnerOnly && brim_type != BrimType::btOuterAndInner) { if (!has_inner_brim) {
// BBS: brim should be apart from holes // BBS: brim should be apart from holes
append(no_brim_area_object, diff_ex(ex_poly_holes_reversed, offset_ex(ex_poly_holes_reversed, -scale_(5.)))); append(no_brim_area_object, diff_ex(ex_poly_holes_reversed, offset_ex(ex_poly_holes_reversed, -scale_(5.))));
} }
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim) if (!has_outer_brim)
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly_holes_reversed)); append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly_holes_reversed));
if (brim_type == BrimType::btNoBrim) if (!has_inner_brim && !has_outer_brim)
append(no_brim_area_object, offset_ex(ex_poly_holes_reversed, -no_brim_offset)); append(no_brim_area_object, offset_ex(ex_poly_holes_reversed, -no_brim_offset));
append(holes_object, ex_poly_holes_reversed); append(holes_object, ex_poly_holes_reversed);
} }

View File

@ -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

View File

@ -1079,6 +1079,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
this->sla_support_points = rhs.sla_support_points; this->sla_support_points = rhs.sla_support_points;
this->sla_points_status = rhs.sla_points_status; this->sla_points_status = rhs.sla_points_status;
this->sla_drain_holes = rhs.sla_drain_holes; this->sla_drain_holes = rhs.sla_drain_holes;
this->brim_points = rhs.brim_points;
this->layer_config_ranges = rhs.layer_config_ranges; this->layer_config_ranges = rhs.layer_config_ranges;
this->layer_height_profile = rhs.layer_height_profile; this->layer_height_profile = rhs.layer_height_profile;
this->printable = rhs.printable; this->printable = rhs.printable;
@ -1123,6 +1124,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
this->sla_support_points = std::move(rhs.sla_support_points); this->sla_support_points = std::move(rhs.sla_support_points);
this->sla_points_status = std::move(rhs.sla_points_status); this->sla_points_status = std::move(rhs.sla_points_status);
this->sla_drain_holes = std::move(rhs.sla_drain_holes); this->sla_drain_holes = std::move(rhs.sla_drain_holes);
this->brim_points = std::move(brim_points);
this->layer_config_ranges = std::move(rhs.layer_config_ranges); this->layer_config_ranges = std::move(rhs.layer_config_ranges);
this->layer_height_profile = std::move(rhs.layer_height_profile); this->layer_height_profile = std::move(rhs.layer_height_profile);
this->printable = std::move(rhs.printable); this->printable = std::move(rhs.printable);
@ -1715,6 +1717,7 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
new_object->sla_support_points.clear(); new_object->sla_support_points.clear();
new_object->sla_drain_holes.clear(); new_object->sla_drain_holes.clear();
new_object->sla_points_status = sla::PointsStatus::NoPoints; new_object->sla_points_status = sla::PointsStatus::NoPoints;
new_object->brim_points.clear();
new_object->clear_volumes(); new_object->clear_volumes();
new_object->input_file.clear(); new_object->input_file.clear();
@ -2313,6 +2316,7 @@ ModelObjectPtrs ModelObject::segment(size_t instance, unsigned int max_extruders
upper->sla_support_points.clear(); upper->sla_support_points.clear();
upper->sla_drain_holes.clear(); upper->sla_drain_holes.clear();
upper->sla_points_status = sla::PointsStatus::NoPoints; upper->sla_points_status = sla::PointsStatus::NoPoints;
upper->brim_points.clear();
upper->clear_volumes(); upper->clear_volumes();
upper->input_file.clear(); upper->input_file.clear();
@ -2552,6 +2556,7 @@ ModelObjectPtrs ModelObject::merge_volumes(std::vector<int>& vol_indeces)
upper->sla_support_points.clear(); upper->sla_support_points.clear();
upper->sla_drain_holes.clear(); upper->sla_drain_holes.clear();
upper->sla_points_status = sla::PointsStatus::NoPoints; upper->sla_points_status = sla::PointsStatus::NoPoints;
upper->brim_points.clear();
upper->clear_volumes(); upper->clear_volumes();
upper->input_file.clear(); upper->input_file.clear();
@ -4112,6 +4117,17 @@ bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObjec
[](const ModelVolume &mv_old, const ModelVolume &mv_new){ return mv_old.mmu_segmentation_facets.timestamp_matches(mv_new.mmu_segmentation_facets); }); [](const ModelVolume &mv_old, const ModelVolume &mv_new){ return mv_old.mmu_segmentation_facets.timestamp_matches(mv_new.mmu_segmentation_facets); });
} }
bool model_brim_points_data_changed(const ModelObject& mo, const ModelObject& mo_new)
{
if (mo.brim_points.size() != mo_new.brim_points.size())
return true;
for (size_t i = 0; i < mo.brim_points.size(); ++i) {
if (mo.brim_points[i] != mo_new.brim_points[i])
return true;
}
return false;
}
bool model_has_multi_part_objects(const Model &model) bool model_has_multi_part_objects(const Model &model)
{ {
for (const ModelObject *model_object : model.objects) for (const ModelObject *model_object : model.objects)

View File

@ -10,6 +10,7 @@
#include "Slicing.hpp" #include "Slicing.hpp"
#include "SLA/SupportPoint.hpp" #include "SLA/SupportPoint.hpp"
#include "SLA/Hollowing.hpp" #include "SLA/Hollowing.hpp"
#include "BrimEarsPoint.hpp"
#include "TriangleMesh.hpp" #include "TriangleMesh.hpp"
#include "CustomGCode.hpp" #include "CustomGCode.hpp"
#include "enum_bitmask.hpp" #include "enum_bitmask.hpp"
@ -372,6 +373,8 @@ public:
// Holes to be drilled into the object so resin can flow out // Holes to be drilled into the object so resin can flow out
sla::DrainHoles sla_drain_holes; sla::DrainHoles sla_drain_holes;
BrimPoints brim_points;
/* This vector accumulates the total translation applied to the object by the /* This vector accumulates the total translation applied to the object by the
center_around_origin() method. Callers might want to apply the same translation center_around_origin() method. Callers might want to apply the same translation
to new volumes before adding them to this object in order to preserve alignment to new volumes before adding them to this object in order to preserve alignment
@ -676,7 +679,7 @@ private:
Internal::StaticSerializationWrapper<ModelConfigObject const> config_wrapper(config); Internal::StaticSerializationWrapper<ModelConfigObject const> config_wrapper(config);
Internal::StaticSerializationWrapper<LayerHeightProfile const> layer_heigth_profile_wrapper(layer_height_profile); Internal::StaticSerializationWrapper<LayerHeightProfile const> layer_heigth_profile_wrapper(layer_height_profile);
ar(name, module_name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_heigth_profile_wrapper, ar(name, module_name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_heigth_profile_wrapper,
sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation, sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation, brim_points,
m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid, m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid,
cut_connectors, cut_id); cut_connectors, cut_id);
} }
@ -687,7 +690,7 @@ private:
// BBS: add backup, check modify // BBS: add backup, check modify
SaveObjectGaurd gaurd(*this); SaveObjectGaurd gaurd(*this);
ar(name, module_name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_heigth_profile_wrapper, ar(name, module_name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_heigth_profile_wrapper,
sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation, sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation, brim_points,
m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid, m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid,
cut_connectors, cut_id); cut_connectors, cut_id);
std::vector<ObjectID> volume_ids2; std::vector<ObjectID> volume_ids2;
@ -1732,6 +1735,8 @@ bool model_custom_seam_data_changed(const ModelObject& mo, const ModelObject& mo
// The function assumes that volumes list is synchronized. // The function assumes that volumes list is synchronized.
extern bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObject& mo_new); extern bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObject& mo_new);
bool model_brim_points_data_changed(const ModelObject& mo, const ModelObject& mo_new);
// If the model has multi-part objects, then it is currently not supported by the SLA mode. // If the model has multi-part objects, then it is currently not supported by the SLA mode.
// Either the model cannot be loaded, or a SLA printer has to be activated. // Either the model cannot be loaded, or a SLA printer has to be activated.
bool model_has_multi_part_objects(const Model &model); bool model_has_multi_part_objects(const Model &model);

View File

@ -237,7 +237,7 @@ Points filter_points_by_vectors(const Points &poly, FilterFn filter)
// p2 is next point to the currently visited point p1. // p2 is next point to the currently visited point p1.
Vec2d v2 = (p2 - p1).cast<double>(); Vec2d v2 = (p2 - p1).cast<double>();
if (filter(v1, v2)) if (filter(v1, v2))
out.emplace_back(p2); out.emplace_back(p1);
v1 = v2; v1 = v2;
p1 = p2; p1 = p2;
} }
@ -249,7 +249,7 @@ template<typename ConvexConcaveFilterFn>
Points filter_convex_concave_points_by_angle_threshold(const Points &poly, double angle_threshold, ConvexConcaveFilterFn convex_concave_filter) Points filter_convex_concave_points_by_angle_threshold(const Points &poly, double angle_threshold, ConvexConcaveFilterFn convex_concave_filter)
{ {
assert(angle_threshold >= 0.); assert(angle_threshold >= 0.);
if (angle_threshold < EPSILON) { if (angle_threshold > EPSILON) {
double cos_angle = cos(angle_threshold); double cos_angle = cos(angle_threshold);
return filter_points_by_vectors(poly, [convex_concave_filter, cos_angle](const Vec2d &v1, const Vec2d &v2){ return filter_points_by_vectors(poly, [convex_concave_filter, cos_angle](const Vec2d &v1, const Vec2d &v2){
return convex_concave_filter(v1, v2) && v1.normalized().dot(v2.normalized()) < cos_angle; return convex_concave_filter(v1, v2) && v1.normalized().dot(v2.normalized()) < cos_angle;

View File

@ -1255,6 +1255,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER); model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
bool layer_height_ranges_differ = ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty()); bool layer_height_ranges_differ = ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty());
bool model_origin_translation_differ = model_object.origin_translation != model_object_new.origin_translation; bool model_origin_translation_differ = model_object.origin_translation != model_object_new.origin_translation;
bool brim_points_differ = model_brim_points_data_changed(model_object, model_object_new);
auto print_objects_range = print_object_status_db.get_range(model_object); auto print_objects_range = print_object_status_db.get_range(model_object);
// The list actually can be empty if all instances are out of the print bed. // The list actually can be empty if all instances are out of the print bed.
//assert(print_objects_range.begin() != print_objects_range.end()); //assert(print_objects_range.begin() != print_objects_range.end());
@ -1301,6 +1302,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
} else if (model_custom_seam_data_changed(model_object, model_object_new)) { } else if (model_custom_seam_data_changed(model_object, model_object_new)) {
update_apply_status(this->invalidate_step(psGCodeExport)); update_apply_status(this->invalidate_step(psGCodeExport));
} }
if (brim_points_differ) {
model_object.brim_points = model_object_new.brim_points;
update_apply_status(this->invalidate_all_steps());
}
} }
if (! solid_or_modifier_differ) { if (! solid_or_modifier_differ) {
// Synchronize Object's config. // Synchronize Object's config.

View File

@ -272,7 +272,8 @@ static const t_config_enum_values s_keys_map_BrimType = {
{"outer_only", btOuterOnly}, {"outer_only", btOuterOnly},
{"inner_only", btInnerOnly}, {"inner_only", btInnerOnly},
{"outer_and_inner", btOuterAndInner}, {"outer_and_inner", btOuterAndInner},
{"auto_brim", btAutoBrim} // BBS {"auto_brim", btAutoBrim}, // BBS
{"brim_ears", btBrimEars} // BBS
}; };
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BrimType) CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BrimType)
@ -973,6 +974,7 @@ void PrintConfigDef::init_fff_params()
def->enum_keys_map = &ConfigOptionEnum<BrimType>::get_enum_values(); def->enum_keys_map = &ConfigOptionEnum<BrimType>::get_enum_values();
def->enum_values.emplace_back("auto_brim"); def->enum_values.emplace_back("auto_brim");
def->enum_values.emplace_back("outer_only"); def->enum_values.emplace_back("outer_only");
def->enum_values.emplace_back("brim_ears");
#if 1 //!BBL_RELEASE_TO_PUBLIC #if 1 //!BBL_RELEASE_TO_PUBLIC
// BBS: The following two types are disabled // BBS: The following two types are disabled
def->enum_values.emplace_back("inner_only"); def->enum_values.emplace_back("inner_only");
@ -981,6 +983,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.emplace_back("no_brim"); def->enum_values.emplace_back("no_brim");
def->enum_labels.emplace_back(L("Auto")); def->enum_labels.emplace_back(L("Auto"));
def->enum_labels.emplace_back(L("Manual"));
def->enum_labels.emplace_back(L("Outer brim only")); def->enum_labels.emplace_back(L("Outer brim only"));
#if 1 //!BBL_RELEASE_TO_PUBLIC #if 1 //!BBL_RELEASE_TO_PUBLIC
// BBS: The following two types are disabled // BBS: The following two types are disabled
@ -1002,6 +1005,13 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.)); def->set_default_value(new ConfigOptionFloat(0.));
def = this->add("brim_ears", coBool);
def->label = L("Brim ears");
def->category = L("Support");
def->tooltip = L("Draw brim");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("compatible_printers", coStrings); def = this->add("compatible_printers", coStrings);
def->label = L("Compatible machine"); def->label = L("Compatible machine");
def->mode = comDevelop; def->mode = comDevelop;

View File

@ -172,6 +172,7 @@ enum SLAPillarConnectionMode {
enum BrimType { enum BrimType {
btAutoBrim, // BBS btAutoBrim, // BBS
btBrimEars, // BBS
btOuterOnly, btOuterOnly,
btInnerOnly, btInnerOnly,
btOuterAndInner, btOuterAndInner,

View File

@ -57,6 +57,7 @@ static constexpr double EPSILON = 1e-4;
// with int64_t we don't have to worry anymore about the size of the int. // with int64_t we don't have to worry anymore about the size of the int.
static constexpr double SCALING_FACTOR = 0.000001; static constexpr double SCALING_FACTOR = 0.000001;
static constexpr double PI = 3.141592653589793238; static constexpr double PI = 3.141592653589793238;
#define POLY_SIDE_COUNT 24 // for brim ear circle
// When extruding a closed loop, the loop is interrupted and shortened a bit to reduce the seam. // When extruding a closed loop, the loop is interrupted and shortened a bit to reduce the seam.
static constexpr double LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER = 0.15; static constexpr double LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER = 0.15;
static constexpr double RESOLUTION = 0.0125; static constexpr double RESOLUTION = 0.0125;

View File

@ -139,6 +139,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Gizmos/GLGizmoFaceDetector.hpp GUI/Gizmos/GLGizmoFaceDetector.hpp
GUI/Gizmos/GLGizmoMeasure.cpp GUI/Gizmos/GLGizmoMeasure.cpp
GUI/Gizmos/GLGizmoMeasure.hpp GUI/Gizmos/GLGizmoMeasure.hpp
GUI/Gizmos/GLGizmoBrimEars.cpp
GUI/Gizmos/GLGizmoBrimEars.hpp
GUI/Gizmos/GLGizmoAssembly.cpp GUI/Gizmos/GLGizmoAssembly.cpp
GUI/Gizmos/GLGizmoAssembly.hpp GUI/Gizmos/GLGizmoAssembly.hpp
GUI/Gizmos/GLGizmoSeam.cpp GUI/Gizmos/GLGizmoSeam.cpp

View File

@ -1469,6 +1469,8 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject
|| gizmo_type == GLGizmosManager::Seam) || gizmo_type == GLGizmosManager::Seam)
&& ! vol->is_modifier) && ! vol->is_modifier)
vol->force_neutral_color = true; vol->force_neutral_color = true;
else if (gizmo_type == GLGizmosManager::BrimEars)
vol->force_neutral_color = false;
else if (gizmo_type == GLGizmosManager::MmuSegmentation) else if (gizmo_type == GLGizmosManager::MmuSegmentation)
vol->is_active = false; vol->is_active = false;
else if (gizmo_type == GLGizmosManager::Text) { else if (gizmo_type == GLGizmosManager::Text) {
@ -1919,17 +1921,21 @@ void GLCanvas3D::render(bool only_init)
//BBS add partplater rendering logic //BBS add partplater rendering logic
bool only_current = false, only_body = false, show_axes = true, no_partplate = false; bool only_current = false, only_body = false, show_axes = true, no_partplate = false;
bool show_grid = true;
GLGizmosManager::EType gizmo_type = m_gizmos.get_current_type(); GLGizmosManager::EType gizmo_type = m_gizmos.get_current_type();
if (!m_main_toolbar.is_enabled() || m_gizmos.is_show_only_active_plate()) { if (!m_main_toolbar.is_enabled() || m_gizmos.is_show_only_active_plate()) {
//only_body = true; //only_body = true;
if (m_gizmos.get_object_located_outside_plate()) { if (m_gizmos.get_object_located_outside_plate()) {
no_partplate = true; no_partplate = true;
} else { }
else {
only_current = true; only_current = true;
} }
} }
else if ((gizmo_type == GLGizmosManager::FdmSupports) || (gizmo_type == GLGizmosManager::Seam) || (gizmo_type == GLGizmosManager::MmuSegmentation)) else if ((gizmo_type == GLGizmosManager::FdmSupports) || (gizmo_type == GLGizmosManager::Seam) || (gizmo_type == GLGizmosManager::MmuSegmentation))
no_partplate = true; no_partplate = true;
else if (gizmo_type == GLGizmosManager::BrimEars && !camera.is_looking_downward())
show_grid = false;
/* view3D render*/ /* view3D render*/
int hover_id = (m_hover_plate_idxs.size() > 0)?m_hover_plate_idxs.front():-1; int hover_id = (m_hover_plate_idxs.size() > 0)?m_hover_plate_idxs.front():-1;
@ -1941,7 +1947,7 @@ void GLCanvas3D::render(bool only_init)
if (!no_partplate) if (!no_partplate)
_render_bed(!camera.is_looking_downward(), show_axes); _render_bed(!camera.is_looking_downward(), show_axes);
if (!no_partplate) //BBS: add outline logic if (!no_partplate) //BBS: add outline logic
_render_platelist(!camera.is_looking_downward(), only_current, only_body, hover_id, true); _render_platelist(!camera.is_looking_downward(), only_current, only_body, hover_id, true, show_grid);
_render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running()); _render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running());
} }
/* preview render */ /* preview render */
@ -7070,9 +7076,9 @@ void GLCanvas3D::_render_bed_for_picking(bool bottom)
//m_bed.render_for_picking(*this, bottom, scale_factor); //m_bed.render_for_picking(*this, bottom, scale_factor);
} }
void GLCanvas3D::_render_platelist(bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali) const void GLCanvas3D::_render_platelist(bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali, bool show_grid) const
{ {
wxGetApp().plater()->get_partplate_list().render(bottom, only_current, only_body, hover_id, render_cali); wxGetApp().plater()->get_partplate_list().render(bottom, only_current, only_body, hover_id, render_cali, show_grid);
} }
void GLCanvas3D::_render_plates_for_picking() const void GLCanvas3D::_render_plates_for_picking() const
@ -9845,5 +9851,50 @@ void GLCanvas3D::GizmoHighlighter::blink()
invalidate(); invalidate();
} }
const ModelVolume *get_model_volume(const GLVolume &v, const Model &model)
{
const ModelVolume * ret = nullptr;
if (v.object_idx() < (int)model.objects.size()) {
const ModelObject *obj = model.objects[v.object_idx()];
if (v.volume_idx() < (int)obj->volumes.size())
ret = obj->volumes[v.volume_idx()];
}
return ret;
}
ModelVolume *get_model_volume(const ObjectID &volume_id, const ModelObjectPtrs &objects)
{
for (const ModelObject *obj : objects)
for (ModelVolume *vol : obj->volumes)
if (vol->id() == volume_id)
return vol;
return nullptr;
}
ModelVolume *get_model_volume(const GLVolume &v, const ModelObject& object) {
if (v.volume_idx() < 0)
return nullptr;
size_t volume_idx = static_cast<size_t>(v.volume_idx());
if (volume_idx >= object.volumes.size())
return nullptr;
return object.volumes[volume_idx];
}
ModelVolume *get_model_volume(const GLVolume &v, const ModelObjectPtrs &objects)
{
if (v.object_idx() < 0)
return nullptr;
size_t objext_idx = static_cast<size_t>(v.object_idx());
if (objext_idx >= objects.size())
return nullptr;
if (objects[objext_idx] == nullptr)
return nullptr;
return get_model_volume(v, *objects[objext_idx]);
}
} // namespace GUI } // namespace GUI
} // namespace Slic3r } // namespace Slic3r

View File

@ -1123,7 +1123,7 @@ private:
void _render_bed(bool bottom, bool show_axes); void _render_bed(bool bottom, bool show_axes);
void _render_bed_for_picking(bool bottom); void _render_bed_for_picking(bool bottom);
//BBS: add part plate related logic //BBS: add part plate related logic
void _render_platelist(bool bottom, bool only_current, bool only_body = false, int hover_id = -1, bool render_cali = false) const; void _render_platelist(bool bottom, bool only_current, bool only_body = false, int hover_id = -1, bool render_cali = false, bool show_grid = true) const;
void _render_plates_for_picking() const; void _render_plates_for_picking() const;
//BBS: add outline drawing logic //BBS: add outline drawing logic
void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true); void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true);
@ -1226,6 +1226,10 @@ private:
static std::vector<std::array<float, 4>> _parse_colors(const std::vector<std::string>& colors); static std::vector<std::array<float, 4>> _parse_colors(const std::vector<std::string>& colors);
}; };
const ModelVolume *get_model_volume(const GLVolume &v, const Model &model);
ModelVolume *get_model_volume(const ObjectID &volume_id, const ModelObjectPtrs &objects);
ModelVolume *get_model_volume(const GLVolume &v, const ModelObjectPtrs &objects);
ModelVolume *get_model_volume(const GLVolume &v, const ModelObject &object);
} // namespace GUI } // namespace GUI
} // namespace Slic3r } // namespace Slic3r

View File

@ -0,0 +1,904 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoBrimEars.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/Camera.hpp"
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
#include "slic3r/GUI/MainFrame.hpp"
#include "slic3r/Utils/UndoRedo.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
#include <GL/glew.h>
#include <wx/msgdlg.h>
#include <wx/settings.h>
#include <wx/stattext.h>
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/GUI_ObjectSettings.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/NotificationManager.hpp"
#include "slic3r/GUI/MsgDialog.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/SLAPrint.hpp"
namespace Slic3r { namespace GUI {
static ModelVolume *get_model_volume(const Selection &selection, Model &model)
{
const Selection::IndicesList &idxs = selection.get_volume_idxs();
// only one selected volume
if (idxs.size() != 1) return nullptr;
const GLVolume *selected_volume = selection.get_volume(*idxs.begin());
if (selected_volume == nullptr) return nullptr;
const GLVolume::CompositeID &cid = selected_volume->composite_id;
const ModelObjectPtrs &objs = model.objects;
if (cid.object_id < 0 || objs.size() <= static_cast<size_t>(cid.object_id)) return nullptr;
const ModelObject *obj = objs[cid.object_id];
if (cid.volume_id < 0 || obj->volumes.size() <= static_cast<size_t>(cid.volume_id)) return nullptr;
return obj->volumes[cid.volume_id];
}
GLGizmoBrimEars::GLGizmoBrimEars(GLCanvas3D &parent, const std::string &icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) {}
bool GLGizmoBrimEars::on_init()
{
m_shortcut_key = WXK_CONTROL_L;
m_desc["head_diameter"] = _L("Head diameter");
m_desc["max_angle"] = _L("Max angle");
m_desc["detection_radius"] = _L("Detection radius");
m_desc["remove_selected"] = _L("Remove selected points");
m_desc["remove_all"] = _L("Remove all points");
m_desc["auto_generate"] = _L("Auto-generate points");
m_desc["section_view"] = _L("Section view");
m_desc["left_click_caption"] = _L("Left click");
m_desc["left_click"] = _L("Add a brim ear");
m_desc["right_click_caption"] = L("Right click");
m_desc["right_click"] = _L("Delete a brim ear");
m_desc["ctrl_mouse_wheel_caption"] = _L("Ctrl+Mouse wheel");
m_desc["ctrl_mouse_wheel"] = _L("Adjust section view");
return true;
}
void GLGizmoBrimEars::set_brim_data(ModelObject *model_object, const Selection &selection)
{
if (!m_c->selection_info()) return;
ModelObject *mo = m_c->selection_info()->model_object();
if (m_state == On && mo && mo->id() != m_old_mo_id) {
reload_cache();
m_old_mo_id = mo->id();
}
}
void GLGizmoBrimEars::on_render()
{
ModelObject *mo = m_c->selection_info()->model_object();
const Selection &selection = m_parent.get_selection();
// If current m_c->m_model_object does not match selection, ask GLCanvas3D to turn us off
if (m_state == On && (mo != selection.get_model()->objects[selection.get_object_idx()] || m_c->selection_info()->get_active_instance() != selection.get_instance_idx())) {
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_RESETGIZMOS));
return;
}
glsafe(::glEnable(GL_BLEND));
glsafe(::glEnable(GL_DEPTH_TEST));
if (selection.is_from_single_instance()) render_points(selection, false);
m_selection_rectangle.render(m_parent);
m_c->object_clipper()->render_cut();
glsafe(::glDisable(GL_BLEND));
}
void GLGizmoBrimEars::on_render_for_picking()
{
const Selection &selection = m_parent.get_selection();
// glsafe(::glEnable(GL_DEPTH_TEST));
render_points(selection, true);
}
void GLGizmoBrimEars::render_points(const Selection &selection, bool picking) const
{
auto editing_cache = m_editing_cache;
if (render_hover_point != nullptr) { editing_cache.push_back(*render_hover_point); }
size_t cache_size = editing_cache.size();
bool has_points = (cache_size != 0);
if (!has_points) return;
GLShaderProgram *shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light");
if (shader != nullptr) shader->start_using();
ScopeGuard guard([shader]() {
if (shader != nullptr) shader->stop_using();
});
const GLVolume *vol = selection.get_volume(*selection.get_volume_idxs().begin());
const Transform3d &instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse();
const Transform3d &instance_matrix = vol->get_instance_transformation().get_matrix();
glsafe(::glPushMatrix());
glsafe(::glMultMatrixd(instance_matrix.data()));
std::array<float, 4> render_color;
for (size_t i = 0; i < cache_size; ++i) {
const BrimPoint &brim_point = editing_cache[i].brim_point;
const bool &point_selected = editing_cache[i].selected;
const bool &hover = editing_cache[i].is_hover;
// keep show brim ear
// if (is_mesh_point_clipped(brim_point.pos.cast<double>()))
// continue;
// First decide about the color of the point.
if (hover) {
render_color = {0.7f, 0.7f, 0.7f, 0.5f};
} else {
if (picking)
render_color = picking_color_component(i);
else {
if (size_t(m_hover_id) == i) // ignore hover state unless editing mode is active
render_color = {0.f, 1.f, 1.f, 1.f};
else { // neigher hover nor picking
if (point_selected)
render_color = {1.f, 0.3f, 0.3f, 1.f};
else
render_color = {0.7f, 0.7f, 0.7f, 1.f};
}
}
}
const_cast<GLModel *>(&m_cylinder)->set_color(-1, render_color);
if (shader && !picking) shader->set_uniform("emission_factor", 0.5f);
// Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
glsafe(::glPushMatrix());
glsafe(::glTranslatef(brim_point.pos(0), brim_point.pos(1), brim_point.pos(2)));
glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data()));
if (vol->is_left_handed()) glFrontFace(GL_CW);
// Matrices set, we can render the point mark now.
// If in editing mode, we'll also render a cone pointing to the sphere.
if (editing_cache[i].normal == Vec3f::Zero()) m_c->raycaster()->raycaster()->get_closest_point(editing_cache[i].brim_point.pos, &editing_cache[i].normal);
Eigen::Quaterniond q;
q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * editing_cache[i].normal.cast<double>());
Eigen::AngleAxisd aa(q);
glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2)));
glsafe(::glPushMatrix());
double radius = (double) brim_point.head_front_radius * RenderPointScale;
glsafe(::glScaled(radius, radius, .2));
m_cylinder.render();
glsafe(::glPopMatrix());
if (vol->is_left_handed()) glFrontFace(GL_CCW);
glsafe(::glPopMatrix());
}
glsafe(::glPopMatrix());
}
bool GLGizmoBrimEars::is_mesh_point_clipped(const Vec3d &point) const
{
if (m_c->object_clipper()->get_position() == 0.) return false;
auto sel_info = m_c->selection_info();
int active_inst = m_c->selection_info()->get_active_instance();
const ModelInstance *mi = sel_info->model_object()->instances[active_inst];
const Transform3d &trafo = mi->get_transformation().get_matrix();
Vec3d transformed_point = trafo * point;
transformed_point(2) += sel_info->get_sla_shift();
return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point);
}
bool GLGizmoBrimEars::unproject_on_mesh2(const Vec2d &mouse_pos, std::pair<Vec3f, Vec3f> &pos_and_normal)
{
const Camera &camera = wxGetApp().plater()->get_camera();
double clp_dist = m_c->object_clipper()->get_position();
const ClippingPlane *clp = m_c->object_clipper()->get_clipping_plane();
bool mouse_on_object = false;
Vec3f position_on_model;
Vec3f normal_on_model;
double closest_hit_distance = std::numeric_limits<double>::max();
for (auto item : m_mesh_raycaster_map) {
auto &raycaster = item.second->mesh_raycaster;
auto &world_tran = item.second->world_tran;
Vec3f normal = Vec3f::Zero();
Vec3f hit = Vec3f::Zero();
if (raycaster->unproject_on_mesh(mouse_pos, world_tran.get_matrix(), camera, hit, normal, clp_dist != 0. ? clp : nullptr)) {
double hit_squared_distance = (camera.get_position() - world_tran.get_matrix() * hit.cast<double>()).norm();
if (hit_squared_distance < closest_hit_distance) {
closest_hit_distance = hit_squared_distance;
mouse_on_object = true;
m_last_hit_volume = item.first;
auto volum_trsf = m_last_hit_volume->get_volume_transformation().get_matrix();
position_on_model = (m_last_hit_volume->get_volume_transformation().get_matrix() * hit.cast<double>()).cast<float>();
normal_on_model = normal;
}
}
}
pos_and_normal = std::make_pair(position_on_model, normal_on_model);
return mouse_on_object;
}
// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal
// Return false if no intersection was found, true otherwise.
bool GLGizmoBrimEars::unproject_on_mesh(const Vec2d &mouse_pos, std::pair<Vec3f, Vec3f> &pos_and_normal)
{
if (m_c->raycaster()->raycasters().size() != 1) return false;
if (!m_c->raycaster()->raycaster()) return false;
const Camera &camera = wxGetApp().plater()->get_camera();
const Selection &selection = m_parent.get_selection();
const GLVolume *volume = selection.get_volume(*selection.get_volume_idxs().begin());
Geometry::Transformation trafo = volume->get_instance_transformation();
// trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift()));//sla shift看起来可以删掉
double clp_dist = m_c->object_clipper()->get_position();
const ClippingPlane *clp = m_c->object_clipper()->get_clipping_plane();
// The raycaster query
Vec3f hit;
Vec3f normal;
if (m_c->raycaster()->raycaster()->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, clp_dist != 0. ? clp : nullptr)) {
pos_and_normal = std::make_pair(hit, normal);
return true;
}
return false;
}
void GLGizmoBrimEars::data_changed(bool is_serializing)
{
if (!m_c->selection_info()) return;
ModelObject *mo = m_c->selection_info()->model_object();
if (mo) {
reset_all_pick();
register_single_mesh_pick();
}
}
// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event.
// The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is
// aware that the event was reacted to and stops trying to make different sense of it. If the gizmo
// concludes that the event was not intended for it, it should return false.
bool GLGizmoBrimEars::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down)
{
ModelObject *mo = m_c->selection_info()->model_object();
int active_inst = m_c->selection_info()->get_active_instance();
if (action == SLAGizmoEventType::Moving) {
// First check that the mouse pointer is on an object.
const Selection &selection = m_parent.get_selection();
const ModelInstance *mi = mo->instances[0];
Plater *plater = wxGetApp().plater();
if (!plater) return false;
const Camera &camera = wxGetApp().plater()->get_camera();
const GLVolume *volume = selection.get_volume(*selection.get_volume_idxs().begin());
Transform3d inverse_trsf = volume->get_instance_transformation().get_matrix(true).inverse();
std::pair<Vec3f, Vec3f> pos_and_normal;
if (unproject_on_mesh2(mouse_position, pos_and_normal)) {
render_hover_point = new CacheEntry(BrimPoint(pos_and_normal.first, m_new_point_head_diameter / 2.f), false, (inverse_trsf * m_world_normal).cast<float>(), true);
} else {
delete render_hover_point;
render_hover_point = nullptr;
}
} else if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) {
// left down with shift - show the selection rectangle:
if (m_hover_id == -1) {
if (shift_down || alt_down) { m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect); }
} else {
if (m_editing_cache[m_hover_id].selected)
unselect_point(m_hover_id);
else {
if (!alt_down) select_point(m_hover_id);
}
}
return true;
}
// left down without selection rectangle - place point on the mesh:
if (action == SLAGizmoEventType::LeftDown && !m_selection_rectangle.is_dragging() && !shift_down) {
// If any point is in hover state, this should initiate its move - return control back to GLCanvas:
if (m_hover_id != -1) return false;
// If there is some selection, don't add new point and deselect everything instead.
if (m_selection_empty) {
std::pair<Vec3f, Vec3f> pos_and_normal;
if (unproject_on_mesh2(mouse_position, pos_and_normal)) {
// we got an intersection
const Selection &selection = m_parent.get_selection();
const GLVolume *volume = selection.get_volume(*selection.get_volume_idxs().begin());
Transform3d trsf = volume->get_instance_transformation().get_matrix();
Transform3d inverse_trsf = volume->get_instance_transformation().get_matrix(true).inverse();
// BBS brim ear postion is placed on the bottom side
Vec3d world_pos = trsf * pos_and_normal.first.cast<double>();
world_pos[2] = -0.0001;
Vec3d object_pos = trsf.inverse() * world_pos;
// brim ear always face up
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Add brim ear");
add_point_to_cache(object_pos.cast<float>(), m_new_point_head_diameter / 2.f, false, (inverse_trsf * m_world_normal).cast<float>());
m_parent.set_as_dirty();
m_wait_for_up_event = true;
} else
return false;
} else
select_point(NoPoints);
return true;
}
// left up with selection rectangle - select points inside the rectangle:
if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp || action == SLAGizmoEventType::AltUp) && m_selection_rectangle.is_dragging()) {
// Is this a selection or deselection rectangle?
GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state();
// First collect positions of all the points in world coordinates.
Geometry::Transformation trafo = mo->instances[active_inst]->get_transformation();
// trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift()));
std::vector<Vec3d> points;
for (unsigned int i = 0; i < m_editing_cache.size(); ++i) points.push_back(trafo.get_matrix() * m_editing_cache[i].brim_point.pos.cast<double>());
// Now ask the rectangle which of the points are inside.
std::vector<Vec3f> points_inside;
std::vector<unsigned int> points_idxs = m_selection_rectangle.stop_dragging(m_parent, points);
for (size_t idx : points_idxs) points_inside.push_back(points[idx].cast<float>());
// Only select/deselect points that are actually visible. We want to check not only
// the point itself, but also the center of base of its cone, so the points don't hide
// under every miniature irregularity on the model. Remember the actual number and
// append the cone bases.
size_t orig_pts_num = points_inside.size();
for (size_t idx : points_idxs)
points_inside.emplace_back((trafo.get_matrix().cast<float>() * (m_editing_cache[idx].brim_point.pos + m_editing_cache[idx].normal)).cast<float>());
for (size_t idx :
m_c->raycaster()->raycaster()->get_unobscured_idxs(trafo, wxGetApp().plater()->get_camera(), points_inside, m_c->object_clipper()->get_clipping_plane())) {
if (idx >= orig_pts_num) // this is a cone-base, get index of point it belongs to
idx -= orig_pts_num;
if (rectangle_status == GLSelectionRectangle::Deselect)
unselect_point(points_idxs[idx]);
else
select_point(points_idxs[idx]);
}
return true;
}
// left up with no selection rectangle
if (action == SLAGizmoEventType::LeftUp) {
if (m_wait_for_up_event) { m_wait_for_up_event = false; }
return true;
}
// dragging the selection rectangle:
if (action == SLAGizmoEventType::Dragging) {
if (m_wait_for_up_event)
return true; // point has been placed and the button not released yet
// this prevents GLCanvas from starting scene rotation
if (m_selection_rectangle.is_dragging()) {
m_selection_rectangle.dragging(mouse_position);
return true;
}
return false;
}
if (action == SLAGizmoEventType::Delete) {
// delete key pressed
delete_selected_points();
return true;
}
if (action == SLAGizmoEventType::RightDown) {
if (m_hover_id != -1) {
select_point(NoPoints);
select_point(m_hover_id);
delete_selected_points();
return true;
}
return false;
}
if (action == SLAGizmoEventType::SelectAll) {
select_point(AllPoints);
return true;
}
// mouse wheel up
if (action == SLAGizmoEventType::MouseWheelUp && control_down) {
double pos = m_c->object_clipper()->get_position();
pos = std::min(1., pos + 0.01);
m_c->object_clipper()->set_position(pos, false, true);
return true;
}
if (action == SLAGizmoEventType::MouseWheelDown && control_down) {
double pos = m_c->object_clipper()->get_position();
pos = std::max(0., pos - 0.01);
m_c->object_clipper()->set_position(pos, false, true);
return true;
}
// reset clipper position
if (action == SLAGizmoEventType::ResetClippingPlane) {
m_c->object_clipper()->set_position(-1., false);
return true;
}
return false;
}
void GLGizmoBrimEars::delete_selected_points()
{
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Delete brim ear");
for (unsigned int idx = 0; idx < m_editing_cache.size(); ++idx) {
if (m_editing_cache[idx].selected) { m_editing_cache.erase(m_editing_cache.begin() + (idx--)); }
}
select_point(NoPoints);
}
void GLGizmoBrimEars::on_update(const UpdateData &data)
{
if (m_hover_id != -1) {
std::pair<Vec3f, Vec3f> pos_and_normal;
if (!unproject_on_mesh(data.mouse_pos.cast<double>(), pos_and_normal)) return;
m_editing_cache[m_hover_id].brim_point.pos[0] = pos_and_normal.first.x();
m_editing_cache[m_hover_id].brim_point.pos[1] = pos_and_normal.first.y();
m_editing_cache[m_hover_id].normal = pos_and_normal.second;
}
}
std::vector<const ConfigOption *> GLGizmoBrimEars::get_config_options(const std::vector<std::string> &keys) const
{
std::vector<const ConfigOption *> out;
const ModelObject *mo = m_c->selection_info()->model_object();
if (!mo) return out;
const DynamicPrintConfig &object_cfg = mo->config.get();
const DynamicPrintConfig &print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
std::unique_ptr<DynamicPrintConfig> default_cfg = nullptr;
for (const std::string &key : keys) {
if (object_cfg.has(key))
out.push_back(object_cfg.option(key));
else if (print_cfg.has(key))
out.push_back(print_cfg.option(key));
else { // we must get it from defaults
if (default_cfg == nullptr) default_cfg.reset(DynamicPrintConfig::new_from_defaults_keys(keys));
out.push_back(default_cfg->option(key));
}
}
return out;
}
void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limit)
{
static float last_y = 0.0f;
static float last_h = 0.0f;
ModelObject *mo = m_c->selection_info()->model_object();
if (!mo) return;
const float win_h = ImGui::GetWindowHeight();
y = std::min(y, bottom_limit - win_h);
GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f);
const float currt_scale = m_parent.get_scale();
ImGuiWrapper::push_toolbar_style(currt_scale);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0 * currt_scale, 5.0 * currt_scale));
ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 4.0f * currt_scale);
GizmoImguiBegin(get_name(),
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
float space_size = m_imgui->get_style_scaling() * 8;
std::vector<wxString> text_list = {m_desc["head_diameter"], m_desc["max_angle"], m_desc["detection_radius"], m_desc["clipping_of_view"]};
float widest_text = m_imgui->find_widest_text(text_list);
float caption_size = widest_text + space_size + ImGui::GetStyle().WindowPadding.x;
float input_text_size = m_imgui->scaled(10.0f);
float button_size = ImGui::GetFrameHeight();
float selectable_size = input_text_size + ImGui::GetFrameHeight() * 2;
float list_width = selectable_size + ImGui::GetStyle().ScrollbarSize + 2 * currt_scale;
const float slider_icon_width = m_imgui->get_slider_icon_size().x;
const float slider_width = list_width - space_size;
const float drag_left_width = caption_size + slider_width + space_size;
// adjust window position to avoid overlap the view toolbar
if (last_h != win_h || last_y != y) {
// ask canvas for another frame to render the window in the correct position
m_imgui->set_requires_extra_frame();
if (last_h != win_h) last_h = win_h;
if (last_y != y) last_y = y;
}
ImGui::AlignTextToFramePadding();
// Following is a nasty way to:
// - save the initial value of the slider before one starts messing with it
// - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene
// - take correct undo/redo snapshot after the user is done with moving the slider
float initial_value = m_new_point_head_diameter;
m_imgui->text(m_desc["head_diameter"]);
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(slider_width);
m_imgui->bbl_slider_float_style("##head_diameter", &m_new_point_head_diameter, 5, 10, "%.1f", 1.0f, true);
if (m_imgui->get_last_slider_status().clicked) {
if (m_old_point_head_diameter == 0.f) m_old_point_head_diameter = initial_value;
}
if (m_imgui->get_last_slider_status().edited) {
for (auto &cache_entry : m_editing_cache)
if (cache_entry.selected) cache_entry.brim_point.head_front_radius = m_new_point_head_diameter / 2.f;
}
if (m_imgui->get_last_slider_status().deactivated_after_edit) {
// momentarily restore the old value to take snapshot
for (auto &cache_entry : m_editing_cache)
if (cache_entry.selected) cache_entry.brim_point.head_front_radius = m_old_point_head_diameter / 2.f;
float backup = m_new_point_head_diameter;
m_new_point_head_diameter = m_old_point_head_diameter;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Change point head diameter");
m_new_point_head_diameter = backup;
for (auto &cache_entry : m_editing_cache)
if (cache_entry.selected) cache_entry.brim_point.head_front_radius = m_new_point_head_diameter / 2.f;
m_old_point_head_diameter = 0.f;
}
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##head_diameter_input", &m_new_point_head_diameter, 0.05f, 0.0f, 0.0f, "%.1f");
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc["max_angle"]);
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(slider_width);
m_imgui->bbl_slider_float_style("##max_angle", &m_max_angle, 0, 180, "%.1f", 1.0f, true);
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##max_angle_input", &m_max_angle, 0.05f, 0.0f, 180.0f, "%.1f");
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc["detection_radius"]);
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(slider_width);
m_imgui->bbl_slider_float_style("##detection_radius", &m_detection_radius, 0, 200, "%.1f", 1.0f, true);
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
ImGui::BBLDragFloat("##detection_radius_input", &m_detection_radius, 0.05f, 0.0f, 200.0f, "%.1f");
ImGui::Separator();
float clp_dist = float(m_c->object_clipper()->get_position());
m_imgui->text(m_desc["section_view"]);
ImGui::SameLine(caption_size);
ImGui::PushItemWidth(slider_width);
bool slider_clp_dist = m_imgui->bbl_slider_float_style("##section_view", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true);
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##section_view_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position(clp_dist, false, true); }
ImGui::Separator();
// ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f));
float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y;
show_tooltip_information(x, get_cur_y);
float f_scale = m_parent.get_gizmos_manager().get_layout_scale();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
ImGui::SameLine();
if (m_imgui->button(m_desc["auto_generate"])) { auto_generate(); }
ImGui::SameLine();
if (m_imgui->button(m_desc["remove_selected"])) { delete_selected_points(); }
float font_size = ImGui::GetFontSize();
ImGui::Dummy(ImVec2(font_size * 1.8, font_size * 1.3));
ImGui::SameLine();
if (m_imgui->button(m_desc["remove_all"])) {
if (m_editing_cache.size() > 0) {
select_point(AllPoints);
delete_selected_points();
}
}
ImGui::PopStyleVar(1);
GizmoImguiEnd();
ImGui::PopStyleVar(2);
ImGuiWrapper::pop_toolbar_style();
}
void GLGizmoBrimEars::show_tooltip_information(float x, float y)
{
std::array<std::string, 3> info_array = std::array<std::string, 3>{"left_click", "right_click", "ctrl_mouse_wheel"};
float caption_max = 0.f;
for (const auto &t : info_array) { caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x); }
ImTextureID normal_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP);
ImTextureID hover_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER);
caption_max += m_imgui->calc_text_size(": ").x + 35.f;
float font_size = ImGui::GetFontSize();
ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {0, ImGui::GetStyle().FramePadding.y});
ImGui::ImageButton3(normal_id, hover_id, button_size);
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip2(ImVec2(x, y));
auto draw_text_with_caption = [this, &caption_max](const wxString &caption, const wxString &text) {
m_imgui->text_colored(ImGuiWrapper::COL_ACTIVE, caption);
ImGui::SameLine(caption_max);
m_imgui->text_colored(ImGuiWrapper::COL_WINDOW_BG, text);
};
for (const auto &t : info_array) draw_text_with_caption(m_desc.at(t + "_caption") + ": ", m_desc.at(t));
ImGui::EndTooltip();
}
ImGui::PopStyleVar(2);
}
bool GLGizmoBrimEars::on_is_activable() const
{
const Selection &selection = m_parent.get_selection();
if (!selection.is_from_single_instance()) return false;
// Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside.
const Selection::IndicesList &list = selection.get_volume_idxs();
for (const auto &idx : list)
if (selection.get_volume(idx)->is_outside && selection.get_volume(idx)->composite_id.volume_id >= 0) return false;
return true;
}
std::string GLGizmoBrimEars::on_get_name() const { return _u8L("Brim Ears"); }
CommonGizmosDataID GLGizmoBrimEars::on_get_requirements() const
{
return CommonGizmosDataID(int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::InstancesHider) | int(CommonGizmosDataID::Raycaster) |
int(CommonGizmosDataID::ObjectClipper));
}
// switch gizmos
void GLGizmoBrimEars::on_set_state()
{
if (m_state == m_old_state) return;
if (m_state == On && m_old_state != On) {
// the gizmo was just turned on
wxGetApp().plater()->enter_gizmos_stack();
m_new_point_head_diameter = 5.0f;
first_layer_slicer();
}
if (m_state == Off && m_old_state != Off) {
// the gizmo was just turned Off
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Brim ears edit");
ModelObject *mo = m_c->selection_info()->model_object();
mo->brim_points.clear();
for (const CacheEntry &ce : m_editing_cache) mo->brim_points.emplace_back(ce.brim_point);
wxGetApp().plater()->leave_gizmos_stack();
// wxGetApp().mainframe->update_slice_print_status(MainFrame::SlicePrintEventType::eEventSliceUpdate, true, true);
}
m_old_state = m_state;
}
void GLGizmoBrimEars::on_start_dragging()
{
if (m_hover_id != -1) {
select_point(NoPoints);
select_point(m_hover_id);
m_point_before_drag = m_editing_cache[m_hover_id];
} else
m_point_before_drag = CacheEntry();
}
void GLGizmoBrimEars::on_stop_dragging()
{
if (m_hover_id != -1) {
CacheEntry backup = m_editing_cache[m_hover_id];
if (m_point_before_drag.brim_point.pos != Vec3f::Zero() // some point was touched
&& backup.brim_point.pos != m_point_before_drag.brim_point.pos) // and it was moved, not just selected
{
m_editing_cache[m_hover_id] = m_point_before_drag;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Move support point");
m_editing_cache[m_hover_id] = backup;
}
}
m_point_before_drag = CacheEntry();
}
void GLGizmoBrimEars::on_load(cereal::BinaryInputArchive &ar) { ar(m_new_point_head_diameter, m_editing_cache, m_selection_empty); }
void GLGizmoBrimEars::on_save(cereal::BinaryOutputArchive &ar) const { ar(m_new_point_head_diameter, m_editing_cache, m_selection_empty); }
void GLGizmoBrimEars::select_point(int i)
{
if (i == AllPoints || i == NoPoints) {
for (auto &point_and_selection : m_editing_cache) point_and_selection.selected = (i == AllPoints);
m_selection_empty = (i == NoPoints);
if (i == AllPoints) m_new_point_head_diameter = m_editing_cache[0].brim_point.head_front_radius * 2.f;
} else {
m_editing_cache[i].selected = true;
m_selection_empty = false;
m_new_point_head_diameter = m_editing_cache[i].brim_point.head_front_radius * 2.f;
}
}
void GLGizmoBrimEars::unselect_point(int i)
{
m_editing_cache[i].selected = false;
m_selection_empty = true;
for (const CacheEntry &ce : m_editing_cache) {
if (ce.selected) {
m_selection_empty = false;
break;
}
}
}
void GLGizmoBrimEars::reload_cache()
{
const ModelObject *mo = m_c->selection_info()->model_object();
m_editing_cache.clear();
for (const BrimPoint &point : mo->brim_points) m_editing_cache.emplace_back(point);
}
Points GLGizmoBrimEars::generate_points(Polygon &obj_polygon, float ear_detection_length, float brim_ears_max_angle, bool is_outer)
{
const coordf_t angle_threshold = (180 - brim_ears_max_angle) * PI / 180.0;
Points pt_ears;
if (ear_detection_length > 0) {
Points points = obj_polygon.points;
points.push_back(points.front());
points = MultiPoint::_douglas_peucker(points, ear_detection_length);
if (points.size() > 4) {
points.erase(points.end() - 1);
obj_polygon.points = points;
}
}
append(pt_ears, is_outer ? obj_polygon.convex_points(angle_threshold) : obj_polygon.concave_points(angle_threshold));
return pt_ears;
}
void GLGizmoBrimEars::first_layer_slicer()
{
const Selection &selection = m_parent.get_selection();
const Selection::IndicesList &idxs = selection.get_volume_idxs();
if (idxs.size() <= 0) return;
std::vector<float> slice_height(1, 0.1);
MeshSlicingParamsEx params;
params.mode = MeshSlicingParams::SlicingMode::Regular;
params.closing_radius = 0.1f;
params.extra_offset = 0.05f;
params.resolution = 0.01;
ExPolygons part_ex;
ExPolygons negative_ex;
for (auto idx : idxs) {
const GLVolume *volume = selection.get_volume(idx);
const ModelVolume *model_volume = get_model_volume(*volume, wxGetApp().model());
if (model_volume == nullptr) continue;
if (model_volume->type() == ModelVolumeType::MODEL_PART || model_volume->type() == ModelVolumeType::NEGATIVE_VOLUME) {
indexed_triangle_set volume_its = model_volume->mesh().its;
if (volume_its.indices.size() <= 0) continue;
Transform3d trsf = volume->get_instance_transformation().get_matrix() * volume->get_volume_transformation().get_matrix();
MeshSlicingParamsEx params_ex(params);
params_ex.trafo = params_ex.trafo * trsf;
if (params_ex.trafo.rotation().determinant() < 0.) its_flip_triangles(volume_its);
ExPolygons sliced_layer = slice_mesh_ex(volume_its, slice_height, params_ex).front();
if (model_volume->type() == ModelVolumeType::MODEL_PART) {
part_ex = union_ex(part_ex, sliced_layer);
} else {
negative_ex = union_ex(negative_ex, sliced_layer);
}
}
}
m_first_layer = diff_ex(part_ex, negative_ex);
}
void GLGizmoBrimEars::auto_generate()
{
/*ModelObject* mo = m_c->selection_info()->model_object();
std::vector<float> slice_height(1, 0.1);
const Selection& selection = m_parent.get_selection();
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
Transform3d trsf = volume->get_instance_transformation().get_matrix();
const ModelVolume* model_volume = get_model_volume(selection, wxGetApp().model());
Vec3f normal = (volume->get_instance_transformation().get_matrix(true).inverse() * m_world_normal).cast<float>();
indexed_triangle_set volume_its = model_volume->mesh().its;
if (volume_its.indices.size() <= 0)
return;
MeshSlicingParamsEx params;
params.mode = MeshSlicingParams::SlicingMode::Regular;
params.closing_radius = 0.1f;
params.extra_offset = 0.05f;
params.resolution = 0.01;
params.trafo = params.trafo * trsf;
if (params.trafo.rotation().determinant() < 0.)
its_flip_triangles(volume_its);
ExPolygons sliced_layer = slice_mesh_ex(volume_its, slice_height, params).front();*/
const Selection &selection = m_parent.get_selection();
const GLVolume *volume = selection.get_volume(*selection.get_volume_idxs().begin());
Transform3d trsf = volume->get_instance_transformation().get_matrix();
Vec3f normal = (volume->get_instance_transformation().get_matrix(true).inverse() * m_world_normal).cast<float>();
auto add_point = [this, &trsf, &normal](const Point &p) {
Vec3d world_pos = {float(p.x() * SCALING_FACTOR), float(p.y() * SCALING_FACTOR), -0.0001};
Vec3d object_pos = trsf.inverse() * world_pos;
// m_editing_cache.emplace_back(BrimPoint(object_pos.cast<float>(), m_new_point_head_diameter / 2), false, normal);
add_point_to_cache(object_pos.cast<float>(), m_new_point_head_diameter / 2, false, normal);
};
for (const ExPolygon &ex_poly : m_first_layer) {
Polygon out_poly = ex_poly.contour;
Polygons inner_poly = ex_poly.holes;
polygons_reverse(inner_poly);
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Auto generate brim ear");
Points out_points = generate_points(out_poly, m_detection_radius, m_max_angle, true);
for (Point &p : out_points) { add_point(p); }
for (Polygon &pl : inner_poly) {
Points inner_points = generate_points(pl, m_detection_radius, m_max_angle, false);
for (Point &p : inner_points) { add_point(p); }
}
}
}
bool GLGizmoBrimEars::add_point_to_cache(Vec3f pos, float head_radius, bool selected, Vec3f normal)
{
BrimPoint point(pos, head_radius);
for (int i = 0; i < m_editing_cache.size(); i++) {
if (m_editing_cache[i].brim_point == point) { return false; }
}
m_editing_cache.emplace_back(point, selected, normal);
return true;
}
void GLGizmoBrimEars::register_single_mesh_pick()
{
Selection &selection = m_parent.get_selection();
const Selection::IndicesList &idxs = selection.get_volume_idxs();
if (idxs.size() > 0) {
for (unsigned int idx : idxs) {
GLVolume *v = const_cast<GLVolume *>(selection.get_volume(idx));
auto world_tran = v->get_instance_transformation() * v->get_volume_transformation();
auto mesh = const_cast<TriangleMesh *>(v->ori_mesh);
if (m_mesh_raycaster_map.find(v) != m_mesh_raycaster_map.end()) {
m_mesh_raycaster_map[v]->world_tran.set_from_transform(world_tran.get_matrix());
} else {
m_mesh_raycaster_map[v] = std::make_shared<PickRaycaster>(mesh, -1);
m_mesh_raycaster_map[v]->world_tran.set_from_transform(world_tran.get_matrix());
}
}
}
}
void GLGizmoBrimEars::update_single_mesh_pick(GLVolume *v)
{
if (m_mesh_raycaster_map.find(v) != m_mesh_raycaster_map.end()) {
auto world_tran = v->get_instance_transformation() * v->get_volume_transformation();
m_mesh_raycaster_map[v]->world_tran.set_from_transform(world_tran.get_matrix());
}
}
void GLGizmoBrimEars::reset_all_pick() { std::map<GLVolume *, std::shared_ptr<PickRaycaster>>().swap(m_mesh_raycaster_map); }
}} // namespace Slic3r::GUI

View File

@ -0,0 +1,163 @@
#ifndef slic3r_GLGizmoBrimEars_hpp_
#define slic3r_GLGizmoBrimEars_hpp_
#include "GLGizmoBase.hpp"
#include "slic3r/GUI/GLSelectionRectangle.hpp"
#include "libslic3r/BrimEarsPoint.hpp"
#include "libslic3r/ObjectID.hpp"
#include <wx/dialog.h>
#include <cereal/types/vector.hpp>
namespace Slic3r {
class ConfigOption;
namespace GUI {
enum class SLAGizmoEventType : unsigned char;
class GLGizmoBrimEars : public GLGizmoBase
{
private:
bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
bool unproject_on_mesh2(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
const float RenderPointScale = 1.f;
class CacheEntry {
public:
CacheEntry() :
brim_point(BrimPoint()), selected(false), normal(Vec3f::Zero()), is_hover(false) {}
CacheEntry(const BrimPoint& point, bool sel = false, const Vec3f& norm = Vec3f::Zero(), bool hover = false) :
brim_point(point), selected(sel), normal(norm), is_hover(hover) {}
bool operator==(const CacheEntry& rhs) const {
return (brim_point == rhs.brim_point);
}
bool operator!=(const CacheEntry& rhs) const {
return ! ((*this) == rhs);
}
inline bool pos_is_zero() {
return brim_point.pos.isZero();
}
void set_empty() {
brim_point = BrimPoint();
selected = false;
normal.setZero();
is_hover = false;
}
BrimPoint brim_point;
bool selected; // whether the point is selected
bool is_hover; // show mouse hover cylinder
Vec3f normal;
template<class Archive>
void serialize(Archive & ar)
{
ar(brim_point, selected, normal);
}
};
public:
GLGizmoBrimEars(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
virtual ~GLGizmoBrimEars() = default;
void data_changed(bool is_serializing) override;
void set_brim_data(ModelObject* model_object, const Selection& selection);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
void delete_selected_points();
//ClippingPlane get_sla_clipping_plane() const;
bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); }
bool wants_enter_leave_snapshots() const override { return true; }
std::string get_gizmo_entering_text() const override { return "Entering Brim Ears"; }
std::string get_gizmo_leaving_text() const override { return "Leaving Brim Ears"; }
private:
bool on_init() override;
void on_update(const UpdateData& data) override;
void on_render() override;
void on_render_for_picking() override;
void render_points(const Selection& selection, bool picking = false) const;
float m_new_point_head_diameter; // Size of a new point.
float m_max_angle = 125.f;
float m_detection_radius = 1.f;
CacheEntry m_point_before_drag; // undo/redo - so we know what state was edited
float m_old_point_head_diameter = 0.; // the same
mutable std::vector<CacheEntry> m_editing_cache; // a support point and whether it is currently selectedchanges or undo/redo
ObjectID m_old_mo_id;
const Vec3d m_world_normal = {0, 0, 1};
std::map<GLVolume*, std::shared_ptr<PickRaycaster>> m_mesh_raycaster_map;
GLVolume* m_last_hit_volume;
CacheEntry* render_hover_point = nullptr;
// This map holds all translated description texts, so they can be easily referenced during layout calculations
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
std::map<std::string, wxString> m_desc;
GLSelectionRectangle m_selection_rectangle;
ExPolygons m_first_layer;
bool m_wait_for_up_event = false;
bool m_selection_empty = true;
EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
std::vector<const ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
bool is_mesh_point_clipped(const Vec3d& point) const;
// Methods that do the model_object and editing cache synchronization,
// editing mode selection, etc:
enum {
AllPoints = -2,
NoPoints,
};
void select_point(int i);
void unselect_point(int i);
void reload_cache();
Points generate_points(Polygon &obj_polygon, float ear_detection_length, float brim_ears_max_angle, bool is_outer);
void auto_generate();
void first_layer_slicer();
protected:
void on_set_state() override;
void on_set_hover_id() override
{
if ((int)m_editing_cache.size() <= m_hover_id)
m_hover_id = -1;
}
void on_start_dragging() override;
void on_stop_dragging() override;
void on_render_input_window(float x, float y, float bottom_limit) override;
void show_tooltip_information(float x, float y);
std::string on_get_name() const override;
std::string on_get_name_str() override { return "Brim Ears"; }
bool on_is_activable() const override;
//bool on_is_selectable() const override;
virtual CommonGizmosDataID on_get_requirements() const override;
void on_load(cereal::BinaryInputArchive& ar) override;
void on_save(cereal::BinaryOutputArchive& ar) const override;
void register_single_mesh_pick();
void update_single_mesh_pick(GLVolume* v);
void reset_all_pick();
bool add_point_to_cache(Vec3f pos, float head_radius, bool selected, Vec3f normal);
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmoBrimEars_hpp_

View File

@ -463,7 +463,7 @@ void CommonGizmosDataObjects::ObjectClipper::set_behaviour(bool hide_clipped, bo
clipper.first->set_behaviour(fill_cut, contour_width); clipper.first->set_behaviour(fill_cut, contour_width);
} }
void ObjectClipper::set_position(double pos, bool keep_normal) void ObjectClipper::set_position(double pos, bool keep_normal, bool vertical_normal)
{ {
const ModelObject *mo = get_pool()->selection_info()->model_object(); const ModelObject *mo = get_pool()->selection_info()->model_object();
int active_inst = get_pool()->selection_info()->get_active_instance(); int active_inst = get_pool()->selection_info()->get_active_instance();
@ -471,7 +471,12 @@ void ObjectClipper::set_position(double pos, bool keep_normal)
if (active_inst < 0) { if (active_inst < 0) {
return; return;
} }
Vec3d normal = (keep_normal && m_clp) ? m_clp->get_normal() : -wxGetApp().plater()->get_camera().get_dir_forward(); Vec3d normal;
if(vertical_normal) {
normal = {0, 0, 1};
}else {
normal = (keep_normal && m_clp) ? m_clp->get_normal() : -wxGetApp().plater()->get_camera().get_dir_forward();
}
Vec3d center; Vec3d center;
if (get_pool()->get_canvas()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) { if (get_pool()->get_canvas()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
const SelectionInfo *sel_info = get_pool()->selection_info(); const SelectionInfo *sel_info = get_pool()->selection_info();
@ -491,6 +496,12 @@ void ObjectClipper::set_position(double pos, bool keep_normal)
get_pool()->get_canvas()->set_as_dirty(); get_pool()->get_canvas()->set_as_dirty();
} }
void ObjectClipper::set_position_to_init_layer()
{
m_clp.reset(new ClippingPlane({0, 0, 1}, 0.1));
get_pool()->get_canvas()->set_as_dirty();
}
int CommonGizmosDataObjects::ObjectClipper::get_number_of_contours() const int CommonGizmosDataObjects::ObjectClipper::get_number_of_contours() const
{ {
int sum = 0; int sum = 0;

View File

@ -263,8 +263,9 @@ public:
CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
#endif // NDEBUG #endif // NDEBUG
void set_position(double pos, bool keep_normal); void set_position(double pos, bool keep_normal, bool vertical_normal=false);
double get_position() const { return m_clp_ratio; } double get_position() const { return m_clp_ratio; }
void set_position_to_init_layer();
ClippingPlane* get_clipping_plane() const { return m_clp.get(); } ClippingPlane* get_clipping_plane() const { return m_clp.get(); }
void render_cut(const std::vector<size_t> *ignore_idxs = nullptr) const; void render_cut(const std::vector<size_t> *ignore_idxs = nullptr) const;
void set_range_and_pos(const Vec3d &cpl_normal, double cpl_offset, double pos); void set_range_and_pos(const Vec3d &cpl_normal, double cpl_offset, double pos);

View File

@ -15,6 +15,7 @@
#include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp" #include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp" #include "slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoBrimEars.hpp"
// BBS // BBS
#include "slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp" #include "slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp"
#include "slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp" #include "slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp"
@ -225,6 +226,7 @@ bool GLGizmosManager::init()
m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg", EType::Measure)); m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg", EType::Measure));
m_gizmos.emplace_back(new GLGizmoAssembly(m_parent, m_is_dark ? "toolbar_assembly_dark.svg" : "toolbar_assembly.svg", EType::Assembly)); m_gizmos.emplace_back(new GLGizmoAssembly(m_parent, m_is_dark ? "toolbar_assembly_dark.svg" : "toolbar_assembly.svg", EType::Assembly));
m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "reduce_triangles.svg", EType::Simplify)); m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "reduce_triangles.svg", EType::Simplify));
m_gizmos.emplace_back(new GLGizmoBrimEars(m_parent, m_is_dark ? "toolbar_brimears_dark.svg" : "toolbar_brimears.svg", EType::BrimEars));
//m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", sprite_id++)); //m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", sprite_id++));
//m_gizmos.emplace_back(new GLGizmoFaceDetector(m_parent, "face recognition.svg", sprite_id++)); //m_gizmos.emplace_back(new GLGizmoFaceDetector(m_parent, "face recognition.svg", sprite_id++));
//m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", sprite_id++)); //m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", sprite_id++));
@ -466,6 +468,7 @@ void GLGizmosManager::update_data()
ModelObject* model_object = selection.get_model()->objects[selection.get_object_idx()]; ModelObject* model_object = selection.get_model()->objects[selection.get_object_idx()];
set_flattening_data(model_object); set_flattening_data(model_object);
set_sla_support_data(model_object); set_sla_support_data(model_object);
set_brim_data(model_object);
set_painter_gizmo_data(); set_painter_gizmo_data();
} }
else if (selection.is_single_volume() || selection.is_single_modifier()) else if (selection.is_single_volume() || selection.is_single_modifier())
@ -477,6 +480,7 @@ void GLGizmosManager::update_data()
finish_cut_rotation(); finish_cut_rotation();
set_flattening_data(nullptr); set_flattening_data(nullptr);
set_sla_support_data(nullptr); set_sla_support_data(nullptr);
set_brim_data(nullptr);
set_painter_gizmo_data(); set_painter_gizmo_data();
} }
else if (is_wipe_tower) else if (is_wipe_tower)
@ -486,6 +490,7 @@ void GLGizmosManager::update_data()
set_rotation(Vec3d(0., 0., (M_PI/180.) * dynamic_cast<const ConfigOptionFloat*>(proj_cfg.option("wipe_tower_rotation_angle"))->value)); set_rotation(Vec3d(0., 0., (M_PI/180.) * dynamic_cast<const ConfigOptionFloat*>(proj_cfg.option("wipe_tower_rotation_angle"))->value));
set_flattening_data(nullptr); set_flattening_data(nullptr);
set_sla_support_data(nullptr); set_sla_support_data(nullptr);
set_brim_data(nullptr);
set_painter_gizmo_data(); set_painter_gizmo_data();
} }
else else
@ -494,6 +499,7 @@ void GLGizmosManager::update_data()
set_rotation(Vec3d::Zero()); set_rotation(Vec3d::Zero());
set_flattening_data(selection.is_from_single_object() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr); set_flattening_data(selection.is_from_single_object() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr);
set_sla_support_data(selection.is_from_single_instance() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr); set_sla_support_data(selection.is_from_single_instance() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr);
set_brim_data(selection.is_from_single_instance() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr);
set_painter_gizmo_data(); set_painter_gizmo_data();
} }
@ -646,6 +652,14 @@ void GLGizmosManager::set_sla_support_data(ModelObject* model_object)
gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection()); gizmo_supports->set_sla_support_data(model_object, m_parent.get_selection());
} }
void GLGizmosManager::set_brim_data(ModelObject* model_object)
{
if (!m_enabled || m_gizmos.empty())
return;
auto* gizmo_brim = dynamic_cast<GLGizmoBrimEars*>(m_gizmos[BrimEars].get());
gizmo_brim->set_brim_data(model_object, m_parent.get_selection());
}
void GLGizmosManager::set_painter_gizmo_data() void GLGizmosManager::set_painter_gizmo_data()
{ {
if (!m_enabled || m_gizmos.empty()) if (!m_enabled || m_gizmos.empty())
@ -760,6 +774,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
return dynamic_cast<GLGizmoAdvancedCut *>(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); return dynamic_cast<GLGizmoAdvancedCut *>(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == MeshBoolean) else if (m_current == MeshBoolean)
return dynamic_cast<GLGizmoMeshBoolean*>(m_gizmos[MeshBoolean].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); return dynamic_cast<GLGizmoMeshBoolean*>(m_gizmos[MeshBoolean].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else if (m_current == BrimEars)
return dynamic_cast<GLGizmoBrimEars*>(m_gizmos[BrimEars].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
else else
return false; return false;
} }
@ -874,7 +890,7 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt)
{ {
bool processed = false; bool processed = false;
if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) { if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == BrimEars) {
float rot = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); float rot = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta();
if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown() if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown()
// BBS // BBS
@ -912,7 +928,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
// mouse anywhere // mouse anywhere
if (evt.Moving()) { if (evt.Moving()) {
m_tooltip = update_hover_state(mouse_pos); m_tooltip = update_hover_state(mouse_pos);
if (m_current == MmuSegmentation || m_current == FdmSupports || m_current == Text) if (m_current == MmuSegmentation || m_current == FdmSupports || m_current == Text || m_current == BrimEars)
// BBS // BBS
gizmo_event(SLAGizmoEventType::Moving, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()); gizmo_event(SLAGizmoEventType::Moving, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown());
} else if (evt.LeftUp()) { } else if (evt.LeftUp()) {
@ -1076,7 +1092,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
if (evt.LeftDown() && (!control_down || grabber_contains_mouse())) { if (evt.LeftDown() && (!control_down || grabber_contains_mouse())) {
if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports ||
m_current == Seam || m_current == MmuSegmentation || m_current == Text || m_current == Cut || m_current == MeshBoolean) m_current == Seam || m_current == MmuSegmentation || m_current == Text || m_current == Cut || m_current == MeshBoolean || m_current == BrimEars)
&& gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown())) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown()))
// the gizmo got the event and took some action, there is no need to do anything more // the gizmo got the event and took some action, there is no need to do anything more
processed = true; processed = true;
@ -1101,7 +1117,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
processed = true; processed = true;
} }
} }
else if (evt.RightDown() && selected_object_idx != -1 && (m_current == SlaSupports || m_current == Hollow) else if (evt.RightDown() && selected_object_idx != -1 && (m_current == SlaSupports || m_current == Hollow || m_current == BrimEars)
&& gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) { && gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) {
// we need to set the following right up as processed to avoid showing the context menu if the user release the mouse over the object // we need to set the following right up as processed to avoid showing the context menu if the user release the mouse over the object
pending_right_up = true; pending_right_up = true;
@ -1115,11 +1131,11 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
processed = true; processed = true;
} }
else if (evt.Dragging() && m_parent.get_move_volume_id() != -1 else if (evt.Dragging() && m_parent.get_move_volume_id() != -1
&& (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation)) && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == BrimEars))
// don't allow dragging objects with the Sla gizmo on // don't allow dragging objects with the Sla gizmo on
processed = true; processed = true;
else if (evt.Dragging() && !control_down else if (evt.Dragging() && !control_down
&& (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut) && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut || m_current == BrimEars)
&& gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown())) { && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown())) {
// the gizmo got the event and took some action, no need to do anything more here // the gizmo got the event and took some action, no need to do anything more here
m_parent.set_as_dirty(); m_parent.set_as_dirty();
@ -1133,7 +1149,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), true); gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), true);
} }
else if (evt.LeftUp() else if (evt.LeftUp()
&& (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut) && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut || m_current == BrimEars)
&& gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down) && gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down)
&& !m_parent.is_mouse_dragging()) { && !m_parent.is_mouse_dragging()) {
// in case SLA/FDM gizmo is selected, we just pass the LeftUp event and stop processing - neither // in case SLA/FDM gizmo is selected, we just pass the LeftUp event and stop processing - neither
@ -1231,7 +1247,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
if (m_current != Undefined) { if (m_current != Undefined) {
if ((m_current == Measure || m_current == Assembly) && gizmo_event(SLAGizmoEventType::Escape)) { if ((m_current == Measure || m_current == Assembly) && gizmo_event(SLAGizmoEventType::Escape)) {
// do nothing // do nothing
} else if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges)) } else if ((m_current != SlaSupports && m_current != BrimEars) || !gizmo_event(SLAGizmoEventType::DiscardChanges))
reset_all_states(); reset_all_states();
processed = true; processed = true;
@ -1340,7 +1356,7 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
if (evt.GetEventType() == wxEVT_KEY_UP) if (evt.GetEventType() == wxEVT_KEY_UP)
{ {
if (m_current == SlaSupports || m_current == Hollow) if (m_current == SlaSupports || m_current == Hollow || m_current == BrimEars)
{ {
bool is_editing = true; bool is_editing = true;
bool is_rectangle_dragging = false; bool is_rectangle_dragging = false;
@ -1349,6 +1365,9 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
GLGizmoSlaSupports* gizmo = dynamic_cast<GLGizmoSlaSupports*>(get_current()); GLGizmoSlaSupports* gizmo = dynamic_cast<GLGizmoSlaSupports*>(get_current());
is_editing = gizmo->is_in_editing_mode(); is_editing = gizmo->is_in_editing_mode();
is_rectangle_dragging = gizmo->is_selection_rectangle_dragging(); is_rectangle_dragging = gizmo->is_selection_rectangle_dragging();
} else if (m_current == BrimEars) {
GLGizmoBrimEars* gizmo = dynamic_cast<GLGizmoBrimEars*>(get_current());
is_rectangle_dragging = gizmo->is_selection_rectangle_dragging();
} }
else { else {
GLGizmoHollow* gizmo = dynamic_cast<GLGizmoHollow*>(get_current()); GLGizmoHollow* gizmo = dynamic_cast<GLGizmoHollow*>(get_current());
@ -1391,6 +1410,10 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
// m_parent.set_cursor(GLCanvas3D::Cross); // m_parent.set_cursor(GLCanvas3D::Cross);
processed = true; processed = true;
} }
else if ((m_current == BrimEars) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT)))
{
processed = true;
}
else if (m_current == Cut) else if (m_current == Cut)
{ {
// BBS // BBS
@ -1885,11 +1908,14 @@ bool GLGizmosManager::grabber_contains_mouse() const
bool GLGizmosManager::is_in_editing_mode(bool error_notification) const bool GLGizmosManager::is_in_editing_mode(bool error_notification) const
{ {
if (m_current != SlaSupports || ! dynamic_cast<GLGizmoSlaSupports*>(get_current())->is_in_editing_mode()) if (m_current == SlaSupports && dynamic_cast<GLGizmoSlaSupports*>(get_current())->is_in_editing_mode()) {
return true;
} else if (m_current == BrimEars) {
return true;
} else {
return false; return false;
}
// BBS: remove SLA editing notification
return true;
} }

View File

@ -81,6 +81,7 @@ public:
Measure, Measure,
Assembly, Assembly,
Simplify, Simplify,
BrimEars,
SlaSupports, SlaSupports,
// BBS // BBS
//FaceRecognition, //FaceRecognition,
@ -282,6 +283,8 @@ public:
void set_sla_support_data(ModelObject* model_object); void set_sla_support_data(ModelObject* model_object);
void set_brim_data(ModelObject* model_object);
void set_painter_gizmo_data(); void set_painter_gizmo_data();
bool is_gizmo_activable_when_single_full_instance(); bool is_gizmo_activable_when_single_full_instance();

View File

@ -532,6 +532,15 @@ ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width) cons
return size; return size;
} }
float ImGuiWrapper::find_widest_text(std::vector<wxString> &text_list)
{
float width = .0f;
for(const wxString &text : text_list) {
width = std::max(width, this->calc_text_size(text).x);
}
return width;
}
ImVec2 ImGuiWrapper::calc_button_size(const wxString &text, const ImVec2 &button_size) const ImVec2 ImGuiWrapper::calc_button_size(const wxString &text, const ImVec2 &button_size) const
{ {
const ImVec2 text_size = this->calc_text_size(text); const ImVec2 text_size = this->calc_text_size(text);

View File

@ -92,7 +92,7 @@ public:
ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); } ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); }
ImVec2 calc_text_size(const wxString &text, float wrap_width = -1.0f) const; ImVec2 calc_text_size(const wxString &text, float wrap_width = -1.0f) const;
ImVec2 calc_button_size(const wxString &text, const ImVec2 &button_size = ImVec2(0, 0)) const; ImVec2 calc_button_size(const wxString &text, const ImVec2 &button_size = ImVec2(0, 0)) const;
float find_widest_text(std::vector<wxString> &text_list);
ImVec2 get_item_spacing() const; ImVec2 get_item_spacing() const;
float get_slider_float_height() const; float get_slider_float_height() const;
const LastSliderStatus& get_last_slider_status() const { return m_last_slider_status; } const LastSliderStatus& get_last_slider_status() const { return m_last_slider_status; }

View File

@ -2381,6 +2381,7 @@ void PartPlate::render(bool bottom, bool only_body, bool force_background_color,
shader->start_using(); shader->start_using();
shader->set_uniform("view_model_matrix", view_mat); shader->set_uniform("view_model_matrix", view_mat);
shader->set_uniform("projection_matrix", proj_mat); shader->set_uniform("projection_matrix", proj_mat);
render_height_limit(mode); render_height_limit(mode);
shader->stop_using(); shader->stop_using();
} }
@ -4682,7 +4683,7 @@ void PartPlateList::postprocess_arrange_polygon(arrangement::ArrangePolygon& arr
} }
/*rendering related functions*/ /*rendering related functions*/
void PartPlateList::render_instance(bool bottom, bool only_current, bool only_body, bool force_background_color, int hover_id) void PartPlateList::render_instance(bool bottom, bool only_current, bool only_body, bool force_background_color, int hover_id, bool show_grid)
{ {
if (m_update_plate_mats_vbo) { if (m_update_plate_mats_vbo) {
m_update_plate_mats_vbo = false; m_update_plate_mats_vbo = false;
@ -4709,7 +4710,8 @@ void PartPlateList::render_instance(bool bottom, bool only_current, bool only_bo
if (!bottom) { // draw background if (!bottom) { // draw background
render_exclude_area(force_background_color); // for selected_plate render_exclude_area(force_background_color); // for selected_plate
} }
render_grid(bottom); // for selected_plate if (show_grid)
render_grid(bottom); // for selected_plate
shader->stop_using(); shader->stop_using();
@ -4804,7 +4806,7 @@ void PartPlateList::render_instance_exclude_area(bool force_default_color)
} }
//render //render
void PartPlateList::render(bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali) void PartPlateList::render(bool bottom, bool only_current, bool only_body, int hover_id, bool render_cali, bool show_grid)
{ {
const std::lock_guard<std::mutex> local_lock(m_plates_mutex); const std::lock_guard<std::mutex> local_lock(m_plates_mutex);
std::vector<PartPlate*>::iterator it = m_plate_list.begin(); std::vector<PartPlate*>::iterator it = m_plate_list.begin();
@ -4828,7 +4830,7 @@ void PartPlateList::render(bool bottom, bool only_current, bool only_body, int h
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
glsafe(::glDepthMask(GL_FALSE)); glsafe(::glDepthMask(GL_FALSE));
render_instance(bottom, only_current, only_body, false, m_plate_hover_action); render_instance(bottom, only_current, only_body, false, m_plate_hover_action, show_grid);
for (it = m_plate_list.begin(); it != m_plate_list.end(); it++) { for (it = m_plate_list.begin(); it != m_plate_list.end(); it++) {
int current_index = (*it)->get_index(); int current_index = (*it)->get_index();
@ -4836,14 +4838,14 @@ void PartPlateList::render(bool bottom, bool only_current, bool only_body, int h
continue; continue;
if (current_index == m_current_plate) { if (current_index == m_current_plate) {
PartPlate::HeightLimitMode height_mode = (only_current)?PartPlate::HEIGHT_LIMIT_NONE:m_height_limit_mode; PartPlate::HeightLimitMode height_mode = (only_current)?PartPlate::HEIGHT_LIMIT_NONE:m_height_limit_mode;
if (m_plate_hover_index == current_index) if (m_plate_hover_index == current_index)
(*it)->render(bottom, only_body, false, height_mode, m_plate_hover_action, render_cali); (*it)->render(bottom, only_body, false, height_mode, m_plate_hover_action, render_cali);
else else
(*it)->render(bottom, only_body, false, height_mode, -1, render_cali); (*it)->render(bottom, only_body, false, height_mode, -1, render_cali);
} }
else { else {
if (m_plate_hover_index == current_index) if (m_plate_hover_index == current_index)
(*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, m_plate_hover_action, render_cali); (*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, m_plate_hover_action, render_cali);
else else
(*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, -1, render_cali); (*it)->render(bottom, only_body, false, PartPlate::HEIGHT_LIMIT_NONE, -1, render_cali);
} }

View File

@ -812,7 +812,8 @@ public:
bool only_current = false, bool only_current = false,
bool only_body = false, bool only_body = false,
bool force_background_color = false, bool force_background_color = false,
int hover_id = -1); int hover_id = -1,
bool show_grid = true);
void render_instance_grid(bool bottom); void render_instance_grid(bool bottom);
void render_instance_background(bool force_default_color = false); void render_instance_background(bool force_default_color = false);
void render_grid(bool bottom); void render_grid(bool bottom);
@ -820,7 +821,7 @@ public:
void render_instance_exclude_area(bool force_default_color); void render_instance_exclude_area(bool force_default_color);
void on_change_color_mode(bool is_dark) { m_is_dark = is_dark; } void on_change_color_mode(bool is_dark) { m_is_dark = is_dark; }
void render(bool bottom, bool only_current = false, bool only_body = false, int hover_id = -1, bool render_cali = false); void render(bool bottom, bool only_current = false, bool only_body = false, int hover_id = -1, bool render_cali = false, bool show_grid = true);
void render_for_picking_pass(); void render_for_picking_pass();
void set_render_option(bool bedtype_texture, bool plate_settings); void set_render_option(bool bedtype_texture, bool plate_settings);
void set_render_cali(bool value = true) { render_cali_logo = value; } void set_render_cali(bool value = true) { render_cali_logo = value; }