Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate into et_sla_switch_view

This commit is contained in:
enricoturri1966 2023-05-03 13:45:28 +02:00
commit eb0f03aa9b
9 changed files with 197 additions and 166 deletions

View File

@ -2406,11 +2406,7 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
{
// static const double SIMPLIFY_TOLERANCE_MM = 0.1;
Vec3d rotation = get_rotation();
rotation.z() = 0.;
Transform3d trafo_instance = Geometry::assemble_transform(get_offset().z() * Vec3d::UnitZ(), rotation, get_scaling_factor(), get_mirror());
Polygon p = get_object()->convex_hull_2d(trafo_instance);
Polygon p = get_object()->convex_hull_2d(this->get_matrix());
// if (!p.points.empty()) {
// Polygons pp{p};
@ -2420,12 +2416,24 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
arrangement::ArrangePolygon ret;
ret.poly.contour = std::move(p);
ret.translation = Vec2crd{scaled(get_offset(X)), scaled(get_offset(Y))};
ret.rotation = get_rotation(Z);
ret.translation = Vec2crd::Zero();
ret.rotation = 0.;
return ret;
}
void ModelInstance::apply_arrange_result(const Vec2d &offs, double rotation)
{
// write the transformation data into the model instance
auto trafo = get_transformation().get_matrix();
auto tr = Transform3d::Identity();
tr.translate(to_3d(unscaled(offs), 0.));
trafo = tr * Eigen::AngleAxisd(rotation, Vec3d::UnitZ()) * trafo;
m_transformation.set_matrix(trafo);
this->object->invalidate_bounding_box();
}
indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const
{
TriangleSelector selector(mv.mesh());

View File

@ -1167,14 +1167,7 @@ public:
arrangement::ArrangePolygon get_arrange_polygon() const;
// Apply the arrange result on the ModelInstance
void apply_arrange_result(const Vec2d& offs, double rotation)
{
// write the transformation data into the model instance
set_rotation(Z, rotation);
set_offset(X, unscale<double>(offs(X)));
set_offset(Y, unscale<double>(offs(Y)));
this->object->invalidate_bounding_box();
}
void apply_arrange_result(const Vec2d& offs, double rotation);
protected:
friend class Print;

View File

@ -39,6 +39,7 @@
#include <ostream>
#include <stack>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
@ -681,7 +682,7 @@ Polylines reconnect_polylines(const Polylines &polylines, double limit_distance)
if (polylines.empty())
return polylines;
ankerl::unordered_dense::map<size_t, Polyline> connected;
std::unordered_map<size_t, Polyline> connected;
connected.reserve(polylines.size());
for (size_t i = 0; i < polylines.size(); i++) {
if (!polylines[i].empty()) {
@ -731,7 +732,7 @@ ExtrusionPaths sort_extra_perimeters(ExtrusionPaths extra_perims, int index_of_f
{
if (extra_perims.empty()) return {};
std::vector<ankerl::unordered_dense::set<size_t>> dependencies(extra_perims.size());
std::vector<std::unordered_set<size_t>> dependencies(extra_perims.size());
for (size_t path_idx = 0; path_idx < extra_perims.size(); path_idx++) {
for (size_t prev_path_idx = 0; prev_path_idx < path_idx; prev_path_idx++) {
if (paths_touch(extra_perims[path_idx], extra_perims[prev_path_idx], extrusion_spacing * 1.5f)) {

View File

@ -880,7 +880,6 @@ void Print::process()
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
for (PrintObject *obj : m_objects)
obj->make_perimeters();
this->set_status(70, _u8L("Infilling layers"));
for (PrintObject *obj : m_objects)
obj->infill();
for (PrintObject *obj : m_objects)

View File

@ -305,7 +305,7 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st
bench.stop();
if (!m.empty())
if (!po.m_preview_meshes[step]->empty())
BOOST_LOG_TRIVIAL(trace) << "Preview gen took: " << bench.getElapsedSec();
else
BOOST_LOG_TRIVIAL(error) << "Preview failed!";

View File

@ -27,6 +27,8 @@
#include <functional>
#include <limits>
#include <math.h>
#include <oneapi/tbb/concurrent_vector.h>
#include <oneapi/tbb/parallel_for.h>
#include <optional>
#include <unordered_map>
#include <unordered_set>
@ -172,6 +174,69 @@ struct SliceConnection
}
};
SliceConnection estimate_slice_connection(size_t slice_idx, const Layer *layer)
{
SliceConnection connection;
const LayerSlice &slice = layer->lslices_ex[slice_idx];
Polygons slice_polys = to_polygons(layer->lslices[slice_idx]);
BoundingBox slice_bb = get_extents(slice_polys);
const Layer *lower_layer = layer->lower_layer;
ExPolygons below{};
for (const auto &link : slice.overlaps_below) { below.push_back(lower_layer->lslices[link.slice_idx]); }
Polygons below_polys = to_polygons(below);
BoundingBox below_bb = get_extents(below_polys);
Polygons overlap = intersection(ClipperUtils::clip_clipper_polygons_with_subject_bbox(slice_polys, below_bb),
ClipperUtils::clip_clipper_polygons_with_subject_bbox(below_polys, slice_bb));
for (const Polygon &poly : overlap) {
Vec2f p0 = unscaled(poly.first_point()).cast<float>();
for (size_t i = 2; i < poly.points.size(); i++) {
Vec2f p1 = unscaled(poly.points[i - 1]).cast<float>();
Vec2f p2 = unscaled(poly.points[i]).cast<float>();
float sign = cross2(p1 - p0, p2 - p1) > 0 ? 1.0f : -1.0f;
auto [area, first_moment_of_area, second_moment_area,
second_moment_of_area_covariance] = compute_moments_of_area_of_triangle(p0, p1, p2);
connection.area += sign * area;
connection.centroid_accumulator += sign * Vec3f(first_moment_of_area.x(), first_moment_of_area.y(), layer->print_z * area);
connection.second_moment_of_area_accumulator += sign * second_moment_area;
connection.second_moment_of_area_covariance_accumulator += sign * second_moment_of_area_covariance;
}
}
return connection;
};
using PrecomputedSliceConnections = std::vector<std::vector<SliceConnection>>;
PrecomputedSliceConnections precompute_slices_connections(const PrintObject *po)
{
PrecomputedSliceConnections result{};
for (size_t lidx = 0; lidx < po->layer_count(); lidx++) {
result.emplace_back(std::vector<SliceConnection>{});
for (size_t slice_idx = 0; slice_idx < po->get_layer(lidx)->lslices_ex.size(); slice_idx++) {
result[lidx].push_back(SliceConnection{});
}
}
tbb::parallel_for(tbb::blocked_range<size_t>(0, po->layers().size()), [po, &result](tbb::blocked_range<size_t> r) {
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
const Layer *l = po->get_layer(lidx);
tbb::parallel_for(tbb::blocked_range<size_t>(0, l->lslices_ex.size()), [lidx, l, &result](tbb::blocked_range<size_t> r2) {
for (size_t slice_idx = r2.begin(); slice_idx < r2.end(); slice_idx++) {
result[lidx][slice_idx] = estimate_slice_connection(slice_idx, l);
}
});
}
});
return result;
};
float get_flow_width(const LayerRegion *region, ExtrusionRole role)
{
if (role == ExtrusionRole::BridgeInfill) return region->flow(FlowRole::frExternalPerimeter).width();
@ -253,15 +318,8 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
const AABBTreeLines::LinesDistancer<Linef> &prev_layer_boundary,
const Params &params)
{
if (entity->is_collection()) {
std::vector<ExtrusionLine> checked_lines_out;
checked_lines_out.reserve(prev_layer_lines.get_lines().size() / 3);
for (const auto *e : static_cast<const ExtrusionEntityCollection *>(entity)->entities) {
auto tmp = check_extrusion_entity_stability(e, layer_region, prev_layer_lines, prev_layer_boundary, params);
checked_lines_out.insert(checked_lines_out.end(), tmp.begin(), tmp.end());
}
return checked_lines_out;
} else if (entity->role().is_bridge() && !entity->role().is_perimeter()) {
assert(!entity->is_collection());
if (entity->role().is_bridge() && !entity->role().is_perimeter()) {
// pure bridges are handled separately, beacuse we need to align the forward and backward direction support points
if (entity->length() < scale_(params.min_distance_to_allow_local_supports)) {
return {};
@ -344,9 +402,10 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
curr_point.distance *= sign;
SupportPointCause potential_cause = SupportPointCause::FloatingExtrusion;
if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) {
potential_cause = SupportPointCause::FloatingExtrusion;
}
// Bridges are now separated. While long overhang perimeter is technically bridge, it would confuse the users
// if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) {
// potential_cause = SupportPointCause::FloatingExtrusion;
// }
float max_bridge_len = std::max(params.support_points_interface_radius * 2.0f,
params.bridge_distance /
@ -383,44 +442,6 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
}
}
SliceConnection estimate_slice_connection(size_t slice_idx, const Layer *layer)
{
SliceConnection connection;
const LayerSlice &slice = layer->lslices_ex[slice_idx];
Polygons slice_polys = to_polygons(layer->lslices[slice_idx]);
BoundingBox slice_bb = get_extents(slice_polys);
const Layer *lower_layer = layer->lower_layer;
ExPolygons below{};
for (const auto &link : slice.overlaps_below) { below.push_back(lower_layer->lslices[link.slice_idx]); }
Polygons below_polys = to_polygons(below);
BoundingBox below_bb = get_extents(below_polys);
Polygons overlap = intersection(ClipperUtils::clip_clipper_polygons_with_subject_bbox(slice_polys, below_bb),
ClipperUtils::clip_clipper_polygons_with_subject_bbox(below_polys, slice_bb));
for (const Polygon &poly : overlap) {
Vec2f p0 = unscaled(poly.first_point()).cast<float>();
for (size_t i = 2; i < poly.points.size(); i++) {
Vec2f p1 = unscaled(poly.points[i - 1]).cast<float>();
Vec2f p2 = unscaled(poly.points[i]).cast<float>();
float sign = cross2(p1 - p0, p2 - p1) > 0 ? 1.0f : -1.0f;
auto [area, first_moment_of_area, second_moment_area,
second_moment_of_area_covariance] = compute_moments_of_area_of_triangle(p0, p1, p2);
connection.area += sign * area;
connection.centroid_accumulator += sign * Vec3f(first_moment_of_area.x(), first_moment_of_area.y(), layer->print_z * area);
connection.second_moment_of_area_accumulator += sign * second_moment_area;
connection.second_moment_of_area_covariance_accumulator += sign * second_moment_of_area_covariance;
}
}
return connection;
};
class ObjectPart
{
public:
@ -761,7 +782,10 @@ public:
}
};
std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po, const PrintTryCancel &cancel_func, const Params &params)
std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
const PrecomputedSliceConnections &precomputed_slices_connections,
const PrintTryCancel &cancel_func,
const Params &params)
{
SupportPoints supp_points{};
SupportGridFilter supports_presence_grid(po, params.min_distance_between_support_points);
@ -790,8 +814,8 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) {
const LayerSlice &slice = layer->lslices_ex.at(slice_idx);
auto [new_part, covered_area] = build_object_part_from_slice(slice_idx, layer, params);
SliceConnection connection_to_below = estimate_slice_connection(slice_idx, layer);
auto [new_part, covered_area] = build_object_part_from_slice(slice_idx, layer, params);
const SliceConnection &connection_to_below = precomputed_slices_connections[layer_idx][slice_idx];
#ifdef DETAILED_DEBUG_LOGS
std::cout << "SLICE IDX: " << slice_idx << std::endl;
@ -858,25 +882,87 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
prev_slice_idx_to_weakest_connection = next_slice_idx_to_weakest_connection;
next_slice_idx_to_weakest_connection.clear();
auto get_flat_entities = [](const ExtrusionEntity *e) {
std::vector<const ExtrusionEntity *> entities;
std::vector<const ExtrusionEntity *> queue{e};
while (!queue.empty()) {
const ExtrusionEntity *next = queue.back();
queue.pop_back();
if (next->is_collection()) {
for (const ExtrusionEntity *e : static_cast<const ExtrusionEntityCollection *>(next)->entities) {
queue.push_back(e);
}
} else {
entities.push_back(next);
}
}
return entities;
};
struct EnitityToCheck
{
const ExtrusionEntity *e;
const LayerRegion *region;
size_t slice_idx;
};
std::vector<EnitityToCheck> entities_to_check;
for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) {
const LayerSlice &slice = layer->lslices_ex.at(slice_idx);
for (const auto &island : slice.islands) {
for (const LayerExtrusionRange &fill_range : island.fills) {
const LayerRegion *fill_region = layer->get_region(fill_range.region());
for (const auto &fill_idx : fill_range) {
for (const ExtrusionEntity *e : get_flat_entities(fill_region->fills().entities[fill_idx])) {
if (e->role() == ExtrusionRole::BridgeInfill) {
entities_to_check.push_back({e, fill_region, slice_idx});
}
}
}
}
const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region());
for (const size_t &perimeter_idx : island.perimeters) {
for (const ExtrusionEntity *e : get_flat_entities(perimeter_region->perimeters().entities[perimeter_idx])) {
entities_to_check.push_back({e, perimeter_region, slice_idx});
}
}
}
}
AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary = layer->lower_layer != nullptr ?
AABBTreeLines::LinesDistancer<Linef>{
to_unscaled_linesf(layer->lower_layer->lslices)} :
AABBTreeLines::LinesDistancer<Linef>{};
std::vector<tbb::concurrent_vector<ExtrusionLine>> unstable_lines_per_slice(layer->lslices_ex.size());
std::vector<tbb::concurrent_vector<ExtrusionLine>> ext_perim_lines_per_slice(layer->lslices_ex.size());
tbb::parallel_for(tbb::blocked_range<size_t>(0, entities_to_check.size()),
[&entities_to_check, &prev_layer_ext_perim_lines, &prev_layer_boundary, &unstable_lines_per_slice,
&ext_perim_lines_per_slice, &params](tbb::blocked_range<size_t> r) {
for (size_t entity_idx = r.begin(); entity_idx < r.end(); ++entity_idx) {
const auto &e_to_check = entities_to_check[entity_idx];
for (const auto &line :
check_extrusion_entity_stability(e_to_check.e, e_to_check.region, prev_layer_ext_perim_lines,
prev_layer_boundary, params)) {
if (line.support_point_generated.has_value()) {
unstable_lines_per_slice[e_to_check.slice_idx].push_back(line);
}
if (line.is_external_perimeter()) {
ext_perim_lines_per_slice[e_to_check.slice_idx].push_back(line);
}
}
}
});
std::vector<ExtrusionLine> current_layer_ext_perims_lines{};
current_layer_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size());
// All object parts updated, and for each slice we have coresponding weakest connection.
// We can now check each slice and its corresponding weakest connection and object part for stability.
for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) {
const LayerSlice &slice = layer->lslices_ex.at(slice_idx);
ObjectPart &part = active_object_parts.access(prev_slice_idx_to_object_part_mapping[slice_idx]);
SliceConnection &weakest_conn = prev_slice_idx_to_weakest_connection[slice_idx];
std::vector<Linef> boundary_lines;
for (const auto &link : slice.overlaps_below) {
auto ls = to_unscaled_linesf({layer->lower_layer->lslices[link.slice_idx]});
boundary_lines.insert(boundary_lines.end(), ls.begin(), ls.end());
}
AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary{std::move(boundary_lines)};
std::vector<ExtrusionLine> current_slice_ext_perims_lines{};
current_slice_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size() / layer->lslices_ex.size());
#ifdef DETAILED_DEBUG_LOGS
weakest_conn.print_info("weakest connection info: ");
#endif
@ -911,73 +997,15 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
}
};
// first we will check local extrusion stability of bridges, then of perimeters. Perimeters are more important, they
// account for most of the curling and possible crashes, so on them we will run also global stability check
for (const auto &island : slice.islands) {
// Support bridges where needed.
for (const LayerExtrusionRange &fill_range : island.fills) {
const LayerRegion *fill_region = layer->get_region(fill_range.region());
for (const auto &fill_idx : fill_range) {
const ExtrusionEntity *entity = fill_region->fills().entities[fill_idx];
if (entity->role() == ExtrusionRole::BridgeInfill) {
for (const ExtrusionLine &bridge :
check_extrusion_entity_stability(entity, fill_region, prev_layer_ext_perim_lines, prev_layer_boundary,
params)) {
if (bridge.support_point_generated.has_value()) {
reckon_new_support_point(*bridge.support_point_generated, create_support_point_position(bridge.b),
float(-EPSILON), Vec2f::Zero());
}
}
}
}
}
const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region());
for (const auto &perimeter_idx : island.perimeters) {
const ExtrusionEntity *entity = perimeter_region->perimeters().entities[perimeter_idx];
std::vector<ExtrusionLine> perims = check_extrusion_entity_stability(entity, perimeter_region,
prev_layer_ext_perim_lines, prev_layer_boundary,
params);
for (const ExtrusionLine &perim : perims) {
if (perim.support_point_generated.has_value()) {
reckon_new_support_point(*perim.support_point_generated, create_support_point_position(perim.b), float(-EPSILON),
Vec2f::Zero());
}
if (perim.is_external_perimeter()) {
current_slice_ext_perims_lines.push_back(perim);
}
}
}
// DEBUG EXPORT, NOT USED NOW
// if (BR_bridge) {
// Lines scaledl;
// for (const auto &l : prev_layer_boundary.get_lines()) {
// scaledl.emplace_back(Point::new_scale(l.a), Point::new_scale(l.b));
// }
// Lines perimsl;
// for (const auto &l : current_slice_ext_perims_lines) {
// perimsl.emplace_back(Point::new_scale(l.a), Point::new_scale(l.b));
// }
// BoundingBox bb = get_extents(scaledl);
// bb.merge(get_extents(perimsl));
// ::Slic3r::SVG svg(debug_out_path(
// ("slice" + std::to_string(slice_idx) + "_" + std::to_string(layer_idx).c_str()).c_str()),
// get_extents(scaledl));
// svg.draw(scaledl, "red", scale_(0.4));
// svg.draw(perimsl, "blue", scale_(0.25));
// svg.Close();
// }
for (const auto &l : unstable_lines_per_slice[slice_idx]) {
assert(l.support_point_generated.has_value());
reckon_new_support_point(*l.support_point_generated, create_support_point_position(l.b), float(-EPSILON), Vec2f::Zero());
}
LD current_slice_lines_distancer(current_slice_ext_perims_lines);
LD current_slice_lines_distancer({ext_perim_lines_per_slice[slice_idx].begin(), ext_perim_lines_per_slice[slice_idx].end()});
float unchecked_dist = params.min_distance_between_support_points + 1.0f;
for (const ExtrusionLine &line : current_slice_ext_perims_lines) {
for (const ExtrusionLine &line : current_slice_lines_distancer.get_lines()) {
if ((unchecked_dist + line.len < params.min_distance_between_support_points && line.curled_up_height < params.curling_tolerance_limit) ||
line.len < EPSILON) {
unchecked_dist += line.len;
@ -993,8 +1021,8 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
}
}
}
current_layer_ext_perims_lines.insert(current_layer_ext_perims_lines.end(), current_slice_ext_perims_lines.begin(),
current_slice_ext_perims_lines.end());
current_layer_ext_perims_lines.insert(current_layer_ext_perims_lines.end(), current_slice_lines_distancer.get_lines().begin(),
current_slice_lines_distancer.get_lines().end());
} // slice iterations
prev_layer_ext_perim_lines = LD(current_layer_ext_perims_lines);
} // layer iterations
@ -1048,7 +1076,8 @@ void debug_export(const SupportPoints& support_points,const PartialObjects& obje
std::tuple<SupportPoints, PartialObjects> full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params &params)
{
auto results = check_stability(po, cancel_func, params);
auto precomputed_slices_connections = precompute_slices_connections(po);
auto results = check_stability(po, precomputed_slices_connections, cancel_func, params);
#ifdef DEBUG_FILES
auto [supp_points, objects] = results;
debug_export(supp_points, objects, "issues");

View File

@ -85,6 +85,17 @@ ObjectList::ObjectList(wxWindow* parent) :
// describe control behavior
Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent& event) {
// do not allow to change selection while the sla support gizmo is in editing mode
const GLGizmosManager& gizmos = wxGetApp().plater()->canvas3D()->get_gizmos_manager();
if (gizmos.get_current_type() == GLGizmosManager::EType::SlaSupports && gizmos.is_in_editing_mode(true)) {
wxDataViewItemArray sels;
GetSelections(sels);
if (sels.size() > 1 || event.GetItem() != m_last_selected_item) {
select_item(m_last_selected_item);
return;
}
}
// detect the current mouse position here, to pass it to list_manipulation() method
// if we detect it later, the user may have moved the mouse pointer while calculations are performed, and this would mess-up the HitTest() call performed into list_manipulation()
// see: https://github.com/prusa3d/PrusaSlicer/issues/3802
@ -4964,6 +4975,11 @@ void ObjectList::update_printable_state(int obj_idx, int instance_idx)
void ObjectList::toggle_printable_state()
{
// do not allow to toggle the printable state while the sla support gizmo is in editing mode
const GLGizmosManager& gizmos = wxGetApp().plater()->canvas3D()->get_gizmos_manager();
if (gizmos.get_current_type() == GLGizmosManager::EType::SlaSupports && gizmos.is_in_editing_mode(true))
return;
wxDataViewItemArray sels;
GetSelections(sels);
if (sels.IsEmpty())

View File

@ -24,14 +24,8 @@ GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filen
bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event)
{
if (mouse_event.Moving()) {
// only for sure
m_mouse_left_down = false;
return false;
}
if (mouse_event.LeftDown()) {
if (m_hover_id != -1) {
m_mouse_left_down = true;
Selection &selection = m_parent.get_selection();
if (selection.is_single_full_instance()) {
// Rotate the object so the normal points downward:
@ -42,16 +36,8 @@ bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event)
return true;
}
}
else if (mouse_event.LeftUp()) {
if (m_mouse_left_down) {
// responsible for mouse left up after selecting plane
m_mouse_left_down = false;
return true;
}
}
else if (mouse_event.Leaving())
m_mouse_left_down = false;
else if (mouse_event.LeftUp())
return m_hover_id != -1;
return false;
}

View File

@ -37,7 +37,6 @@ private:
std::vector<PlaneData> m_planes;
std::vector<std::shared_ptr<SceneRaycasterItem>> m_planes_casters;
bool m_mouse_left_down = false; // for detection left_up of this gizmo
const ModelObject* m_old_model_object = nullptr;
int m_old_instance_id{ -1 };