Merge branch 'et_world_coordinates' into fs_emboss_with_CGAL_5_4

# Conflicts:
#	src/libslic3r/Technologies.hpp
#	src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
#	src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
This commit is contained in:
Filip Sykala 2022-03-09 11:06:58 +01:00
commit 70b94d592b
94 changed files with 22001 additions and 19538 deletions

View file

@ -523,21 +523,28 @@ add_custom_target(gettext_make_pot
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMENT "Generate pot file from strings in the source tree"
)
add_custom_target(gettext_merge_po_with_pot
add_custom_target(gettext_merge_community_po_with_pot
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMENT "Merge localization po with new generted pot file"
COMMENT "Merge community po with new generated pot file"
)
file(GLOB L10N_PO_FILES "${L10N_DIR}/*/PrusaSlicer*.po")
# list of names of directories, which are licalized by PS internally
list(APPEND PS_L10N_DIRS "cs" "de" "es" "fr" "it" "ja" "pl")
foreach(po_file ${L10N_PO_FILES})
#GET_FILENAME_COMPONENT(po_dir "${po_file}" DIRECTORY)
#SET(po_new_file "${po_dir}/PrusaSlicer_.po")
add_custom_command(
TARGET gettext_merge_po_with_pot PRE_BUILD
COMMAND msgmerge -N -o ${po_file} ${po_file} "${L10N_DIR}/PrusaSlicer.pot"
# delete obsolete lines from resulting PO to avoid conflicts after a merging of it with wxWidgets.po
COMMAND msgattrib --no-obsolete -o ${po_file} ${po_file}
DEPENDS ${po_file}
)
GET_FILENAME_COMPONENT(po_dir "${po_file}" DIRECTORY)
GET_FILENAME_COMPONENT(po_dir_name "${po_dir}" NAME)
list(FIND PS_L10N_DIRS ${po_dir_name} found_dir_id)
# found_dir_id==-1 means that po_dir_name wasn't found in PS_L10N_DIRS
if(found_dir_id LESS 0)
add_custom_command(
TARGET gettext_merge_community_po_with_pot PRE_BUILD
COMMAND msgmerge -N -o ${po_file} ${po_file} "${L10N_DIR}/PrusaSlicer.pot"
# delete obsolete lines from resulting PO to avoid conflicts after a merging of it with wxWidgets.po
COMMAND msgattrib --no-obsolete -o ${po_file} ${po_file}
DEPENDS ${po_file}
)
endif()
endforeach()
add_custom_target(gettext_concat_wx_po_with_po
@ -545,7 +552,6 @@ add_custom_target(gettext_concat_wx_po_with_po
COMMENT "Concatenate and merge wxWidgets localization po with PrusaSlicer po file"
)
file(GLOB L10N_PO_FILES "${L10N_DIR}/*/PrusaSlicer*.po")
file(GLOB L10N_WX_PO_FILES "${L10N_DIR}/*/PrusaSlicer*.po")
foreach(po_file ${L10N_PO_FILES})
GET_FILENAME_COMPONENT(po_dir "${po_file}" DIRECTORY)
GET_FILENAME_COMPONENT(po_dir_name "${po_dir}" NAME)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,10 @@
#include <algorithm>
#include <numeric>
#include <unordered_set>
#include <mutex>
#include <tbb/parallel_for.h>
#include <boost/thread/lock_guard.hpp>
#ifndef NDEBUG
// #define BRIM_DEBUG_TO_SVG
@ -200,20 +203,94 @@ static ExPolygons top_level_outer_brim_area(const Print &print
return diff_ex(brim_area, no_brim_area);
}
static ExPolygons inner_brim_area(const Print &print,
const ConstPrintObjectPtrs &top_level_objects_with_brim,
const std::vector<ExPolygons> &bottom_layers_expolygons,
const float no_brim_offset)
// Return vector of booleans indicated if polygons from bottom_layers_expolygons contain another polygon or not.
// Every ExPolygon is counted as several Polygons (contour and holes). Contour polygon is always processed before holes.
static std::vector<bool> has_polygons_nothing_inside(const Print &print, const std::vector<ExPolygons> &bottom_layers_expolygons)
{
assert(print.objects().size() == bottom_layers_expolygons.size());
Polygons islands;
for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
const PrintObject *object = print.objects()[print_object_idx];
const Polygons islands_object = to_polygons(bottom_layers_expolygons[print_object_idx]);
islands.reserve(islands.size() + object->instances().size() * islands_object.size());
for (const PrintInstance &instance : object->instances())
append_and_translate(islands, islands_object, instance);
}
ClipperLib_Z::Paths islands_clip;
islands_clip.reserve(islands.size());
for (const Polygon &poly : islands) {
size_t island_idx = &poly - &islands.front();
ClipperLib_Z::Path island_clip;
for (const Point &pt : poly.points)
island_clip.emplace_back(pt.x(), pt.y(), island_idx + 1);
islands_clip.emplace_back(island_clip);
}
ClipperLib_Z::Clipper clipper;
// Always assign zero to detect cases when two polygons are overlapping.
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot, const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) {
pt.z() = 0;
});
clipper.AddPaths(islands_clip, ClipperLib_Z::ptSubject, true);
ClipperLib_Z::PolyTree islands_polytree;
clipper.Execute(ClipperLib_Z::ctUnion, islands_polytree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
std::vector<bool> has_nothing_inside(islands.size());
std::function<void(const ClipperLib_Z::PolyNode&)> check_contours = [&check_contours, &has_nothing_inside](const ClipperLib_Z::PolyNode &parent_node)->void {
if (!parent_node.Childs.empty())
for(const ClipperLib_Z::PolyNode *child_node : parent_node.Childs)
check_contours(*child_node);
if (parent_node.Childs.empty() && !parent_node.Contour.empty() && parent_node.Contour.front().z() != 0) {
int polygon_idx = parent_node.Contour.front().z();
assert(polygon_idx > 0 && polygon_idx <= int(has_nothing_inside.size()));
// The whole contour must have the same ID. In other cases, some counters overlap.
for (const ClipperLib_Z::IntPoint &point : parent_node.Contour)
if (polygon_idx != point.z())
return;
has_nothing_inside[polygon_idx - 1] = true;
}
};
check_contours(islands_polytree);
return has_nothing_inside;
}
// INNERMOST means that ExPolygon doesn't contain any other ExPolygons.
// NORMAL is for other cases.
enum class InnerBrimType {NORMAL, INNERMOST};
struct InnerBrimExPolygons
{
ExPolygons brim_area;
InnerBrimType type = InnerBrimType::NORMAL;
double brim_width = 0.;
};
static std::vector<InnerBrimExPolygons> inner_brim_area(const Print &print,
const ConstPrintObjectPtrs &top_level_objects_with_brim,
const std::vector<ExPolygons> &bottom_layers_expolygons,
const float no_brim_offset)
{
assert(print.objects().size() == bottom_layers_expolygons.size());
std::vector<bool> has_nothing_inside = has_polygons_nothing_inside(print, bottom_layers_expolygons);
std::unordered_set<size_t> top_level_objects_idx;
top_level_objects_idx.reserve(top_level_objects_with_brim.size());
for (const PrintObject *object : top_level_objects_with_brim)
top_level_objects_idx.insert(object->id().id);
ExPolygons brim_area;
ExPolygons no_brim_area;
Polygons holes;
std::vector<ExPolygons> brim_area_innermost(print.objects().size());
ExPolygons brim_area;
ExPolygons no_brim_area;
Polygons holes_reversed;
// polygon_idx must correspond to idx generated inside has_polygons_nothing_inside()
size_t polygon_idx = 0;
for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
const PrintObject *object = print.objects()[print_object_idx];
const BrimType brim_type = object->config().brim_type.value;
@ -221,9 +298,10 @@ static ExPolygons inner_brim_area(const Print &print,
const float brim_width = scale_(object->config().brim_width.value);
const bool top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end();
ExPolygons brim_area_innermost_object;
ExPolygons brim_area_object;
ExPolygons no_brim_area_object;
Polygons holes_object;
Polygons holes_reversed_object;
for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx]) {
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) {
if (top_outer_brim)
@ -235,8 +313,20 @@ static ExPolygons inner_brim_area(const Print &print,
// After 7ff76d07684858fd937ef2f5d863f105a10f798e offset and shrink don't work with CW polygons (holes), so let's make it CCW.
Polygons ex_poly_holes_reversed = ex_poly.holes;
polygons_reverse(ex_poly_holes_reversed);
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner)
append(brim_area_object, diff_ex(shrink_ex(ex_poly_holes_reversed, brim_separation, ClipperLib::jtSquare), shrink_ex(ex_poly_holes_reversed, brim_width + brim_separation, ClipperLib::jtSquare)));
for (const PrintInstance &instance : object->instances()) {
++polygon_idx; // Increase idx because of the contour of the ExPolygon.
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner)
for(const Polygon &hole : ex_poly_holes_reversed) {
size_t hole_idx = &hole - &ex_poly_holes_reversed.front();
if (has_nothing_inside[polygon_idx + hole_idx])
append(brim_area_innermost_object, shrink_ex({hole}, brim_separation, ClipperLib::jtSquare));
else
append(brim_area_object, diff_ex(shrink_ex({hole}, brim_separation, ClipperLib::jtSquare), shrink_ex({hole}, brim_width + brim_separation, ClipperLib::jtSquare)));
}
polygon_idx += ex_poly.holes.size(); // Increase idx for every hole of the ExPolygon.
}
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset, ClipperLib::jtSquare), ex_poly_holes_reversed));
@ -244,18 +334,34 @@ static ExPolygons inner_brim_area(const Print &print,
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
append(no_brim_area_object, diff_ex(ExPolygon(ex_poly.contour), shrink_ex(ex_poly_holes_reversed, no_brim_offset, ClipperLib::jtSquare)));
append(holes_object, ex_poly_holes_reversed);
append(holes_reversed_object, ex_poly_holes_reversed);
}
append(no_brim_area_object, offset_ex(bottom_layers_expolygons[print_object_idx], brim_separation, ClipperLib::jtSquare));
for (const PrintInstance &instance : object->instances()) {
append_and_translate(brim_area_innermost[print_object_idx], brim_area_innermost_object, instance);
append_and_translate(brim_area, brim_area_object, instance);
append_and_translate(no_brim_area, no_brim_area_object, instance);
append_and_translate(holes, holes_object, instance);
append_and_translate(holes_reversed, holes_reversed_object, instance);
}
}
assert(polygon_idx == has_nothing_inside.size());
return diff_ex(intersection_ex(to_polygons(std::move(brim_area)), holes), no_brim_area);
ExPolygons brim_area_innermost_merged;
// Append all innermost brim areas.
std::vector<InnerBrimExPolygons> brim_area_out;
for (size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx)
if (const double brim_width = print.objects()[print_object_idx]->config().brim_width.value; !brim_area_innermost[print_object_idx].empty()) {
append(brim_area_innermost_merged, brim_area_innermost[print_object_idx]);
brim_area_out.push_back({std::move(brim_area_innermost[print_object_idx]), InnerBrimType::INNERMOST, brim_width});
}
// Append all normal brim areas.
brim_area_out.push_back({diff_ex(intersection_ex(to_polygons(std::move(brim_area)), holes_reversed), no_brim_area), InnerBrimType::NORMAL});
// Cut out a huge brim areas that overflows into the INNERMOST holes.
brim_area_out.back().brim_area = diff_ex(brim_area_out.back().brim_area, brim_area_innermost_merged);
return brim_area_out;
}
// Flip orientation of open polylines to minimize travel distance.
@ -359,17 +465,28 @@ static void make_inner_brim(const Print &print,
ExtrusionEntityCollection &brim)
{
assert(print.objects().size() == bottom_layers_expolygons.size());
const auto scaled_resolution = scaled<double>(print.config().gcode_resolution.value);
Flow flow = print.brim_flow();
ExPolygons islands_ex = inner_brim_area(print, top_level_objects_with_brim, bottom_layers_expolygons, float(flow.scaled_spacing()));
Polygons loops;
islands_ex = offset_ex(islands_ex, -0.5f * float(flow.scaled_spacing()), ClipperLib::jtSquare);
for (size_t i = 0; !islands_ex.empty(); ++i) {
for (ExPolygon &poly_ex : islands_ex)
poly_ex.douglas_peucker(scaled_resolution);
polygons_append(loops, to_polygons(islands_ex));
islands_ex = offset_ex(islands_ex, -float(flow.scaled_spacing()), ClipperLib::jtSquare);
}
const auto scaled_resolution = scaled<double>(print.config().gcode_resolution.value);
Flow flow = print.brim_flow();
std::vector<InnerBrimExPolygons> inner_brims_ex = inner_brim_area(print, top_level_objects_with_brim, bottom_layers_expolygons, float(flow.scaled_spacing()));
Polygons loops;
std::mutex loops_mutex;
tbb::parallel_for(tbb::blocked_range<size_t>(0, inner_brims_ex.size()), [&inner_brims_ex, &flow, &scaled_resolution, &loops, &loops_mutex](const tbb::blocked_range<size_t> &range) {
for (size_t brim_idx = range.begin(); brim_idx < range.end(); ++brim_idx) {
const InnerBrimExPolygons &inner_brim_ex = inner_brims_ex[brim_idx];
auto num_loops = size_t(floor(inner_brim_ex.brim_width / flow.spacing()));
ExPolygons islands_ex = offset_ex(inner_brim_ex.brim_area, -0.5f * float(flow.scaled_spacing()), ClipperLib::jtSquare);
for (size_t i = 0; (inner_brim_ex.type == InnerBrimType::INNERMOST ? i < num_loops : !islands_ex.empty()); ++i) {
for (ExPolygon &poly_ex : islands_ex)
poly_ex.douglas_peucker(scaled_resolution);
{
boost::lock_guard<std::mutex> lock(loops_mutex);
polygons_append(loops, to_polygons(islands_ex));
}
islands_ex = offset_ex(islands_ex, -float(flow.scaled_spacing()), ClipperLib::jtSquare);
}
}
}); // end of parallel_for
loops = union_pt_chained_outside_in(loops);
std::reverse(loops.begin(), loops.end());

View file

@ -94,6 +94,12 @@ public:
// Called on initial G-code preview on OpenGL vertex buffer interleaved normals and vertices.
bool all_paths_inside_vertices_and_normals_interleaved(const std::vector<float>& paths, const Eigen::AlignedBox<float, 3>& bbox, bool ignore_bottom = true) const;
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
const std::pair<std::vector<Vec2d>, std::vector<Vec2d>>& top_bottom_convex_hull_decomposition_scene() const { return m_top_bottom_convex_hull_decomposition_scene; }
const std::pair<std::vector<Vec2d>, std::vector<Vec2d>>& top_bottom_convex_hull_decomposition_bed() const { return m_top_bottom_convex_hull_decomposition_bed; }
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
private:
// Source definition of the print bed geometry (PrintConfig::bed_shape)
std::vector<Vec2d> m_bed_shape;

View file

@ -274,7 +274,6 @@ namespace Slic3r {
// Otherwise, leave control to the user completely.
std::string toolchange_gcode_str;
const std::string& toolchange_gcode = gcodegen.config().toolchange_gcode.value;
// m_max_layer_z = std::max(m_max_layer_z, tcr.print_z);
if (! toolchange_gcode.empty()) {
DynamicConfig config;
int previous_extruder_id = gcodegen.writer().extruder() ? (int)gcodegen.writer().extruder()->id() : -1;
@ -283,7 +282,7 @@ namespace Slic3r {
config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z));
config.set_key_value("toolchange_z", new ConfigOptionFloat(z));
// config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
config.set_key_value("max_layer_z", new ConfigOptionFloat(gcodegen.m_max_layer_z));
toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config);
check_add_eol(toolchange_gcode_str);
}
@ -305,6 +304,9 @@ namespace Slic3r {
if (!start_filament_gcode.empty()) {
// Process the start_filament_gcode for the active filament only.
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(gcodegen.writer().get_position()(2) - gcodegen.m_config.z_offset.value));
config.set_key_value("max_layer_z", new ConfigOptionFloat(gcodegen.m_max_layer_z));
config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id));
start_filament_gcode_str = gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config);
check_add_eol(start_filament_gcode_str);
@ -1045,7 +1047,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
if (! print.config().gcode_substitutions.values.empty()) {
m_find_replace = make_unique<GCodeFindReplace>(print.config());
file.set_find_replace(m_find_replace.get());
file.set_find_replace(m_find_replace.get(), false);
}
// resets analyzer's tracking data
@ -1153,6 +1155,9 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
}
print.throw_if_canceled();
// Starting now, the G-code find / replace post-processor will be enabled.
file.find_replace_enable();
// adds tags for time estimators
if (print.config().remaining_times.value)
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::First_Line_M73_Placeholder).c_str());
@ -1274,15 +1279,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// Write the custom start G-code
file.writeln(start_gcode);
// Process filament-specific gcode.
/* if (has_wipe_tower) {
// Wipe tower will control the extruder switching, it will call the start_filament_gcode.
} else {
DynamicConfig config;
config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id)));
file.writeln(this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config));
}
*/
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
print.throw_if_canceled();
@ -1468,6 +1464,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges);
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
// From now to the end of G-code, the G-code find / replace post-processor will be disabled.
// Thus the PrusaSlicer generated config will NOT be processed by the G-code post-processor, see GH issue #7952.
file.find_replace_supress();
// Append full config, delimited by two 'phony' configuration keys prusaslicer_config = begin and prusaslicer_config = end.
// The delimiters are structured as configuration key / value pairs to be parsable by older versions of PrusaSlicer G-code viewer.
{
@ -1525,7 +1525,7 @@ void GCode::process_layers(
);
// The pipeline elements are joined using const references, thus no copying is performed.
output_stream.set_find_replace(nullptr);
output_stream.find_replace_supress();
if (m_spiral_vase && m_find_replace)
tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output);
else if (m_spiral_vase)
@ -1534,7 +1534,7 @@ void GCode::process_layers(
tbb::parallel_pipeline(12, generator & cooling & find_replace & output);
else
tbb::parallel_pipeline(12, generator & cooling & output);
output_stream.set_find_replace(m_find_replace.get());
output_stream.find_replace_enable();
}
// Process all layers of a single object instance (sequential mode) with a parallel pipeline:
@ -1578,7 +1578,7 @@ void GCode::process_layers(
);
// The pipeline elements are joined using const references, thus no copying is performed.
output_stream.set_find_replace(nullptr);
output_stream.find_replace_supress();
if (m_spiral_vase && m_find_replace)
tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output);
else if (m_spiral_vase)
@ -1587,7 +1587,7 @@ void GCode::process_layers(
tbb::parallel_pipeline(12, generator & cooling & find_replace & output);
else
tbb::parallel_pipeline(12, generator & cooling & output);
output_stream.set_find_replace(m_find_replace.get());
output_stream.find_replace_enable();
}
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)
@ -1899,6 +1899,8 @@ namespace ProcessLayer
// && !MMU1
) {
//! FIXME_in_fw show message during print pause
// FIXME: Why is pause_print_gcode here? Why is it supplied "color_change_extruder"? Why is that not
// passed to color_change_gcode below?
DynamicConfig cfg;
cfg.set_key_value("color_change_extruder", new ConfigOptionInt(m600_extruder_before_layer));
gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id, &cfg);
@ -2109,10 +2111,10 @@ GCode::LayerResult GCode::process_layer(
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
gcode += this->placeholder_parser_process("layer_gcode",
print.config().layer_gcode.value, m_writer.extruder()->id(), &config)
+ "\n";
config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
}
if (! first_layer && ! m_second_layer_things_done) {
@ -3146,7 +3148,9 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id);
if (! start_filament_gcode.empty()) {
// Process the start_filament_gcode for the filament.
gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id);
DynamicConfig config;
config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id)));
gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config);
check_add_eol(gcode);
}
gcode += m_writer.toolchange(extruder_id);
@ -3215,7 +3219,9 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id);
if (! start_filament_gcode.empty()) {
// Process the start_filament_gcode for the new filament.
gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id);
DynamicConfig config;
config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id)));
gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config);
check_add_eol(gcode);
}
// Set the new extruder to the operating temperature.

View file

@ -196,7 +196,9 @@ private:
// Set a find-replace post-processor to modify the G-code before GCodePostProcessor.
// It is being set to null inside process_layers(), because the find-replace process
// is being called on a secondary thread to improve performance.
void set_find_replace(GCodeFindReplace *find_replace) { m_find_replace = find_replace; }
void set_find_replace(GCodeFindReplace *find_replace, bool enabled) { m_find_replace_backup = find_replace; m_find_replace = enabled ? find_replace : nullptr; }
void find_replace_enable() { m_find_replace = m_find_replace_backup; }
void find_replace_supress() { m_find_replace = nullptr; }
bool is_open() const { return f; }
bool is_error() const;
@ -220,6 +222,8 @@ private:
FILE *f { nullptr };
// Find-replace post-processor to be called before GCodePostProcessor.
GCodeFindReplace *m_find_replace { nullptr };
// If suppressed, the backoup holds m_find_replace.
GCodeFindReplace *m_find_replace_backup { nullptr };
GCodeProcessor &m_processor;
};
void _do_export(Print &print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb);

View file

@ -1412,9 +1412,11 @@ void ModelObject::bake_xy_rotation_into_meshes(size_t instance_idx)
assert(instance_idx < this->instances.size());
const Geometry::Transformation reference_trafo = this->instances[instance_idx]->get_transformation();
#if !ENABLE_WORLD_COORDINATE_SCALE_REVISITED
if (Geometry::is_rotation_ninety_degrees(reference_trafo.get_rotation()))
// nothing to do, scaling in the world coordinate space is possible in the representation of Geometry::Transformation.
return;
#endif // !ENABLE_WORLD_COORDINATE_SCALE_REVISITED
bool left_handed = reference_trafo.is_left_handed();
bool has_mirrorring = ! reference_trafo.get_mirror().isApprox(Vec3d(1., 1., 1.));

View file

@ -605,7 +605,7 @@ struct MMU_Graph
if (arcs[arc_idx].to_idx == to_idx)
return;
for (const size_t &arc_idx : this->nodes[to_idx].arc_idxs)
if (arcs[arc_idx].to_idx == to_idx)
if (arcs[arc_idx].to_idx == from_idx)
return;
this->nodes[from_idx].arc_idxs.push_back(this->arcs.size());
@ -1201,7 +1201,7 @@ static inline double compute_edge_length(const MMU_Graph &graph, const size_t st
used_arcs[start_arc_idx] = true;
const MMU_Graph::Arc *arc = &graph.arcs[start_arc_idx];
size_t idx = start_idx;
double line_total_length = (graph.nodes[arc->to_idx].point - graph.nodes[idx].point).norm();;
double line_total_length = (graph.nodes[arc->to_idx].point - graph.nodes[idx].point).norm();
while (graph.nodes[arc->to_idx].arc_idxs.size() == 2) {
bool found = false;
for (const size_t &arc_idx : graph.nodes[arc->to_idx].arc_idxs) {
@ -1711,7 +1711,7 @@ std::vector<std::vector<ExPolygons>> multi_material_segmentation_by_painting(con
// Such close points sometimes caused that the Voronoi diagram has self-intersecting edges around these vertices.
// This consequently leads to issues with the extraction of colored segments by function extract_colored_segments.
// Calling expolygons_simplify fixed these issues.
input_expolygons[layer_idx] = smooth_outward(expolygons_simplify(offset_ex(ex_polygons, -10.f * float(SCALED_EPSILON)), 5 * SCALED_EPSILON), 10 * coord_t(SCALED_EPSILON));
input_expolygons[layer_idx] = remove_duplicates(expolygons_simplify(offset_ex(ex_polygons, -10.f * float(SCALED_EPSILON)), 5 * SCALED_EPSILON), scaled<coord_t>(0.01), PI/6);
#ifdef MMU_SEGMENTATION_DEBUG_INPUT
{

View file

@ -37,6 +37,36 @@ void remove_duplicates(MutablePolygon &polygon, double eps)
}
}
// Remove nearly duplicate points. If a distance between two points is less than scaled_eps
// and if the angle between its surrounding lines is less than max_angle, the point will be removed.
// May reduce the polygon down to empty polygon.
void remove_duplicates(MutablePolygon &polygon, coord_t scaled_eps, const double max_angle)
{
if (polygon.size() >= 3) {
auto cos_max_angle_2 = Slic3r::sqr<double>(cos(max_angle));
auto scaled_eps_sqr = Slic3r::sqr<int64_t>(scaled_eps);
auto begin = polygon.begin();
auto it = begin;
for (++it; it != begin;) {
auto prev = it.prev();
auto next = it.next();
Vec2i64 v1 = (*it - *prev).cast<int64_t>();
int64_t v1_sqr_norm = v1.squaredNorm();
if (v1_sqr_norm < scaled_eps_sqr) {
if (Vec2i64 v2 = (*next - *prev).cast<int64_t>();
Slic3r::sqr<double>(double(v1.dot(v2))) > cos_max_angle_2 * double(v1_sqr_norm) * double(v2.squaredNorm())) {
it = it.remove();
continue;
}
}
it = next;
}
}
if (polygon.size() < 3)
polygon.clear();
}
// Adapted from Cura ConstPolygonRef::smooth_corner_complex() by Tim Kuipers.
// A concave corner at it1 with position p1 has been removed by the caller between it0 and it2, where |p2 - p0| < shortcut_length.
// Now try to close a concave crack by walking left from it0 and right from it2 as long as the new clipping edge is smaller than shortcut_length

View file

@ -309,6 +309,28 @@ inline bool operator!=(const MutablePolygon &p1, const MutablePolygon &p2) { ret
void remove_duplicates(MutablePolygon &polygon);
void remove_duplicates(MutablePolygon &polygon, double eps);
// Remove nearly duplicate points. If a distance between two points is less than scaled_eps
// and if the angle between its surrounding lines is less than max_angle, the point will be removed.
// May reduce the polygon down to empty polygon.
void remove_duplicates(MutablePolygon &polygon, coord_t scaled_eps, const double max_angle);
inline ExPolygons remove_duplicates(ExPolygons expolygons, coord_t scaled_eps, double max_angle)
{
MutablePolygon mp;
for (ExPolygon &expolygon : expolygons) {
mp.assign(expolygon.contour, expolygon.contour.size() * 2);
remove_duplicates(mp, scaled_eps, max_angle);
mp.polygon(expolygon.contour);
for (Polygon &hole : expolygon.holes) {
mp.assign(hole, hole.size() * 2);
remove_duplicates(mp, scaled_eps, max_angle);
mp.polygon(hole);
}
expolygon.holes.erase(std::remove_if(expolygon.holes.begin(), expolygon.holes.end(), [](const auto &p) { return p.empty(); }), expolygon.holes.end());
}
expolygons.erase(std::remove_if(expolygons.begin(), expolygons.end(), [](const auto &p) { return p.empty(); }), expolygons.end());
return expolygons;
}
void smooth_outward(MutablePolygon &polygon, coord_t clip_dist_scaled);
inline Polygon smooth_outward(Polygon polygon, coord_t clip_dist_scaled)

View file

@ -412,6 +412,8 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config)
for (auto it = this->renamed_from.begin(); ! is_visible && it != this->renamed_from.end(); ++ it)
is_visible = has(*it);
}
else
is_visible = false;
}
}
@ -791,7 +793,8 @@ std::pair<Preset*, bool> PresetCollection::load_external_preset(
// The source config may contain keys from many possible preset types. Just copy those that relate to this preset.
this->get_edited_preset().config.apply_only(combined_config, keys, true);
this->update_dirty();
update_saved_preset_from_current_preset();
// Don't save the newly loaded project as a "saved into project" state.
//update_saved_preset_from_current_preset();
assert(this->get_edited_preset().is_dirty);
return std::make_pair(&(*it), this->get_edited_preset().is_dirty);
}
@ -1226,7 +1229,6 @@ Preset& PresetCollection::select_preset(size_t idx)
idx = first_visible_idx();
m_idx_selected = idx;
m_edited_preset = m_presets[idx];
update_saved_preset_from_current_preset();
bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets;
for (size_t i = 0; i < m_num_default_presets; ++i)
m_presets[i].is_visible = default_visible;

View file

@ -397,6 +397,7 @@ public:
void discard_current_changes() {
m_presets[m_idx_selected].reset_dirty();
m_edited_preset = m_presets[m_idx_selected];
// Don't save the resetted preset state as a "saved into project" state.
// update_saved_preset_from_current_preset();
}

View file

@ -660,7 +660,8 @@ std::string Print::validate(std::string* warning) const
bool layer_gcode_resets_extruder = boost::regex_search(m_config.layer_gcode.value, regex_g92e0);
if (m_config.use_relative_e_distances) {
// See GH issues #6336 #5073
if (! before_layer_gcode_resets_extruder && ! layer_gcode_resets_extruder)
if ((m_config.gcode_flavor == gcfMarlinLegacy || m_config.gcode_flavor == gcfMarlinFirmware) &&
! before_layer_gcode_resets_extruder && ! layer_gcode_resets_extruder)
return L("Relative extruder addressing requires resetting the extruder position at each layer to prevent loss of floating point accuracy. Add \"G92 E0\" to layer_gcode.");
} else if (before_layer_gcode_resets_extruder)
return L("\"G92 E0\" was found in before_layer_gcode, which is incompatible with absolute extruder addressing.");

View file

@ -74,12 +74,20 @@
#define ENABLE_SHOW_NON_MANIFOLD_EDGES (1 && ENABLE_2_5_0_ALPHA1)
// Enable rework of Reload from disk command
#define ENABLE_RELOAD_FROM_DISK_REWORK (1 && ENABLE_2_5_0_ALPHA1)
// Enable showing toolpaths center of gravity
#define ENABLE_SHOW_TOOLPATHS_COG (1 && ENABLE_2_5_0_ALPHA1)
// Enable recalculating toolpaths when switching to/from volumetric rate visualization
#define ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC (1 && ENABLE_2_5_0_ALPHA1)
// Enable editing volumes transformation in world coordinates and instances in local coordinates
#define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_5_0_ALPHA1)
// Enable showing world coordinates of volumes' offset relative to the instance containing them
#define ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET (0 && ENABLE_WORLD_COORDINATE)
// Enable editing instance coordinates of volumes
#define ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES (1 && ENABLE_WORLD_COORDINATE)
// Enable rendering the selection bounding box in the current reference system
#define ENABLE_COORDINATE_DEPENDENT_SELECTION_BOX (1 && ENABLE_WORLD_COORDINATE)
// Enable showing the axes of the current reference system when sidebar hints are active
#define ENABLE_WORLD_COORDINATE_SHOW_AXES (1 && ENABLE_WORLD_COORDINATE)
// Enable alternate implementation of manipulating scale for instances and volumes
#define ENABLE_WORLD_COORDINATE_SCALE_REVISITED (1 && ENABLE_WORLD_COORDINATE)
#endif // _prusaslicer_technologies_h_

View file

@ -133,6 +133,8 @@ set(SLIC3R_GUI_SOURCES
GUI/2DBed.hpp
GUI/3DBed.cpp
GUI/3DBed.hpp
GUI/CoordAxes.cpp
GUI/CoordAxes.hpp
GUI/Camera.cpp
GUI/Camera.hpp
GUI/CameraUtils.cpp

View file

@ -98,6 +98,7 @@ const float* GeometryBuffer::get_vertices_data() const
}
#endif // !ENABLE_GLBEGIN_GLEND_REMOVAL
#if !ENABLE_WORLD_COORDINATE_SHOW_AXES
const float Bed3D::Axes::DefaultStemRadius = 0.5f;
const float Bed3D::Axes::DefaultStemLength = 25.0f;
const float Bed3D::Axes::DefaultTipRadius = 2.5f * Bed3D::Axes::DefaultStemRadius;
@ -152,6 +153,7 @@ void Bed3D::Axes::render()
glsafe(::glDisable(GL_DEPTH_TEST));
}
#endif // !ENABLE_WORLD_COORDINATE_SHOW_AXES
bool Bed3D::set_shape(const Pointfs& bed_shape, const double max_print_height, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
{
@ -288,7 +290,11 @@ BoundingBoxf3 Bed3D::calc_extended_bounding_box() const
out.max.z() = 0.0;
// extend to contain axes
out.merge(m_axes.get_origin() + m_axes.get_total_length() * Vec3d::Ones());
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
out.merge(out.min + Vec3d(-m_axes.get_tip_radius(), -m_axes.get_tip_radius(), out.max.z()));
#else
out.merge(out.min + Vec3d(-Axes::DefaultTipRadius, -Axes::DefaultTipRadius, out.max.z()));
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES
// extend to contain model, if any
BoundingBoxf3 model_bb = m_model.get_bounding_box();
if (model_bb.defined) {
@ -458,7 +464,11 @@ std::tuple<Bed3D::Type, std::string, std::string> Bed3D::detect_type(const Point
void Bed3D::render_axes()
{
if (m_build_volume.valid())
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
m_axes.render(0.25f);
#else
m_axes.render();
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES
}
void Bed3D::render_system(GLCanvas3D& canvas, bool bottom, bool show_texture)

View file

@ -3,7 +3,11 @@
#include "GLTexture.hpp"
#include "3DScene.hpp"
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
#include "CoordAxes.hpp"
#else
#include "GLModel.hpp"
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES
#include "libslic3r/BuildVolume.hpp"
#if ENABLE_GLBEGIN_GLEND_REMOVAL
@ -44,6 +48,7 @@ public:
class Bed3D
{
#if !ENABLE_WORLD_COORDINATE_SHOW_AXES
class Axes
{
public:
@ -67,6 +72,7 @@ class Bed3D
float get_total_length() const { return m_stem_length + DefaultTipLength; }
void render();
};
#endif // !ENABLE_WORLD_COORDINATE_SHOW_AXES
public:
enum class Type : unsigned char
@ -105,7 +111,11 @@ private:
#if !ENABLE_GLBEGIN_GLEND_REMOVAL
unsigned int m_vbo_id{ 0 };
#endif // !ENABLE_GLBEGIN_GLEND_REMOVAL
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
CoordAxes m_axes;
#else
Axes m_axes;
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES
float m_scale_factor{ 1.0f };

View file

@ -1,10 +1,12 @@
#include <GL/glew.h>
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_SMOOTH_NORMALS
#include <igl/per_face_normals.h>
#include <igl/per_corner_normals.h>
#include <igl/per_vertex_normals.h>
#endif // ENABLE_SMOOTH_NORMALS
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#include "3DScene.hpp"
#include "GLShader.hpp"
@ -69,6 +71,7 @@ void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char
namespace Slic3r {
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_SMOOTH_NORMALS
static void smooth_normals_corner(TriangleMesh& mesh, std::vector<stl_normal>& normals)
{
@ -287,6 +290,7 @@ void GLIndexedVertexArray::render(
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
const float GLVolume::SinkingContours::HalfWidth = 0.25f;
@ -483,7 +487,9 @@ GLVolume::GLVolume(float r, float g, float b, float a)
, force_neutral_color(false)
, force_sinking_contours(false)
, tverts_range(0, size_t(-1))
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
, qverts_range(0, size_t(-1))
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
{
color = { r, g, b, a };
set_render_color(color);
@ -599,6 +605,36 @@ const BoundingBoxf3& GLVolume::transformed_non_sinking_bounding_box() const
return *m_transformed_non_sinking_bounding_box;
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void GLVolume::set_range(double min_z, double max_z)
{
this->tverts_range.first = 0;
this->tverts_range.second = this->model.indices_count();
if (!this->print_zs.empty()) {
// The Z layer range is specified.
// First test whether the Z span of this object is not out of (min_z, max_z) completely.
if (this->print_zs.front() > max_z || this->print_zs.back() < min_z)
this->tverts_range.second = 0;
else {
// Then find the lowest layer to be displayed.
size_t i = 0;
for (; i < this->print_zs.size() && this->print_zs[i] < min_z; ++i);
if (i == this->print_zs.size())
// This shall not happen.
this->tverts_range.second = 0;
else {
// Remember start of the layer.
this->tverts_range.first = this->offsets[i];
// Some layers are above $min_z. Which?
for (; i < this->print_zs.size() && this->print_zs[i] <= max_z; ++i);
if (i < this->print_zs.size())
this->tverts_range.second = this->offsets[i];
}
}
}
}
#else
void GLVolume::set_range(double min_z, double max_z)
{
this->qverts_range.first = 0;
@ -611,7 +647,8 @@ void GLVolume::set_range(double min_z, double max_z)
if (this->print_zs.front() > max_z || this->print_zs.back() < min_z) {
this->qverts_range.second = 0;
this->tverts_range.second = 0;
} else {
}
else {
// Then find the lowest layer to be displayed.
size_t i = 0;
for (; i < this->print_zs.size() && this->print_zs[i] < min_z; ++ i);
@ -619,7 +656,8 @@ void GLVolume::set_range(double min_z, double max_z)
// This shall not happen.
this->qverts_range.second = 0;
this->tverts_range.second = 0;
} else {
}
else {
// Remember start of the layer.
this->qverts_range.first = this->offsets[i * 2];
this->tverts_range.first = this->offsets[i * 2 + 1];
@ -633,8 +671,9 @@ void GLVolume::set_range(double min_z, double max_z)
}
}
}
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void GLVolume::render() const
void GLVolume::render()
{
if (!is_active)
return;
@ -645,7 +684,14 @@ void GLVolume::render() const
glsafe(::glPushMatrix());
glsafe(::glMultMatrixd(world_matrix().data()));
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (tverts_range == std::make_pair<size_t, size_t>(0, -1))
model.render();
else
model.render(this->tverts_range);
#else
this->indexed_vertex_array.render(this->tverts_range, this->qverts_range);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
glsafe(::glPopMatrix());
if (this->is_left_handed())
@ -680,25 +726,44 @@ void GLVolume::render_non_manifold_edges()
}
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
std::vector<int> GLVolumeCollection::load_object(
const ModelObject* model_object,
int obj_idx,
const std::vector<int>& instance_idxs)
#else
std::vector<int> GLVolumeCollection::load_object(
const ModelObject *model_object,
int obj_idx,
const std::vector<int> &instance_idxs,
bool opengl_initialized)
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
{
std::vector<int> volumes_idx;
for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++volume_idx)
for (int instance_idx : instance_idxs)
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx));
#else
volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, opengl_initialized));
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
return volumes_idx;
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
int GLVolumeCollection::load_object_volume(
const ModelObject* model_object,
int obj_idx,
int volume_idx,
int instance_idx)
#else
int GLVolumeCollection::load_object_volume(
const ModelObject *model_object,
int obj_idx,
int volume_idx,
int instance_idx,
bool opengl_initialized)
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
{
const ModelVolume *model_volume = model_object->volumes[volume_idx];
const int extruder_id = model_volume->extruder_id();
@ -707,15 +772,22 @@ int GLVolumeCollection::load_object_volume(
this->volumes.emplace_back(new GLVolume());
GLVolume& v = *this->volumes.back();
v.set_color(color_from_model_volume(*model_volume));
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_SMOOTH_NORMALS
v.model.init_from(mesh, true);
#else
v.model.init_from(mesh);
#endif // ENABLE_SMOOTH_NORMALS
#else
#if ENABLE_SMOOTH_NORMALS
v.indexed_vertex_array.load_mesh(mesh, true);
#else
v.indexed_vertex_array.load_mesh(mesh);
#endif // ENABLE_SMOOTH_NORMALS
v.indexed_vertex_array.finalize_geometry(opengl_initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx);
if (model_volume->is_model_part())
{
if (model_volume->is_model_part()) {
// GLVolume will reference a convex hull from model_volume!
v.set_convex_hull(model_volume->get_convex_hull_shared_ptr());
if (extruder_id != -1)
@ -732,6 +804,16 @@ int GLVolumeCollection::load_object_volume(
// Load SLA auxiliary GLVolumes (for support trees or pad).
// This function produces volumes for multiple instances in a single shot,
// as some object specific mesh conversions may be expensive.
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void GLVolumeCollection::load_object_auxiliary(
const SLAPrintObject* print_object,
int obj_idx,
// pairs of <instance_idx, print_instance_idx>
const std::vector<std::pair<size_t, size_t>>& instances,
SLAPrintObjectStep milestone,
// Timestamp of the last change of the milestone
size_t timestamp)
#else
void GLVolumeCollection::load_object_auxiliary(
const SLAPrintObject *print_object,
int obj_idx,
@ -741,6 +823,7 @@ void GLVolumeCollection::load_object_auxiliary(
// Timestamp of the last change of the milestone
size_t timestamp,
bool opengl_initialized)
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
{
assert(print_object->is_step_done(milestone));
Transform3d mesh_trafo_inv = print_object->trafo().inverse();
@ -753,12 +836,21 @@ void GLVolumeCollection::load_object_auxiliary(
const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first];
this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR));
GLVolume& v = *this->volumes.back();
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_SMOOTH_NORMALS
v.model.init_from(mesh, true);
#else
v.model.init_from(mesh);
#endif // ENABLE_SMOOTH_NORMALS
v.model.set_color((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR);
#else
#if ENABLE_SMOOTH_NORMALS
v.indexed_vertex_array.load_mesh(mesh, true);
#else
v.indexed_vertex_array.load_mesh(mesh);
#endif // ENABLE_SMOOTH_NORMALS
v.indexed_vertex_array.finalize_geometry(opengl_initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first);
v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
// Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
@ -774,6 +866,17 @@ void GLVolumeCollection::load_object_auxiliary(
}
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
int GLVolumeCollection::load_wipe_tower_preview(
float pos_x, float pos_y, float width, float depth, float height,
float rotation_angle, bool size_unknown, float brim_width)
#else
int GLVolumeCollection::load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height,
float rotation_angle, bool size_unknown, float brim_width)
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
#else
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
int GLVolumeCollection::load_wipe_tower_preview(
float pos_x, float pos_y, float width, float depth, float height,
@ -783,6 +886,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height,
float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized)
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
{
if (depth < 0.01f)
return int(this->volumes.size() - 1);
@ -839,9 +943,16 @@ int GLVolumeCollection::load_wipe_tower_preview(
volumes.emplace_back(new GLVolume(color));
GLVolume& v = *volumes.back();
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.model.init_from(mesh);
v.model.set_color(color);
#else
v.indexed_vertex_array.load_mesh(mesh);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.set_convex_hull(mesh.convex_hull_3d());
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.indexed_vertex_array.finalize_geometry(opengl_initialized);
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
@ -856,6 +967,22 @@ int GLVolumeCollection::load_wipe_tower_preview(
return int(volumes.size() - 1);
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
GLVolume* GLVolumeCollection::new_toolpath_volume(const ColorRGBA& rgba)
{
GLVolume* out = new_nontoolpath_volume(rgba);
out->is_extrusion_path = true;
return out;
}
GLVolume* GLVolumeCollection::new_nontoolpath_volume(const ColorRGBA& rgba)
{
GLVolume* out = new GLVolume(rgba);
out->is_extrusion_path = false;
this->volumes.emplace_back(out);
return out;
}
#else
GLVolume* GLVolumeCollection::new_toolpath_volume(const ColorRGBA& rgba, size_t reserve_vbo_floats)
{
GLVolume *out = new_nontoolpath_volume(rgba, reserve_vbo_floats);
@ -872,6 +999,7 @@ GLVolume* GLVolumeCollection::new_nontoolpath_volume(const ColorRGBA& rgba, size
this->volumes.emplace_back(out);
return out;
}
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func)
{
@ -960,7 +1088,10 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
shader->set_uniform("uniform_color", volume.first->render_color);
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (!volume.first->model.is_initialized())
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
shader->set_uniform("uniform_color", volume.first->render_color);
shader->set_uniform("z_range", m_z_range, 2);
shader->set_uniform("clipping_plane", m_clipping_plane, 4);
shader->set_uniform("print_volume.type", static_cast<int>(m_print_volume.type));
@ -980,6 +1111,9 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
#endif // ENABLE_ENVIRONMENT_MAP
glcheck();
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume.first->model.set_color(volume.first->render_color);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume.first->render();
#if ENABLE_ENVIRONMENT_MAP
@ -1215,6 +1349,466 @@ std::string GLVolumeCollection::log_memory_info() const
return " (GLVolumeCollection RAM: " + format_memsize_MB(this->cpu_memory_used()) + " GPU: " + format_memsize_MB(this->gpu_memory_used()) + " Both: " + format_memsize_MB(this->gpu_memory_used()) + ")";
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
static void thick_lines_to_geometry(
const Lines& lines,
const std::vector<double>& widths,
const std::vector<double>& heights,
bool closed,
double top_z,
GUI::GLModel::Geometry& geometry)
{
assert(!lines.empty());
if (lines.empty())
return;
enum Direction : unsigned char
{
Left,
Right,
Top,
Bottom
};
// right, left, top, bottom
std::array<int, 4> idx_prev = { -1, -1, -1, -1 };
std::array<int, 4> idx_initial = { -1, -1, -1, -1 };
double bottom_z_prev = 0.0;
Vec2d b1_prev(Vec2d::Zero());
Vec2d v_prev(Vec2d::Zero());
double len_prev = 0.0;
double width_initial = 0.0;
double bottom_z_initial = 0.0;
// loop once more in case of closed loops
const size_t lines_end = closed ? (lines.size() + 1) : lines.size();
for (size_t ii = 0; ii < lines_end; ++ii) {
const size_t i = (ii == lines.size()) ? 0 : ii;
const Line& line = lines[i];
const double bottom_z = top_z - heights[i];
const double middle_z = 0.5 * (top_z + bottom_z);
const double width = widths[i];
const bool is_first = (ii == 0);
const bool is_last = (ii == lines_end - 1);
const bool is_closing = closed && is_last;
const Vec2d v = unscale(line.vector()).normalized();
const double len = unscale<double>(line.length());
const Vec2d a = unscale(line.a);
const Vec2d b = unscale(line.b);
Vec2d a1 = a;
Vec2d a2 = a;
Vec2d b1 = b;
Vec2d b2 = b;
{
const double dist = 0.5 * width; // scaled
const double dx = dist * v.x();
const double dy = dist * v.y();
a1 += Vec2d(+dy, -dx);
a2 += Vec2d(-dy, +dx);
b1 += Vec2d(+dy, -dx);
b2 += Vec2d(-dy, +dx);
}
// calculate new XY normals
const Vec2d xy_right_normal = unscale(line.normal()).normalized();
std::array<int, 4> idx_a = { 0, 0, 0, 0 };
std::array<int, 4> idx_b = { 0, 0, 0, 0 };
int idx_last = int(geometry.vertices_count());
const bool bottom_z_different = bottom_z_prev != bottom_z;
bottom_z_prev = bottom_z;
if (!is_first && bottom_z_different) {
// Found a change of the layer thickness -> Add a cap at the end of the previous segment.
geometry.add_uint_triangle(idx_b[Bottom], idx_b[Left], idx_b[Top]);
geometry.add_uint_triangle(idx_b[Bottom], idx_b[Top], idx_b[Right]);
}
// Share top / bottom vertices if possible.
if (is_first) {
idx_a[Top] = idx_last++;
geometry.add_vertex(Vec3f(a.x(), a.y(), top_z), Vec3f(0.0f, 0.0f, 1.0f));
}
else
idx_a[Top] = idx_prev[Top];
if (is_first || bottom_z_different) {
// Start of the 1st line segment or a change of the layer thickness while maintaining the print_z.
idx_a[Bottom] = idx_last++;
geometry.add_vertex(Vec3f(a.x(), a.y(), bottom_z), Vec3f(0.0f, 0.0f, -1.0f));
idx_a[Left] = idx_last++;
geometry.add_vertex(Vec3f(a2.x(), a2.y(), middle_z), Vec3f(-xy_right_normal.x(), -xy_right_normal.y(), 0.0f));
idx_a[Right] = idx_last++;
geometry.add_vertex(Vec3f(a1.x(), a1.y(), middle_z), Vec3f(xy_right_normal.x(), xy_right_normal.y(), 0.0f));
}
else
idx_a[Bottom] = idx_prev[Bottom];
if (is_first) {
// Start of the 1st line segment.
width_initial = width;
bottom_z_initial = bottom_z;
idx_initial = idx_a;
}
else {
// Continuing a previous segment.
// Share left / right vertices if possible.
const double v_dot = v_prev.dot(v);
// To reduce gpu memory usage, we try to reuse vertices
// To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges
// is longer than a fixed threshold.
// The following value is arbitrary, it comes from tests made on a bunch of models showing the visual artifacts
const double len_threshold = 2.5;
// Generate new vertices if the angle between adjacent edges is greater than 45 degrees or thresholds conditions are met
const bool sharp = (v_dot < 0.707) || (len_prev > len_threshold) || (len > len_threshold);
if (sharp) {
if (!bottom_z_different) {
// Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn.
idx_a[Right] = idx_last++;
geometry.add_vertex(Vec3f(a1.x(), a1.y(), middle_z), Vec3f(xy_right_normal.x(), xy_right_normal.y(), 0.0f));
idx_a[Left] = idx_last++;
geometry.add_vertex(Vec3f(a2.x(), a2.y(), middle_z), Vec3f(-xy_right_normal.x(), -xy_right_normal.y(), 0.0f));
if (cross2(v_prev, v) > 0.0) {
// Right turn. Fill in the right turn wedge.
geometry.add_uint_triangle(idx_prev[Right], idx_a[Right], idx_prev[Top]);
geometry.add_uint_triangle(idx_prev[Right], idx_prev[Bottom], idx_a[Right]);
}
else {
// Left turn. Fill in the left turn wedge.
geometry.add_uint_triangle(idx_prev[Left], idx_prev[Top], idx_a[Left]);
geometry.add_uint_triangle(idx_prev[Left], idx_a[Left], idx_prev[Bottom]);
}
}
}
else {
if (!bottom_z_different) {
// The two successive segments are nearly collinear.
idx_a[Left] = idx_prev[Left];
idx_a[Right] = idx_prev[Right];
}
}
if (is_closing) {
if (!sharp) {
if (!bottom_z_different) {
// Closing a loop with smooth transition. Unify the closing left / right vertices.
geometry.set_vertex(idx_initial[Left], geometry.extract_position_3(idx_prev[Left]), geometry.extract_normal_3(idx_prev[Left]));
geometry.set_vertex(idx_initial[Right], geometry.extract_position_3(idx_prev[Right]), geometry.extract_normal_3(idx_prev[Right]));
geometry.remove_vertex(geometry.vertices_count() - 1);
geometry.remove_vertex(geometry.vertices_count() - 1);
// Replace the left / right vertex indices to point to the start of the loop.
const size_t indices_count = geometry.indices_count();
for (size_t u = indices_count - 24; u < indices_count; ++u) {
const unsigned int id = geometry.extract_uint_index(u);
if (id == (unsigned int)idx_prev[Left])
geometry.set_uint_index(u, (unsigned int)idx_initial[Left]);
else if (id == (unsigned int)idx_prev[Right])
geometry.set_uint_index(u, (unsigned int)idx_initial[Right]);
}
}
}
// This is the last iteration, only required to solve the transition.
break;
}
}
// Only new allocate top / bottom vertices, if not closing a loop.
if (is_closing)
idx_b[Top] = idx_initial[Top];
else {
idx_b[Top] = idx_last++;
geometry.add_vertex(Vec3f(b.x(), b.y(), top_z), Vec3f(0.0f, 0.0f, 1.0f));
}
if (is_closing && width == width_initial && bottom_z == bottom_z_initial)
idx_b[Bottom] = idx_initial[Bottom];
else {
idx_b[Bottom] = idx_last++;
geometry.add_vertex(Vec3f(b.x(), b.y(), bottom_z), Vec3f(0.0f, 0.0f, -1.0f));
}
// Generate new vertices for the end of this line segment.
idx_b[Left] = idx_last++;
geometry.add_vertex(Vec3f(b2.x(), b2.y(), middle_z), Vec3f(-xy_right_normal.x(), -xy_right_normal.y(), 0.0f));
idx_b[Right] = idx_last++;
geometry.add_vertex(Vec3f(b1.x(), b1.y(), middle_z), Vec3f(xy_right_normal.x(), xy_right_normal.y(), 0.0f));
idx_prev = idx_b;
bottom_z_prev = bottom_z;
b1_prev = b1;
v_prev = v;
len_prev = len;
if (bottom_z_different && (closed || (!is_first && !is_last))) {
// Found a change of the layer thickness -> Add a cap at the beginning of this segment.
geometry.add_uint_triangle(idx_a[Bottom], idx_a[Right], idx_a[Top]);
geometry.add_uint_triangle(idx_a[Bottom], idx_a[Top], idx_a[Left]);
}
if (!closed) {
// Terminate open paths with caps.
if (is_first) {
geometry.add_uint_triangle(idx_a[Bottom], idx_a[Right], idx_a[Top]);
geometry.add_uint_triangle(idx_a[Bottom], idx_a[Top], idx_a[Left]);
}
// We don't use 'else' because both cases are true if we have only one line.
if (is_last) {
geometry.add_uint_triangle(idx_b[Bottom], idx_b[Left], idx_b[Top]);
geometry.add_uint_triangle(idx_b[Bottom], idx_b[Top], idx_b[Right]);
}
}
// Add quads for a straight hollow tube-like segment.
// bottom-right face
geometry.add_uint_triangle(idx_a[Bottom], idx_b[Bottom], idx_b[Right]);
geometry.add_uint_triangle(idx_a[Bottom], idx_b[Right], idx_a[Right]);
// top-right face
geometry.add_uint_triangle(idx_a[Right], idx_b[Right], idx_b[Top]);
geometry.add_uint_triangle(idx_a[Right], idx_b[Top], idx_a[Top]);
// top-left face
geometry.add_uint_triangle(idx_a[Top], idx_b[Top], idx_b[Left]);
geometry.add_uint_triangle(idx_a[Top], idx_b[Left], idx_a[Left]);
// bottom-left face
geometry.add_uint_triangle(idx_a[Left], idx_b[Left], idx_b[Bottom]);
geometry.add_uint_triangle(idx_a[Left], idx_b[Bottom], idx_a[Bottom]);
}
}
// caller is responsible for supplying NO lines with zero length
static void thick_lines_to_geometry(
const Lines3& lines,
const std::vector<double>& widths,
const std::vector<double>& heights,
bool closed,
GUI::GLModel::Geometry& geometry)
{
assert(!lines.empty());
if (lines.empty())
return;
enum Direction : unsigned char
{
Left,
Right,
Top,
Bottom
};
// left, right, top, bottom
std::array<int, 4> idx_prev = { -1, -1, -1, -1 };
std::array<int, 4> idx_initial = { -1, -1, -1, -1 };
double z_prev = 0.0;
double len_prev = 0.0;
Vec3d n_right_prev = Vec3d::Zero();
Vec3d n_top_prev = Vec3d::Zero();
Vec3d unit_v_prev = Vec3d::Zero();
double width_initial = 0.0;
// new vertices around the line endpoints
// left, right, top, bottom
std::array<Vec3d, 4> a = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() };
std::array<Vec3d, 4> b = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() };
// loop once more in case of closed loops
const size_t lines_end = closed ? (lines.size() + 1) : lines.size();
for (size_t ii = 0; ii < lines_end; ++ii) {
const size_t i = (ii == lines.size()) ? 0 : ii;
const Line3& line = lines[i];
const double height = heights[i];
const double width = widths[i];
const Vec3d unit_v = unscale(line.vector()).normalized();
const double len = unscale<double>(line.length());
Vec3d n_top = Vec3d::Zero();
Vec3d n_right = Vec3d::Zero();
if (line.a.x() == line.b.x() && line.a.y() == line.b.y()) {
// vertical segment
n_top = Vec3d::UnitY();
n_right = Vec3d::UnitX();
if (line.a.z() < line.b.z())
n_right = -n_right;
}
else {
// horizontal segment
n_right = unit_v.cross(Vec3d::UnitZ()).normalized();
n_top = n_right.cross(unit_v).normalized();
}
const Vec3d rl_displacement = 0.5 * width * n_right;
const Vec3d tb_displacement = 0.5 * height * n_top;
const Vec3d l_a = unscale(line.a);
const Vec3d l_b = unscale(line.b);
a[Right] = l_a + rl_displacement;
a[Left] = l_a - rl_displacement;
a[Top] = l_a + tb_displacement;
a[Bottom] = l_a - tb_displacement;
b[Right] = l_b + rl_displacement;
b[Left] = l_b - rl_displacement;
b[Top] = l_b + tb_displacement;
b[Bottom] = l_b - tb_displacement;
const Vec3d n_bottom = -n_top;
const Vec3d n_left = -n_right;
std::array<int, 4> idx_a = { 0, 0, 0, 0};
std::array<int, 4> idx_b = { 0, 0, 0, 0 };
int idx_last = int(geometry.vertices_count());
const bool z_different = (z_prev != l_a.z());
z_prev = l_b.z();
// Share top / bottom vertices if possible.
if (ii == 0) {
idx_a[Top] = idx_last++;
geometry.add_vertex((Vec3f)a[Top].cast<float>(), (Vec3f)n_top.cast<float>());
}
else
idx_a[Top] = idx_prev[Top];
if (ii == 0 || z_different) {
// Start of the 1st line segment or a change of the layer thickness while maintaining the print_z.
idx_a[Bottom] = idx_last++;
geometry.add_vertex((Vec3f)a[Bottom].cast<float>(), (Vec3f)n_bottom.cast<float>());
idx_a[Left] = idx_last++;
geometry.add_vertex((Vec3f)a[Left].cast<float>(), (Vec3f)n_left.cast<float>());
idx_a[Right] = idx_last++;
geometry.add_vertex((Vec3f)a[Right].cast<float>(), (Vec3f)n_right.cast<float>());
}
else
idx_a[Bottom] = idx_prev[Bottom];
if (ii == 0) {
// Start of the 1st line segment.
width_initial = width;
idx_initial = idx_a;
}
else {
// Continuing a previous segment.
// Share left / right vertices if possible.
const double v_dot = unit_v_prev.dot(unit_v);
const bool is_right_turn = n_top_prev.dot(unit_v_prev.cross(unit_v)) > 0.0;
// To reduce gpu memory usage, we try to reuse vertices
// To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges
// is longer than a fixed threshold.
// The following value is arbitrary, it comes from tests made on a bunch of models showing the visual artifacts
const double len_threshold = 2.5;
// Generate new vertices if the angle between adjacent edges is greater than 45 degrees or thresholds conditions are met
const bool is_sharp = v_dot < 0.707 || len_prev > len_threshold || len > len_threshold;
if (is_sharp) {
// Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn.
idx_a[Right] = idx_last++;
geometry.add_vertex((Vec3f)a[Right].cast<float>(), (Vec3f)n_right.cast<float>());
idx_a[Left] = idx_last++;
geometry.add_vertex((Vec3f)a[Left].cast<float>(), (Vec3f)n_left.cast<float>());
if (is_right_turn) {
// Right turn. Fill in the right turn wedge.
geometry.add_uint_triangle(idx_prev[Right], idx_a[Right], idx_prev[Top]);
geometry.add_uint_triangle(idx_prev[Right], idx_prev[Bottom], idx_a[Right]);
}
else {
// Left turn. Fill in the left turn wedge.
geometry.add_uint_triangle(idx_prev[Left], idx_prev[Top], idx_a[Left]);
geometry.add_uint_triangle(idx_prev[Left], idx_a[Left], idx_prev[Bottom]);
}
}
else {
// The two successive segments are nearly collinear.
idx_a[Left] = idx_prev[Left];
idx_a[Right] = idx_prev[Right];
}
if (ii == lines.size()) {
if (!is_sharp) {
// Closing a loop with smooth transition. Unify the closing left / right vertices.
geometry.set_vertex(idx_initial[Left], geometry.extract_position_3(idx_prev[Left]), geometry.extract_normal_3(idx_prev[Left]));
geometry.set_vertex(idx_initial[Right], geometry.extract_position_3(idx_prev[Right]), geometry.extract_normal_3(idx_prev[Right]));
geometry.remove_vertex(geometry.vertices_count() - 1);
geometry.remove_vertex(geometry.vertices_count() - 1);
// Replace the left / right vertex indices to point to the start of the loop.
const size_t indices_count = geometry.indices_count();
for (size_t u = indices_count - 24; u < indices_count; ++u) {
const unsigned int id = geometry.extract_uint_index(u);
if (id == (unsigned int)idx_prev[Left])
geometry.set_uint_index(u, (unsigned int)idx_initial[Left]);
else if (id == (unsigned int)idx_prev[Right])
geometry.set_uint_index(u, (unsigned int)idx_initial[Right]);
}
}
// This is the last iteration, only required to solve the transition.
break;
}
}
// Only new allocate top / bottom vertices, if not closing a loop.
if (closed && ii + 1 == lines.size())
idx_b[Top] = idx_initial[Top];
else {
idx_b[Top] = idx_last++;
geometry.add_vertex((Vec3f)b[Top].cast<float>(), (Vec3f)n_top.cast<float>());
}
if (closed && ii + 1 == lines.size() && width == width_initial)
idx_b[Bottom] = idx_initial[Bottom];
else {
idx_b[Bottom] = idx_last++;
geometry.add_vertex((Vec3f)b[Bottom].cast<float>(), (Vec3f)n_bottom.cast<float>());
}
// Generate new vertices for the end of this line segment.
idx_b[Left] = idx_last++;
geometry.add_vertex((Vec3f)b[Left].cast<float>(), (Vec3f)n_left.cast<float>());
idx_b[Right] = idx_last++;
geometry.add_vertex((Vec3f)b[Right].cast<float>(), (Vec3f)n_right.cast<float>());
idx_prev = idx_b;
n_right_prev = n_right;
n_top_prev = n_top;
unit_v_prev = unit_v;
len_prev = len;
if (!closed) {
// Terminate open paths with caps.
if (i == 0) {
geometry.add_uint_triangle(idx_a[Bottom], idx_a[Right], idx_a[Top]);
geometry.add_uint_triangle(idx_a[Bottom], idx_a[Top], idx_a[Left]);
}
// We don't use 'else' because both cases are true if we have only one line.
if (i + 1 == lines.size()) {
geometry.add_uint_triangle(idx_b[Bottom], idx_b[Left], idx_b[Top]);
geometry.add_uint_triangle(idx_b[Bottom], idx_b[Top], idx_b[Right]);
}
}
// Add quads for a straight hollow tube-like segment.
// bottom-right face
geometry.add_uint_triangle(idx_a[Bottom], idx_b[Bottom], idx_b[Right]);
geometry.add_uint_triangle(idx_a[Bottom], idx_b[Right], idx_a[Right]);
// top-right face
geometry.add_uint_triangle(idx_a[Right], idx_b[Right], idx_b[Top]);
geometry.add_uint_triangle(idx_a[Right], idx_b[Top], idx_a[Top]);
// top-left face
geometry.add_uint_triangle(idx_a[Top], idx_b[Top], idx_b[Left]);
geometry.add_uint_triangle(idx_a[Top], idx_b[Left], idx_a[Left]);
// bottom-left face
geometry.add_uint_triangle(idx_a[Left], idx_b[Left], idx_b[Bottom]);
geometry.add_uint_triangle(idx_a[Left], idx_b[Bottom], idx_a[Bottom]);
}
}
#else
// caller is responsible for supplying NO lines with zero length
static void thick_lines_to_indexed_vertex_array(
const Lines &lines,
@ -1724,7 +2318,30 @@ static void point_to_indexed_vertex_array(const Vec3crd& point,
volume.push_triangle(idxs[3], idxs[1], idxs[4]);
volume.push_triangle(idxs[0], idxs[3], idxs[4]);
}
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void _3DScene::thick_lines_to_verts(
const Lines& lines,
const std::vector<double>& widths,
const std::vector<double>& heights,
bool closed,
double top_z,
GUI::GLModel::Geometry& geometry)
{
thick_lines_to_geometry(lines, widths, heights, closed, top_z, geometry);
}
void _3DScene::thick_lines_to_verts(
const Lines3& lines,
const std::vector<double>& widths,
const std::vector<double>& heights,
bool closed,
GUI::GLModel::Geometry& geometry)
{
thick_lines_to_geometry(lines, widths, heights, closed, geometry);
}
#else
void _3DScene::thick_lines_to_verts(
const Lines &lines,
const std::vector<double> &widths,
@ -1766,8 +2383,21 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, flo
{
extrusionentity_to_verts(extrusion_path.polyline, extrusion_path.width, extrusion_path.height, print_z, volume);
}
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// Fill in the qverts and tverts with quads and triangles for the extrusion_path.
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void _3DScene::extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry)
{
Polyline polyline = extrusion_path.polyline;
polyline.remove_duplicate_points();
polyline.translate(copy);
const Lines lines = polyline.lines();
std::vector<double> widths(lines.size(), extrusion_path.width);
std::vector<double> heights(lines.size(), extrusion_path.height);
thick_lines_to_verts(lines, widths, heights, false, print_z, geometry);
}
#else
void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point &copy, GLVolume &volume)
{
Polyline polyline = extrusion_path.polyline;
@ -1778,8 +2408,27 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, flo
std::vector<double> heights(lines.size(), extrusion_path.height);
thick_lines_to_verts(lines, widths, heights, false, print_z, volume);
}
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// Fill in the qverts and tverts with quads and triangles for the extrusion_loop.
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void _3DScene::extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry)
{
Lines lines;
std::vector<double> widths;
std::vector<double> heights;
for (const ExtrusionPath& extrusion_path : extrusion_loop.paths) {
Polyline polyline = extrusion_path.polyline;
polyline.remove_duplicate_points();
polyline.translate(copy);
const Lines lines_this = polyline.lines();
append(lines, lines_this);
widths.insert(widths.end(), lines_this.size(), extrusion_path.width);
heights.insert(heights.end(), lines_this.size(), extrusion_path.height);
}
thick_lines_to_verts(lines, widths, heights, true, print_z, geometry);
}
#else
void _3DScene::extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point &copy, GLVolume &volume)
{
Lines lines;
@ -1796,8 +2445,27 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, flo
}
thick_lines_to_verts(lines, widths, heights, true, print_z, volume);
}
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// Fill in the qverts and tverts with quads and triangles for the extrusion_multi_path.
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry)
{
Lines lines;
std::vector<double> widths;
std::vector<double> heights;
for (const ExtrusionPath& extrusion_path : extrusion_multi_path.paths) {
Polyline polyline = extrusion_path.polyline;
polyline.remove_duplicate_points();
polyline.translate(copy);
const Lines lines_this = polyline.lines();
append(lines, lines_this);
widths.insert(widths.end(), lines_this.size(), extrusion_path.width);
heights.insert(heights.end(), lines_this.size(), extrusion_path.height);
}
thick_lines_to_verts(lines, widths, heights, false, print_z, geometry);
}
#else
void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point &copy, GLVolume &volume)
{
Lines lines;
@ -1814,13 +2482,49 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_mult
}
thick_lines_to_verts(lines, widths, heights, false, print_z, volume);
}
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void _3DScene::extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry)
{
for (const ExtrusionEntity* extrusion_entity : extrusion_entity_collection.entities)
extrusionentity_to_verts(extrusion_entity, print_z, copy, geometry);
}
#else
void _3DScene::extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point &copy, GLVolume &volume)
{
for (const ExtrusionEntity *extrusion_entity : extrusion_entity_collection.entities)
extrusionentity_to_verts(extrusion_entity, print_z, copy, volume);
}
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void _3DScene::extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry)
{
if (extrusion_entity != nullptr) {
auto* extrusion_path = dynamic_cast<const ExtrusionPath*>(extrusion_entity);
if (extrusion_path != nullptr)
extrusionentity_to_verts(*extrusion_path, print_z, copy, geometry);
else {
auto* extrusion_loop = dynamic_cast<const ExtrusionLoop*>(extrusion_entity);
if (extrusion_loop != nullptr)
extrusionentity_to_verts(*extrusion_loop, print_z, copy, geometry);
else {
auto* extrusion_multi_path = dynamic_cast<const ExtrusionMultiPath*>(extrusion_entity);
if (extrusion_multi_path != nullptr)
extrusionentity_to_verts(*extrusion_multi_path, print_z, copy, geometry);
else {
auto* extrusion_entity_collection = dynamic_cast<const ExtrusionEntityCollection*>(extrusion_entity);
if (extrusion_entity_collection != nullptr)
extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, geometry);
else
throw Slic3r::RuntimeError("Unexpected extrusion_entity type in to_verts()");
}
}
}
}
}
#else
void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point &copy, GLVolume &volume)
{
if (extrusion_entity != nullptr) {
@ -1839,9 +2543,8 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity,
auto *extrusion_entity_collection = dynamic_cast<const ExtrusionEntityCollection*>(extrusion_entity);
if (extrusion_entity_collection != nullptr)
extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, volume);
else {
else
throw Slic3r::RuntimeError("Unexpected extrusion_entity type in to_verts()");
}
}
}
}
@ -1860,5 +2563,6 @@ void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height
{
thick_point_to_verts(point, width, height, volume);
}
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
} // namespace Slic3r

View file

@ -46,6 +46,7 @@ enum ModelInstanceEPrintVolumeState : unsigned char;
// Return appropriate color based on the ModelVolume.
extern ColorRGBA color_from_model_volume(const ModelVolume& model_volume);
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// A container for interleaved arrays of 3D vertices and normals,
// possibly indexed by triangles and / or quads.
class GLIndexedVertexArray {
@ -246,6 +247,7 @@ public:
private:
BoundingBox m_bounding_box;
};
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
class GLVolume {
public:
@ -388,11 +390,17 @@ public:
// Is mouse or rectangle selection over this object to select/deselect it ?
EHoverState hover;
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
GUI::GLModel model;
#else
// Interleaved triangles & normals with indexed triangles & quads.
GLIndexedVertexArray indexed_vertex_array;
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// Ranges of triangle and quad indices to be rendered.
std::pair<size_t, size_t> tverts_range;
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
std::pair<size_t, size_t> qverts_range;
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// If the qverts or tverts contain thick extrusions, then offsets keeps pointers of the starts
// of the extrusions per layer.
@ -402,13 +410,17 @@ public:
// Bounding box of this volume, in unscaled coordinates.
BoundingBoxf3 bounding_box() const {
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
return this->model.get_bounding_box();
#else
BoundingBoxf3 out;
if (! this->indexed_vertex_array.bounding_box().isEmpty()) {
if (!this->indexed_vertex_array.bounding_box().isEmpty()) {
out.min = this->indexed_vertex_array.bounding_box().min().cast<double>();
out.max = this->indexed_vertex_array.bounding_box().max().cast<double>();
out.defined = true;
};
}
return out;
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
void set_color(const ColorRGBA& rgba) { color = rgba; }
@ -498,14 +510,20 @@ public:
// convex hull
const TriangleMesh* convex_hull() const { return m_convex_hull.get(); }
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
bool empty() const { return this->model.is_empty(); }
#else
bool empty() const { return this->indexed_vertex_array.empty(); }
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void set_range(double low, double high);
void render() const;
void render();
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); }
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void set_bounding_boxes_as_dirty() {
m_transformed_bounding_box.reset();
@ -524,12 +542,20 @@ public:
#endif // ENABLE_SHOW_NON_MANIFOLD_EDGES
// Return an estimate of the memory consumed by this class.
size_t cpu_memory_used() const {
//FIXME what to do wih m_convex_hull?
size_t cpu_memory_used() const {
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
return sizeof(*this) + this->model.cpu_memory_used() + this->print_zs.capacity() * sizeof(coordf_t) +
this->offsets.capacity() * sizeof(size_t);
}
// Return an estimate of the memory held by GPU vertex buffers.
size_t gpu_memory_used() const { return this->model.gpu_memory_used(); }
#else
//FIXME what to do wih m_convex_hull?
return sizeof(*this) - sizeof(this->indexed_vertex_array) + this->indexed_vertex_array.cpu_memory_used() + this->print_zs.capacity() * sizeof(coordf_t) + this->offsets.capacity() * sizeof(size_t);
}
// Return an estimate of the memory held by GPU vertex buffers.
size_t gpu_memory_used() const { return this->indexed_vertex_array.gpu_memory_used(); }
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
};
@ -589,6 +615,36 @@ public:
GLVolumeCollection() { set_default_slope_normal_z(); }
~GLVolumeCollection() { clear(); }
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
std::vector<int> load_object(
const ModelObject* model_object,
int obj_idx,
const std::vector<int>& instance_idxs);
int load_object_volume(
const ModelObject* model_object,
int obj_idx,
int volume_idx,
int instance_idx);
// Load SLA auxiliary GLVolumes (for support trees or pad).
void load_object_auxiliary(
const SLAPrintObject* print_object,
int obj_idx,
// pairs of <instance_idx, print_instance_idx>
const std::vector<std::pair<size_t, size_t>>& instances,
SLAPrintObjectStep milestone,
// Timestamp of the last change of the milestone
size_t timestamp);
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
int load_wipe_tower_preview(
float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width);
#else
int load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width);
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
#else
std::vector<int> load_object(
const ModelObject *model_object,
int obj_idx,
@ -620,13 +676,20 @@ public:
int load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized);
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
GLVolume* new_toolpath_volume(const ColorRGBA& rgba);
GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba);
#else
GLVolume* new_toolpath_volume(const ColorRGBA& rgba, size_t reserve_vbo_floats = 0);
GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba, size_t reserve_vbo_floats = 0);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// Render the volumes by OpenGL.
void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// Finalize the initialization of the geometry & indices,
// upload the geometry and indices to OpenGL VBO objects
// and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs.
@ -634,11 +697,12 @@ public:
// Release the geometry data assigned to the volumes.
// If OpenGL VBOs were allocated, an OpenGL context has to be active to release them.
void release_geometry() { for (auto *v : volumes) v->release_geometry(); }
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// Clear the geometry
void clear() { for (auto *v : volumes) delete v; volumes.clear(); }
bool empty() const { return volumes.empty(); }
void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); }
void set_range(double low, double high) { for (GLVolume* vol : this->volumes) vol->set_range(low, high); }
void set_print_volume(const PrintVolume& print_volume) { m_print_volume = print_volume; }
@ -683,9 +747,18 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo
struct _3DScene
{
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GUI::GLModel::Geometry& geometry);
static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GUI::GLModel::Geometry& geometry);
static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry);
static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry);
static void extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry);
static void extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry);
static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry);
#else
static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GLVolume& volume);
static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GLVolume& volume);
static void extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume);
static void extrusionentity_to_verts(const Polyline& polyline, float width, float height, float print_z, GLVolume& volume);
static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume);
static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GLVolume& volume);
static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GLVolume& volume);
@ -694,6 +767,7 @@ struct _3DScene
static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GLVolume& volume);
static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume);
static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
};
}

View file

@ -0,0 +1,79 @@
#include "libslic3r/libslic3r.h"
#include "CoordAxes.hpp"
#include "GUI_App.hpp"
#include "3DScene.hpp"
#include <GL/glew.h>
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
namespace Slic3r {
namespace GUI {
const float CoordAxes::DefaultStemRadius = 0.5f;
const float CoordAxes::DefaultStemLength = 25.0f;
const float CoordAxes::DefaultTipRadius = 2.5f * CoordAxes::DefaultStemRadius;
const float CoordAxes::DefaultTipLength = 5.0f;
void CoordAxes::render(float emission_factor)
{
auto render_axis = [this](const Transform3f& transform) {
glsafe(::glPushMatrix());
glsafe(::glMultMatrixf(transform.data()));
m_arrow.render();
glsafe(::glPopMatrix());
};
if (!m_arrow.is_initialized())
m_arrow.init_from(stilized_arrow(16, m_tip_radius, m_tip_length, m_stem_radius, m_stem_length));
GLShaderProgram* curr_shader = wxGetApp().get_current_shader();
bool shader_differs = (curr_shader == nullptr || curr_shader->get_name() != "gouraud_light");
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader == nullptr)
return;
if (shader_differs) {
if (curr_shader != nullptr)
curr_shader->stop_using();
shader->start_using();
}
shader->set_uniform("emission_factor", emission_factor);
// x axis
#if ENABLE_GLBEGIN_GLEND_REMOVAL
m_arrow.set_color(ColorRGBA::X());
#else
m_arrow.set_color(-1, ColorRGBA::X());
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
render_axis(Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0 }).cast<float>());
// y axis
#if ENABLE_GLBEGIN_GLEND_REMOVAL
m_arrow.set_color(ColorRGBA::Y());
#else
m_arrow.set_color(-1, ColorRGBA::Y());
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
render_axis(Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0 }).cast<float>());
// z axis
#if ENABLE_GLBEGIN_GLEND_REMOVAL
m_arrow.set_color(ColorRGBA::Z());
#else
m_arrow.set_color(-1, ColorRGBA::Z());
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
render_axis(Geometry::assemble_transform(m_origin).cast<float>());
if (shader_differs) {
shader->stop_using();
if (curr_shader != nullptr)
curr_shader->start_using();
}
}
} // GUI
} // Slic3r
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES

View file

@ -0,0 +1,59 @@
#ifndef slic3r_CoordAxes_hpp_
#define slic3r_CoordAxes_hpp_
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
#include "GLModel.hpp"
namespace Slic3r {
namespace GUI {
class CoordAxes
{
public:
static const float DefaultStemRadius;
static const float DefaultStemLength;
static const float DefaultTipRadius;
static const float DefaultTipLength;
private:
Vec3d m_origin{ Vec3d::Zero() };
float m_stem_radius{ DefaultStemRadius };
float m_stem_length{ DefaultStemLength };
float m_tip_radius{ DefaultTipRadius };
float m_tip_length{ DefaultTipLength };
GLModel m_arrow;
public:
const Vec3d& get_origin() const { return m_origin; }
void set_origin(const Vec3d& origin) { m_origin = origin; }
void set_stem_radius(float radius) {
m_stem_radius = radius;
m_arrow.reset();
}
void set_stem_length(float length) {
m_stem_length = length;
m_arrow.reset();
}
void set_tip_radius(float radius) {
m_tip_radius = radius;
m_arrow.reset();
}
void set_tip_length(float length) {
m_tip_length = length;
m_arrow.reset();
}
float get_stem_radius() const { return m_stem_radius; }
float get_stem_length() const { return m_stem_length; }
float get_tip_radius() const { return m_tip_radius; }
float get_tip_length() const { return m_tip_length; }
float get_total_length() const { return m_stem_length + m_tip_length; }
void render(float emission_factor = 0.0f);
};
} // GUI
} // Slic3r
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES
#endif // slic3r_CoordAxes_hpp_

View file

@ -711,7 +711,11 @@ void GCodeViewer::init()
m_gl_data_initialized = true;
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& print)
#else
void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& print, bool initialized)
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
{
// avoid processing if called with the same gcode_result
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
@ -750,7 +754,11 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr
m_filament_densities = gcode_result.filament_densities;
if (wxGetApp().is_editor())
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
load_shells(print);
#else
load_shells(print, initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
else {
Pointfs bed_shape;
std::string texture;
@ -2289,7 +2297,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
progress_dialog->Destroy();
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void GCodeViewer::load_shells(const Print& print)
#else
void GCodeViewer::load_shells(const Print& print, bool initialized)
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
{
if (print.objects().empty())
// no shells, return
@ -2306,7 +2318,11 @@ void GCodeViewer::load_shells(const Print& print, bool initialized)
}
size_t current_volumes_count = m_shells.volumes.volumes.size();
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
m_shells.volumes.load_object(model_obj, object_id, instance_ids);
#else
m_shells.volumes.load_object(model_obj, object_id, instance_ids, initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// adjust shells' z if raft is present
const SlicingParameters& slicing_parameters = obj->slicing_parameters();
@ -2330,6 +2346,15 @@ void GCodeViewer::load_shells(const Print& print, bool initialized)
const float depth = print.wipe_tower_data(extruders_count).depth;
const float brim_width = print.wipe_tower_data(extruders_count).brim_width;
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
m_shells.volumes.load_wipe_tower_preview(config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
!print.is_step_done(psWipeTower), brim_width);
#else
m_shells.volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
!print.is_step_done(psWipeTower), brim_width);
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
#else
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
m_shells.volumes.load_wipe_tower_preview(config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
!print.is_step_done(psWipeTower), brim_width, initialized);
@ -2337,6 +2362,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized)
m_shells.volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
!print.is_step_done(psWipeTower), brim_width, initialized);
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
}
@ -3199,6 +3225,7 @@ void GCodeViewer::render_shells()
if (shader == nullptr)
return;
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// when the background processing is enabled, it may happen that the shells data have been loaded
// before opengl has been initialized for the preview canvas.
// when this happens, the volumes' data have not been sent to gpu yet.
@ -3206,6 +3233,7 @@ void GCodeViewer::render_shells()
if (!v->indexed_vertex_array.has_VBOs())
v->finalize_geometry(true);
}
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// glsafe(::glDepthMask(GL_FALSE));

View file

@ -823,7 +823,11 @@ public:
void init();
// extract rendering data from the given parameters
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void load(const GCodeProcessorResult& gcode_result, const Print& print);
#else
void load(const GCodeProcessorResult& gcode_result, const Print& print, bool initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// recalculate ranges in dependence of what is visible and sets tool/print colors
void refresh(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors);
#if ENABLE_PREVIEW_LAYOUT
@ -883,7 +887,11 @@ public:
private:
void load_toolpaths(const GCodeProcessorResult& gcode_result);
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void load_shells(const Print& print);
#else
void load_shells(const Print& print, bool initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if !ENABLE_PREVIEW_LAYOUT
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
#endif // !ENABLE_PREVIEW_LAYOUT

View file

@ -87,9 +87,11 @@ static const Slic3r::ColorRGB ERROR_BG_LIGHT_COLOR = { 0.753f, 0.192f, 0.039f
// Number of floats
static constexpr const size_t MAX_VERTEX_BUFFER_SIZE = 131072 * 6; // 3.15MB
// Reserve size in number of floats.
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
static constexpr const size_t VERTEX_BUFFER_RESERVE_SIZE = 131072 * 2; // 1.05MB
// Reserve size in number of floats, maximum sum of all preallocated buffers.
//static constexpr const size_t VERTEX_BUFFER_RESERVE_SIZE_SUM_MAX = 1024 * 1024 * 128 / 4; // 128MB
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
namespace Slic3r {
namespace GUI {
@ -526,7 +528,7 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
glsafe(::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data()));
glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4));
for (const GLVolume* glvolume : volumes.volumes) {
for (GLVolume* glvolume : volumes.volumes) {
// Render the object using the layer editing shader and texture.
if (!glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
continue;
@ -1192,9 +1194,11 @@ bool GLCanvas3D::init()
if (m_main_toolbar.is_enabled())
m_layers_editing.init();
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// on linux the gl context is not valid until the canvas is not shown on screen
// we defer the geometry finalization of volumes until the first call to render()
m_volumes.finalize_geometry(true);
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (m_gizmos.is_enabled() && !m_gizmos.init())
std::cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std::endl;
@ -1799,7 +1803,11 @@ std::vector<int> GLCanvas3D::load_object(const ModelObject& model_object, int ob
instance_idxs.emplace_back(i);
}
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
return m_volumes.load_object(&model_object, obj_idx, instance_idxs);
#else
return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx)
@ -2024,7 +2032,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
// Note the index of the loaded volume, so that we can reload the main model GLVolume with the hollowed mesh
// later in this function.
it->volume_idx = m_volumes.volumes.size();
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx);
#else
m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
m_volumes.volumes.back()->geometry_id = key.geometry_id;
update_object_list = true;
} else {
@ -2081,31 +2093,55 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
GLVolume &volume = *m_volumes.volumes[it->volume_idx];
if (! volume.offsets.empty() && state.step[istep].timestamp != volume.offsets.front()) {
// The backend either produced a new hollowed mesh, or it invalidated the one that the front end has seen.
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume.model.reset();
#else
volume.indexed_vertex_array.release_geometry();
if (state.step[istep].state == PrintStateBase::DONE) {
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (state.step[istep].state == PrintStateBase::DONE) {
TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles);
assert(! mesh.empty());
mesh.transform(sla_print->sla_trafo(*m_model->objects[volume.object_idx()]).inverse());
#if ENABLE_SMOOTH_NORMALS
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume.model.init_from(mesh, true);
#else
volume.indexed_vertex_array.load_mesh(mesh, true);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#else
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume.model.init_from(mesh);
#else
volume.indexed_vertex_array.load_mesh(mesh);
#endif // ENABLE_SMOOTH_NORMALS
} else {
// Reload the original volume.
#if ENABLE_SMOOTH_NORMALS
volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true);
#else
volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh());
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#endif // ENABLE_SMOOTH_NORMALS
}
else {
// Reload the original volume.
#if ENABLE_SMOOTH_NORMALS
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true);
#else
volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#else
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh());
#else
volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh());
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#endif // ENABLE_SMOOTH_NORMALS
}
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume.finalize_geometry(true);
}
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
//FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable
// to the GLVolume. We should refactor GLVolume significantly, so that the GLVolume will not contain member variables
// of various concenrs (model vs. 3D print path).
volume.offsets = { state.step[istep].timestamp };
} else if (state.step[istep].state == PrintStateBase::DONE) {
}
else if (state.step[istep].state == PrintStateBase::DONE) {
// Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created.
ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id);
auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower);
@ -2117,7 +2153,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
instances[istep].emplace_back(std::pair<size_t, size_t>(instance_idx, print_instance_idx));
else
shift_zs[object_idx] = 0.;
} else {
}
else {
// Recycling an old GLVolume. Update the Object/Instance indices into the current Model.
m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx);
m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation());
@ -2127,7 +2164,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
for (size_t istep = 0; istep < sla_steps.size(); ++istep)
if (!instances[istep].empty())
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp);
#else
m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
// Shift-up all volumes of the object so that it has the right elevation with respect to the print bed
@ -2157,6 +2198,17 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
float depth = print->wipe_tower_data(extruders_count).depth;
float brim_width = print->wipe_tower_data(extruders_count).brim_width;
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(
x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower),
brim_width);
#else
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(
1000, x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower),
brim_width);
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
#else
#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(
x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower),
@ -2166,6 +2218,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
1000, x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower),
brim_width, m_initialized);
#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (volume_idx_wipe_tower_old != -1)
map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new;
}
@ -2225,9 +2278,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
m_dirty = true;
}
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume& vol_old, bool gl_initialized, size_t prealloc_size = VERTEX_BUFFER_RESERVE_SIZE)
{
// Assign the large pre-allocated buffers to the new GLVolume.
// Assign the large pre-allocated buffers to the new GLVolume.
vol_new.indexed_vertex_array = std::move(vol_old.indexed_vertex_array);
// Copy the content back to the old GLVolume.
vol_old.indexed_vertex_array = vol_new.indexed_vertex_array;
@ -2239,10 +2293,15 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume&
// Finalize the old geometry, possibly move data to the graphics card.
vol_old.finalize_geometry(gl_initialized);
}
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors)
{
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
m_gcode_viewer.load(gcode_result, *this->fff_print());
#else
m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (wxGetApp().is_editor()) {
m_gcode_viewer.update_shells_color_by_extruder(m_config);
@ -2715,7 +2774,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
m_dirty = true;
},
[this](const Vec3d& direction, bool slow, bool camera_space) {
m_selection.start_dragging();
m_selection.setup_cache();
double multiplier = slow ? 1.0 : 10.0;
Vec3d displacement;
@ -2728,7 +2787,6 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
displacement = multiplier * direction;
m_selection.translate(displacement);
m_selection.stop_dragging();
m_dirty = true;
}
);
@ -2825,9 +2883,8 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
m_dirty = true;
else if (m_gizmos.is_enabled() && !m_selection.is_empty()) {
auto do_rotate = [this](double angle_z_rad) {
m_selection.start_dragging();
m_selection.setup_cache();
m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint));
m_selection.stop_dragging();
m_dirty = true;
// wxGetApp().obj_manipul()->set_dirty();
};
@ -3135,30 +3192,21 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_mouse.set_start_position_3D_as_invalid();
m_mouse.position = pos.cast<double>();
if (evt.Dragging() && current_printer_technology() == ptFFF && fff_print()->config().complete_objects) {
switch (m_gizmos.get_current_type())
{
case GLGizmosManager::EType::Move:
case GLGizmosManager::EType::Scale:
case GLGizmosManager::EType::Rotate:
{
update_sequential_clearance();
break;
}
default: { break; }
}
}
else if (evt.Dragging()) {
switch (m_gizmos.get_current_type())
{
case GLGizmosManager::EType::Move:
case GLGizmosManager::EType::Scale:
case GLGizmosManager::EType::Rotate:
{
show_sinking_contours();
break;
}
default: { break; }
// It should be detection of volume change
// Not only detection of some modifiers !!!
if (evt.Dragging()) {
GLGizmosManager::EType c = m_gizmos.get_current_type();
if (current_printer_technology() == ptFFF &&
fff_print()->config().complete_objects){
if (c == GLGizmosManager::EType::Move ||
c == GLGizmosManager::EType::Scale ||
c == GLGizmosManager::EType::Rotate )
update_sequential_clearance();
} else {
if (c == GLGizmosManager::EType::Move ||
c == GLGizmosManager::EType::Scale ||
c == GLGizmosManager::EType::Rotate)
show_sinking_contours();
}
}
@ -3302,7 +3350,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_volumes.volumes[volume_idx]->hover = GLVolume::HS_None;
// The dragging operation is initiated.
m_mouse.drag.move_volume_idx = volume_idx;
m_selection.start_dragging();
m_selection.setup_cache();
m_mouse.drag.start_position_3D = m_mouse.scene_position;
m_sequential_print_clearance_first_displacement = true;
m_moving = true;
@ -3414,9 +3462,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) {
if (evt.LeftUp())
m_selection.stop_dragging();
if (m_layers_editing.state != LayersEditing::Unknown) {
m_layers_editing.state = LayersEditing::Unknown;
_stop_timer();
@ -3785,15 +3830,6 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
m_dirty = true;
}
void GLCanvas3D::do_flatten(const Vec3d& normal, const std::string& snapshot_type)
{
if (!snapshot_type.empty())
wxGetApp().plater()->take_snapshot(_(snapshot_type));
m_selection.flattening_rotate(normal);
do_rotate(""); // avoid taking another snapshot
}
void GLCanvas3D::do_mirror(const std::string& snapshot_type)
{
if (m_model == nullptr)
@ -4364,7 +4400,11 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const
shader->set_uniform("emission_factor", 0.0f);
for (GLVolume* vol : visible_volumes) {
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
vol->model.set_color((vol->printable && !vol->is_outside) ? (current_printer_technology() == ptSLA ? vol->color : ColorRGBA::ORANGE()) : ColorRGBA::GRAY());
#else
shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? (current_printer_technology() == ptSLA ? vol->color : ColorRGBA::ORANGE()) : ColorRGBA::GRAY());
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// the volume may have been deactivated by an active gizmo
bool is_active = vol->is_active;
vol->is_active = true;
@ -5570,6 +5610,12 @@ void GLCanvas3D::_render_overlays()
void GLCanvas3D::_render_volumes_for_picking() const
{
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
GLShaderProgram* shader = wxGetApp().get_shader("flat");
if (shader == nullptr)
return;
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// do not cull backfaces to show broken geometry, if any
glsafe(::glDisable(GL_CULL_FACE));
@ -5585,9 +5631,17 @@ void GLCanvas3D::_render_volumes_for_picking() const
// we reserve color = (0,0,0) for occluders (as the printbed)
// so we shift volumes' id by 1 to get the proper color
const unsigned int id = 1 + volume.second.first;
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume.first->model.set_color(picking_decode(id));
shader->start_using();
#else
glsafe(::glColor4fv(picking_decode(id).data()));
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume.first->render();
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
shader->stop_using();
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
}
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
@ -6173,23 +6227,48 @@ void GLCanvas3D::_load_print_toolpaths(const BuildVolume &build_volume)
skirt_height = std::min(skirt_height, print_zs.size());
print_zs.erase(print_zs.begin() + skirt_height, print_zs.end());
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
GLVolume* volume = m_volumes.new_toolpath_volume(color);
GLModel::Geometry init_data;
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT };
#else
GLVolume *volume = m_volumes.new_toolpath_volume(color, VERTEX_BUFFER_RESERVE_SIZE);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
for (size_t i = 0; i < skirt_height; ++ i) {
volume->print_zs.emplace_back(print_zs[i]);
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume->offsets.emplace_back(init_data.indices_count());
if (i == 0)
_3DScene::extrusionentity_to_verts(print->brim(), print_zs[i], Point(0, 0), init_data);
_3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), init_data);
#else
volume->offsets.emplace_back(volume->indexed_vertex_array.quad_indices.size());
volume->offsets.emplace_back(volume->indexed_vertex_array.triangle_indices.size());
if (i == 0)
_3DScene::extrusionentity_to_verts(print->brim(), print_zs[i], Point(0, 0), *volume);
_3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), *volume);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (init_data.vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) {
volume->model.init_from(std::move(init_data));
#else
if (volume->indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
GLVolume &vol = *volume;
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
GLVolume &vol = *volume;
volume = m_volumes.new_toolpath_volume(vol.color);
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
reserve_new_volume_finalize_old_volume(*volume, vol, m_initialized);
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume->model.init_from(std::move(init_data));
volume->is_outside = !contains(build_volume, volume->model);
#else
volume->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(volume->indexed_vertex_array.vertices_and_normals_interleaved, volume->indexed_vertex_array.bounding_box());
volume->indexed_vertex_array.finalize_geometry(m_initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const BuildVolume& build_volume, const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values)
@ -6361,7 +6440,12 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
// Allocate the volume before locking.
GLVolume *volume = new GLVolume(color);
volume->is_extrusion_path = true;
tbb::spin_mutex::scoped_lock lock;
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// to prevent sending data to gpu (in the main thread) while
// editing the model geometry
volume->model.disable_render();
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
tbb::spin_mutex::scoped_lock lock;
// Lock by ROII, so if the emplace_back() fails, the lock will be released.
lock.acquire(new_volume_mutex);
m_volumes.volumes.emplace_back(volume);
@ -6373,31 +6457,57 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size),
[&ctxt, &new_volume, is_selected_separate_extruder, this](const tbb::blocked_range<size_t>& range) {
GLVolumePtrs vols;
auto volume = [&ctxt, &vols](size_t layer_idx, int extruder, int feature) -> GLVolume& {
return *vols[ctxt.color_by_color_print()?
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
std::vector<GLModel::Geometry> geometries;
auto select_geometry = [&ctxt, &geometries](size_t layer_idx, int extruder, int feature) -> GLModel::Geometry& {
return geometries[ctxt.color_by_color_print() ?
ctxt.color_print_color_idx_by_layer_idx_and_extruder(layer_idx, extruder) :
ctxt.color_by_tool() ?
std::min<int>(ctxt.number_tools() - 1, std::max<int>(extruder - 1, 0)) :
feature
];
ctxt.color_by_tool() ?
std::min<int>(ctxt.number_tools() - 1, std::max<int>(extruder - 1, 0)) :
feature
];
};
#else
auto volume = [&ctxt, &vols](size_t layer_idx, int extruder, int feature) -> GLVolume& {
return *vols[ctxt.color_by_color_print() ?
ctxt.color_print_color_idx_by_layer_idx_and_extruder(layer_idx, extruder) :
ctxt.color_by_tool() ?
std::min<int>(ctxt.number_tools() - 1, std::max<int>(extruder - 1, 0)) :
feature
];
};
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (ctxt.color_by_color_print() || ctxt.color_by_tool()) {
for (size_t i = 0; i < ctxt.number_tools(); ++i)
for (size_t i = 0; i < ctxt.number_tools(); ++i) {
vols.emplace_back(new_volume(ctxt.color_tool(i)));
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
geometries.emplace_back(GLModel::Geometry());
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
}
else
else {
vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) };
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
geometries = { GLModel::Geometry(), GLModel::Geometry(), GLModel::Geometry() };
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
assert(vols.size() == geometries.size());
for (GLModel::Geometry& g : geometries) {
g.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT };
}
#else
for (GLVolume *vol : vols)
// Reserving number of vertices (3x position + 3x color)
vol->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
const Layer *layer = ctxt.layers[idx_layer];
if (is_selected_separate_extruder)
{
if (is_selected_separate_extruder) {
bool at_least_one_has_correct_extruder = false;
for (const LayerRegion* layerm : layer->regions())
{
for (const LayerRegion* layerm : layer->regions()) {
if (layerm->slices.surfaces.empty())
continue;
const PrintRegionConfig& cfg = layerm->region().config();
@ -6412,17 +6522,27 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
continue;
}
for (GLVolume *vol : vols)
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
for (size_t i = 0; i < vols.size(); ++i) {
GLVolume* vol = vols[i];
if (vol->print_zs.empty() || vol->print_zs.back() != layer->print_z) {
vol->print_zs.emplace_back(layer->print_z);
vol->offsets.emplace_back(geometries[i].indices_count());
}
}
#else
for (GLVolume* vol : vols)
if (vol->print_zs.empty() || vol->print_zs.back() != layer->print_z) {
vol->print_zs.emplace_back(layer->print_z);
vol->offsets.emplace_back(vol->indexed_vertex_array.quad_indices.size());
vol->offsets.emplace_back(vol->indexed_vertex_array.triangle_indices.size());
}
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
for (const PrintInstance &instance : *ctxt.shifted_copies) {
const Point &copy = instance.shift;
for (const LayerRegion *layerm : layer->regions()) {
if (is_selected_separate_extruder)
{
if (is_selected_separate_extruder) {
const PrintRegionConfig& cfg = layerm->region().config();
if (cfg.perimeter_extruder.value != m_selected_extruder ||
cfg.infill_extruder.value != m_selected_extruder ||
@ -6430,19 +6550,31 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
continue;
}
if (ctxt.has_perimeters)
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
_3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy,
select_geometry(idx_layer, layerm->region().config().perimeter_extruder.value, 0));
#else
_3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy,
volume(idx_layer, layerm->region().config().perimeter_extruder.value, 0));
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (ctxt.has_infill) {
for (const ExtrusionEntity *ee : layerm->fills.entities) {
// fill represents infill extrusions of a single island.
const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (! fill->entities.empty())
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
_3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy,
select_geometry(idx_layer, is_solid_infill(fill->entities.front()->role()) ?
layerm->region().config().solid_infill_extruder :
layerm->region().config().infill_extruder, 1));
#else
_3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy,
volume(idx_layer,
is_solid_infill(fill->entities.front()->role()) ?
layerm->region().config().solid_infill_extruder :
layerm->region().config().infill_extruder,
1));
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
}
}
@ -6450,28 +6582,50 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
const SupportLayer *support_layer = dynamic_cast<const SupportLayer*>(layer);
if (support_layer) {
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
_3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy,
select_geometry(idx_layer, (extrusion_entity->role() == erSupportMaterial) ?
support_layer->object()->config().support_material_extruder :
support_layer->object()->config().support_material_interface_extruder, 2));
#else
_3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy,
volume(idx_layer,
(extrusion_entity->role() == erSupportMaterial) ?
support_layer->object()->config().support_material_extruder :
support_layer->object()->config().support_material_interface_extruder,
2));
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
}
}
// Ensure that no volume grows over the limits. If the volume is too large, allocate a new one.
for (size_t i = 0; i < vols.size(); ++i) {
GLVolume &vol = *vols[i];
if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
vols[i] = new_volume(vol.color);
reserve_new_volume_finalize_old_volume(*vols[i], vol, false);
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (geometries[i].vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) {
vol.model.init_from(std::move(geometries[i]));
#else
if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
vols[i] = new_volume(vol.color);
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
reserve_new_volume_finalize_old_volume(*vols[i], vol, false);
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
}
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
for (size_t i = 0; i < vols.size(); ++i) {
if (!geometries[i].is_empty())
vols[i]->model.init_from(std::move(geometries[i]));
}
#else
for (GLVolume *vol : vols)
// Ideally one would call vol->indexed_vertex_array.finalize() here to move the buffers to the OpenGL driver,
// but this code runs in parallel and the OpenGL driver is not thread safe.
vol->indexed_vertex_array.shrink_to_fit();
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
});
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results" << m_volumes.log_memory_info() << log_memory_info();
@ -6486,8 +6640,14 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
}
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) {
GLVolume* v = m_volumes.volumes[i];
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v->is_outside = !contains(build_volume, v->model);
// We are done editinig the model, now it can be sent to gpu
v->model.enable_render();
#else
v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box());
v->indexed_vertex_array.finalize_geometry(m_initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info();
@ -6507,10 +6667,10 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con
struct Ctxt
{
const Print *print;
const std::vector<ColorRGBA>* tool_colors;
Vec2f wipe_tower_pos;
float wipe_tower_angle;
const Print *print;
const std::vector<ColorRGBA> *tool_colors;
Vec2f wipe_tower_pos;
float wipe_tower_angle;
static ColorRGBA color_support() { return ColorRGBA::GREENISH(); }
@ -6552,6 +6712,11 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con
auto new_volume = [this, &new_volume_mutex](const ColorRGBA& color) {
auto *volume = new GLVolume(color);
volume->is_extrusion_path = true;
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// to prevent sending data to gpu (in the main thread) while
// editing the model geometry
volume->model.disable_render();
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
tbb::spin_mutex::scoped_lock lock;
lock.acquire(new_volume_mutex);
m_volumes.volumes.emplace_back(volume);
@ -6565,23 +6730,46 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con
[&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
// Bounding box of this slab of a wipe tower.
GLVolumePtrs vols;
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
std::vector<GLModel::Geometry> geometries;
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (ctxt.color_by_tool()) {
for (size_t i = 0; i < ctxt.number_tools(); ++i)
for (size_t i = 0; i < ctxt.number_tools(); ++i) {
vols.emplace_back(new_volume(ctxt.color_tool(i)));
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
geometries.emplace_back(GLModel::Geometry());
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
}
else
else {
vols = { new_volume(ctxt.color_support()) };
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
geometries = { GLModel::Geometry() };
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
assert(vols.size() == geometries.size());
for (GLModel::Geometry& g : geometries) {
g.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT };
}
#else
for (GLVolume *volume : vols)
// Reserving number of vertices (3x position + 3x color)
volume->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.tool_change(idx_layer);
for (size_t i = 0; i < vols.size(); ++i) {
GLVolume &vol = *vols[i];
if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) {
vol.print_zs.emplace_back(layer.front().print_z);
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
vol.offsets.emplace_back(geometries[i].indices_count());
#else
vol.offsets.emplace_back(vol.indexed_vertex_array.quad_indices.size());
vol.offsets.emplace_back(vol.indexed_vertex_array.triangle_indices.size());
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
}
for (const WipeTower::ToolChangeResult &extrusions : layer) {
@ -6623,21 +6811,42 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con
e_prev = e;
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
_3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z,
geometries[ctxt.volume_idx(e.tool, 0)]);
#else
_3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z,
*vols[ctxt.volume_idx(e.tool, 0)]);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
}
}
for (size_t i = 0; i < vols.size(); ++i) {
GLVolume &vol = *vols[i];
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
if (geometries[i].vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) {
vol.model.init_from(std::move(geometries[i]));
#else
if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) {
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
vols[i] = new_volume(vol.color);
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
reserve_new_volume_finalize_old_volume(*vols[i], vol, false);
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
for (size_t i = 0; i < vols.size(); ++i) {
if (!geometries[i].is_empty())
vols[i]->model.init_from(std::move(geometries[i]));
}
#else
for (GLVolume *vol : vols)
vol->indexed_vertex_array.shrink_to_fit();
});
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
});
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results" << m_volumes.log_memory_info() << log_memory_info();
// Remove empty volumes from the newly added volumes.
@ -6651,8 +6860,14 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con
}
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) {
GLVolume* v = m_volumes.volumes[i];
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v->is_outside = !contains(build_volume, v->model);
// We are done editinig the model, now it can be sent to gpu
v->model.enable_render();
#else
v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box());
v->indexed_vertex_array.finalize_geometry(m_initialized);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info();
@ -6676,11 +6891,21 @@ void GLCanvas3D::_load_sla_shells()
m_volumes.volumes.emplace_back(new GLVolume(color));
GLVolume& v = *m_volumes.volumes.back();
#if ENABLE_SMOOTH_NORMALS
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.model.init_from(mesh, true);
#else
v.indexed_vertex_array.load_mesh(mesh, true);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#else
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.model.init_from(mesh);
#else
v.indexed_vertex_array.load_mesh(mesh);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#endif // ENABLE_SMOOTH_NORMALS
#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.indexed_vertex_array.finalize_geometry(m_initialized);
#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled;
v.composite_id.volume_id = volume_id;
v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0.0));

View file

@ -799,7 +799,6 @@ public:
void do_move(const std::string& snapshot_type);
void do_rotate(const std::string& snapshot_type);
void do_scale(const std::string& snapshot_type);
void do_flatten(const Vec3d& normal, const std::string& snapshot_type);
void do_mirror(const std::string& snapshot_type);
void update_gizmos_on_off_state();

View file

@ -8,15 +8,56 @@
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/Polygon.hpp"
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#include "libslic3r/BuildVolume.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp"
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#include <boost/filesystem/operations.hpp>
#include <boost/algorithm/string/predicate.hpp>
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_SMOOTH_NORMALS
#include <igl/per_face_normals.h>
#include <igl/per_corner_normals.h>
#include <igl/per_vertex_normals.h>
#endif // ENABLE_SMOOTH_NORMALS
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#include <GL/glew.h>
namespace Slic3r {
namespace GUI {
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_SMOOTH_NORMALS
static void smooth_normals_corner(const TriangleMesh& mesh, std::vector<stl_normal>& normals)
{
using MapMatrixXfUnaligned = Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
using MapMatrixXiUnaligned = Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>;
std::vector<Vec3f> face_normals = its_face_normals(mesh.its);
Eigen::MatrixXd vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(),
Eigen::Index(mesh.its.vertices.size()), 3).cast<double>();
Eigen::MatrixXi indices = MapMatrixXiUnaligned(mesh.its.indices.front().data(),
Eigen::Index(mesh.its.indices.size()), 3);
Eigen::MatrixXd in_normals = MapMatrixXfUnaligned(face_normals.front().data(),
Eigen::Index(face_normals.size()), 3).cast<double>();
Eigen::MatrixXd out_normals;
igl::per_corner_normals(vertices, indices, in_normals, 1.0, out_normals);
normals = std::vector<stl_normal>(mesh.its.vertices.size());
for (size_t i = 0; i < mesh.its.indices.size(); ++i) {
for (size_t j = 0; j < 3; ++j) {
normals[mesh.its.indices[i][j]] = out_normals.row(i * 3 + j).cast<float>();
}
}
}
#endif // ENABLE_SMOOTH_NORMALS
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_GLBEGIN_GLEND_REMOVAL
void GLModel::Geometry::reserve_vertices(size_t vertices_count)
{
@ -207,6 +248,37 @@ Vec2f GLModel::Geometry::extract_tex_coord_2(size_t id) const
return { *(start + 0), *(start + 1) };
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void GLModel::Geometry::set_vertex(size_t id, const Vec3f& position, const Vec3f& normal)
{
assert(format.vertex_layout == EVertexLayout::P3N3);
assert(id < vertices_count());
if (id < vertices_count()) {
float* start = &vertices[id * vertex_stride_floats(format)];
*(start + 0) = position.x();
*(start + 1) = position.y();
*(start + 2) = position.z();
*(start + 3) = normal.x();
*(start + 4) = normal.y();
*(start + 5) = normal.z();
}
}
void GLModel::Geometry::set_ushort_index(size_t id, unsigned short index)
{
assert(id < indices_count());
if (id < indices_count())
::memcpy(indices.data() + id * sizeof(unsigned short), &index, sizeof(unsigned short));
}
void GLModel::Geometry::set_uint_index(size_t id, unsigned int index)
{
assert(id < indices_count());
if (id < indices_count())
::memcpy(indices.data() + id * sizeof(unsigned int), &index, sizeof(unsigned int));
}
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
unsigned int GLModel::Geometry::extract_uint_index(size_t id) const
{
if (format.index_type != EIndexType::UINT) {
@ -219,7 +291,7 @@ unsigned int GLModel::Geometry::extract_uint_index(size_t id) const
return -1;
}
unsigned int ret = -1;
unsigned int ret = (unsigned int)-1;
::memcpy(&ret, indices.data() + id * index_stride_bytes(format), sizeof(unsigned int));
return ret;
}
@ -236,11 +308,23 @@ unsigned short GLModel::Geometry::extract_ushort_index(size_t id) const
return -1;
}
unsigned short ret = -1;
unsigned short ret = (unsigned short)-1;
::memcpy(&ret, indices.data() + id * index_stride_bytes(format), sizeof(unsigned short));
return ret;
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void GLModel::Geometry::remove_vertex(size_t id)
{
assert(id < vertices_count());
if (id < vertices_count()) {
size_t stride = vertex_stride_floats(format);
std::vector<float>::iterator it = vertices.begin() + id * stride;
vertices.erase(it, it + stride);
}
}
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
size_t GLModel::Geometry::vertex_stride_floats(const Format& format)
{
switch (format.vertex_layout)
@ -461,10 +545,58 @@ void GLModel::init_from(const Geometry& data)
}
#if ENABLE_GLBEGIN_GLEND_REMOVAL
#if ENABLE_SMOOTH_NORMALS
void GLModel::init_from(const TriangleMesh& mesh, bool smooth_normals)
{
if (smooth_normals) {
if (is_initialized()) {
// call reset() if you want to reuse this model
assert(false);
return;
}
if (mesh.its.vertices.empty() || mesh.its.indices.empty()) {
assert(false);
return;
}
std::vector<stl_normal> normals;
smooth_normals_corner(mesh, normals);
const indexed_triangle_set& its = mesh.its;
Geometry& data = m_render_data.geometry;
data.format = { Geometry::EPrimitiveType::Triangles, Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(3 * its.indices.size()) };
data.reserve_vertices(3 * its.indices.size());
data.reserve_indices(3 * its.indices.size());
// vertices
for (size_t i = 0; i < its.vertices.size(); ++i) {
data.add_vertex(its.vertices[i], normals[i]);
}
// indices
for (size_t i = 0; i < its.indices.size(); ++i) {
const stl_triangle_vertex_indices& idx = its.indices[i];
if (data.format.index_type == GLModel::Geometry::EIndexType::USHORT)
data.add_ushort_triangle((unsigned short)idx(0), (unsigned short)idx(1), (unsigned short)idx(2));
else
data.add_uint_triangle((unsigned int)idx(0), (unsigned int)idx(1), (unsigned int)idx(2));
}
// update bounding box
for (size_t i = 0; i < vertices_count(); ++i) {
m_bounding_box.merge(m_render_data.geometry.extract_position_3(i).cast<double>());
}
}
else
init_from(mesh.its);
}
#else
void GLModel::init_from(const TriangleMesh& mesh)
{
init_from(mesh.its);
}
#endif // ENABLE_SMOOTH_NORMALS
void GLModel::init_from(const indexed_triangle_set& its)
#else
@ -484,21 +616,24 @@ void GLModel::init_from(const indexed_triangle_set& its, const BoundingBoxf3 &bb
}
Geometry& data = m_render_data.geometry;
data.format = { Geometry::EPrimitiveType::Triangles, Geometry::EVertexLayout::P3N3, Geometry::EIndexType::UINT };
data.format = { Geometry::EPrimitiveType::Triangles, Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(3 * its.indices.size()) };
data.reserve_vertices(3 * its.indices.size());
data.reserve_indices(3 * its.indices.size());
// vertices + indices
unsigned int vertices_counter = 0;
for (uint32_t i = 0; i < its.indices.size(); ++i) {
stl_triangle_vertex_indices face = its.indices[i];
stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] };
stl_vertex n = face_normal_normalized(vertex);
const stl_triangle_vertex_indices face = its.indices[i];
const stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] };
const stl_vertex n = face_normal_normalized(vertex);
for (size_t j = 0; j < 3; ++j) {
data.add_vertex(vertex[j], n);
}
vertices_counter += 3;
data.add_uint_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
if (data.format.index_type == GLModel::Geometry::EIndexType::USHORT)
data.add_ushort_triangle((unsigned short)vertices_counter - 3, (unsigned short)vertices_counter - 2, (unsigned short)vertices_counter - 1);
else
data.add_uint_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
}
// update bounding box
@ -721,6 +856,9 @@ void GLModel::render()
void GLModel::render() const
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
{
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
render(std::make_pair<size_t, size_t>(0, indices_count()));
#else
GLShaderProgram* shader = wxGetApp().get_current_shader();
#if ENABLE_GLBEGIN_GLEND_REMOVAL
@ -809,8 +947,71 @@ void GLModel::render() const
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void GLModel::render(const std::pair<size_t, size_t>& range)
{
if (m_render_disabled)
return;
if (range.second == range.first)
return;
GLShaderProgram* shader = wxGetApp().get_current_shader();
if (shader == nullptr)
return;
// sends data to gpu if not done yet
if (m_render_data.vbo_id == 0 || m_render_data.ibo_id == 0) {
if (m_render_data.geometry.vertices_count() > 0 && m_render_data.geometry.indices_count() > 0 && !send_to_gpu())
return;
}
const Geometry& data = m_render_data.geometry;
const GLenum mode = get_primitive_mode(data.format);
const GLenum index_type = get_index_type(data.format);
const size_t vertex_stride_bytes = Geometry::vertex_stride_bytes(data.format);
const bool position = Geometry::has_position(data.format);
const bool normal = Geometry::has_normal(data.format);
const bool tex_coord = Geometry::has_tex_coord(data.format);
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_render_data.vbo_id));
if (position) {
glsafe(::glVertexPointer(Geometry::position_stride_floats(data.format), GL_FLOAT, vertex_stride_bytes, (const void*)Geometry::position_offset_bytes(data.format)));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
}
if (normal) {
glsafe(::glNormalPointer(GL_FLOAT, vertex_stride_bytes, (const void*)Geometry::normal_offset_bytes(data.format)));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
}
if (tex_coord) {
glsafe(::glTexCoordPointer(Geometry::tex_coord_stride_floats(data.format), GL_FLOAT, vertex_stride_bytes, (const void*)Geometry::tex_coord_offset_bytes(data.format)));
glsafe(::glEnableClientState(GL_TEXTURE_COORD_ARRAY));
}
shader->set_uniform("uniform_color", data.color);
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_render_data.ibo_id));
glsafe(::glDrawElements(mode, range.second - range.first + 1, index_type, (const void*)(range.first * Geometry::index_stride_bytes(data.format))));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
if (tex_coord)
glsafe(::glDisableClientState(GL_TEXTURE_COORD_ARRAY));
if (normal)
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
if (position)
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#if ENABLE_GLBEGIN_GLEND_REMOVAL
void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count)
#else
@ -1027,6 +1228,62 @@ static void append_triangle(GLModel::Geometry& data, unsigned short v1, unsigned
}
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
template<typename Fn>
inline bool all_vertices_inside(const GLModel::Geometry& geometry, Fn fn)
{
const size_t position_stride_floats = geometry.position_stride_floats(geometry.format);
const size_t position_offset_floats = geometry.position_offset_floats(geometry.format);
assert(position_stride_floats == 3);
if (geometry.vertices.empty() || position_stride_floats != 3)
return false;
for (auto it = geometry.vertices.begin(); it != geometry.vertices.end(); ) {
it += position_offset_floats;
if (!fn({ *it, *(it + 1), *(it + 2) }))
return false;
it += (geometry.vertex_stride_floats(geometry.format) - position_offset_floats - position_stride_floats);
}
return true;
}
bool contains(const BuildVolume& volume, const GLModel& model, bool ignore_bottom)
{
static constexpr const double epsilon = BuildVolume::BedEpsilon;
switch (volume.type()) {
case BuildVolume::Type::Rectangle:
{
BoundingBox3Base<Vec3d> build_volume = volume.bounding_volume().inflated(epsilon);
if (volume.max_print_height() == 0.0)
build_volume.max.z() = std::numeric_limits<double>::max();
if (ignore_bottom)
build_volume.min.z() = -std::numeric_limits<double>::max();
const BoundingBoxf3& model_box = model.get_bounding_box();
return build_volume.contains(model_box.min) && build_volume.contains(model_box.max);
}
case BuildVolume::Type::Circle:
{
const Geometry::Circled& circle = volume.circle();
const Vec2f c = unscaled<float>(circle.center);
const float r = unscaled<double>(circle.radius) + float(epsilon);
const float r2 = sqr(r);
return volume.max_print_height() == 0.0 ?
all_vertices_inside(model.get_geometry(), [c, r2](const Vec3f& p) { return (to_2d(p) - c).squaredNorm() <= r2; }) :
all_vertices_inside(model.get_geometry(), [c, r2, z = volume.max_print_height() + epsilon](const Vec3f& p) { return (to_2d(p) - c).squaredNorm() <= r2 && p.z() <= z; });
}
case BuildVolume::Type::Convex:
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
case BuildVolume::Type::Custom:
return volume.max_print_height() == 0.0 ?
all_vertices_inside(model.get_geometry(), [&volume](const Vec3f& p) { return Geometry::inside_convex_polygon(volume.top_bottom_convex_hull_decomposition_bed(), to_2d(p).cast<double>()); }) :
all_vertices_inside(model.get_geometry(), [&volume, z = volume.max_print_height() + epsilon](const Vec3f& p) { return Geometry::inside_convex_polygon(volume.top_bottom_convex_hull_decomposition_bed(), to_2d(p).cast<double>()) && p.z() <= z; });
default:
return true;
}
}
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
GLModel::Geometry stilized_arrow(unsigned short resolution, float tip_radius, float tip_height, float stem_radius, float stem_height)
{
#if !ENABLE_GLBEGIN_GLEND_REMOVAL

View file

@ -14,6 +14,9 @@ namespace Slic3r {
class TriangleMesh;
class Polygon;
using Polygons = std::vector<Polygon>;
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
class BuildVolume;
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
namespace GUI {
@ -89,6 +92,13 @@ namespace GUI {
void add_vertex(const Vec3f& position, const Vec2f& tex_coord); // EVertexLayout::P3T2
void add_vertex(const Vec3f& position, const Vec3f& normal); // EVertexLayout::P3N3
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void set_vertex(size_t id, const Vec3f& position, const Vec3f& normal); // EVertexLayout::P3N3
void set_ushort_index(size_t id, unsigned short index);
void set_uint_index(size_t id, unsigned int index);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void add_ushort_index(unsigned short id);
void add_uint_index(unsigned int id);
@ -106,7 +116,11 @@ namespace GUI {
unsigned int extract_uint_index(size_t id) const;
unsigned short extract_ushort_index(size_t id) const;
bool is_empty() const { return vertices.empty() || indices.empty(); }
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void remove_vertex(size_t id);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
bool is_empty() const { return vertices_count() == 0 || indices_count() == 0; }
size_t vertices_count() const { return vertices.size() / vertex_stride_floats(format); }
size_t indices_count() const { return indices.size() / index_stride_bytes(format); }
@ -179,6 +193,16 @@ namespace GUI {
std::vector<RenderData> m_render_data;
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// By default the vertex and index buffers data are sent to gpu at the first call to render() method.
// If you need to initialize a model from outside the main thread, so that a call to render() may happen
// before the initialization is complete, use the methods:
// disable_render()
// ... do your initialization ...
// enable_render()
// to keep the data on cpu side until needed.
bool m_render_disabled{ false };
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
BoundingBoxf3 m_bounding_box;
std::string m_filename;
@ -197,8 +221,16 @@ namespace GUI {
size_t indices_size_bytes() const { return indices_count() * Geometry::index_stride_bytes(m_render_data.geometry.format); }
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
const Geometry& get_geometry() const { return m_render_data.geometry; }
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void init_from(Geometry&& data);
#if ENABLE_SMOOTH_NORMALS
void init_from(const TriangleMesh& mesh, bool smooth_normals = false);
#else
void init_from(const TriangleMesh& mesh);
#endif // ENABLE_SMOOTH_NORMALS
#else
void init_from(const Geometry& data);
void init_from(const indexed_triangle_set& its, const BoundingBoxf3& bbox);
@ -219,9 +251,15 @@ namespace GUI {
void reset();
#if ENABLE_GLBEGIN_GLEND_REMOVAL
void render();
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void render(const std::pair<size_t, size_t>& range);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
void render_instanced(unsigned int instances_vbo, unsigned int instances_count);
bool is_initialized() const { return vertices_count() > 0 && indices_count() > 0; }
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
bool is_empty() const { return m_render_data.geometry.is_empty(); }
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
#else
void render() const;
void render_instanced(unsigned int instances_vbo, unsigned int instances_count) const;
@ -232,6 +270,29 @@ namespace GUI {
const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; }
const std::string& get_filename() const { return m_filename; }
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
bool is_render_disabled() const { return m_render_disabled; }
void enable_render() { m_render_disabled = false; }
void disable_render() { m_render_disabled = true; }
size_t cpu_memory_used() const {
size_t ret = 0;
if (!m_render_data.geometry.vertices.empty())
ret += vertices_size_bytes();
if (!m_render_data.geometry.indices.empty())
ret += indices_size_bytes();
return ret;
}
size_t gpu_memory_used() const {
size_t ret = 0;
if (m_render_data.geometry.vertices.empty())
ret += vertices_size_bytes();
if (m_render_data.geometry.indices.empty())
ret += indices_size_bytes();
return ret;
}
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
private:
#if ENABLE_GLBEGIN_GLEND_REMOVAL
bool send_to_gpu();
@ -240,6 +301,10 @@ namespace GUI {
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
};
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
bool contains(const BuildVolume& volume, const GLModel& model, bool ignore_bottom = true);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
// create an arrow with cylindrical stem and conical tip, with the given dimensions and resolution
// the origin of the arrow is in the center of the stem cap
// the arrow has its axis of symmetry along the Z axis and is pointing upward

View file

@ -87,7 +87,7 @@ namespace GUI {
color[1] = (m_state == Select) ? 1.0f : 0.3f;
color[2] = 0.3f;
glsafe(::glColor3fv(color));
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
#endif // !ENABLE_GLBEGIN_GLEND_REMOVAL
glsafe(::glDisable(GL_DEPTH_TEST));

View file

@ -755,7 +755,7 @@ void GUI_App::post_init()
if (boost::algorithm::iends_with(filename, ".amf") ||
boost::algorithm::iends_with(filename, ".amf.xml") ||
boost::algorithm::iends_with(filename, ".3mf"))
this->plater()->set_project_filename(filename);
this->plater()->set_project_filename(from_u8(filename));
}
}
if (! this->init_params->extra_config.empty())
@ -1286,8 +1286,13 @@ bool GUI_App::on_init_inner()
else
load_current_presets();
// Save the active profiles as a "saved into project".
update_saved_preset_from_current_preset();
if (plater_ != nullptr) {
// Save the names of active presets and project specific config into ProjectDirtyStateManager.
plater_->reset_project_dirty_initial_presets();
// Update Project dirty state, update application title bar.
plater_->update_project_dirty_from_presets();
}
@ -2448,16 +2453,13 @@ void GUI_App::update_saved_preset_from_current_preset()
}
}
std::vector<std::pair<unsigned int, std::string>> GUI_App::get_selected_presets() const
std::vector<const PresetCollection*> GUI_App::get_active_preset_collections() const
{
std::vector<std::pair<unsigned int, std::string>> ret;
std::vector<const PresetCollection*> ret;
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
for (Tab* tab : tabs_list) {
if (tab->supports_printer_technology(printer_technology)) {
const PresetCollection* presets = tab->get_presets();
ret.push_back({ static_cast<unsigned int>(presets->type()), presets->get_selected_preset_name() });
}
}
for (const Tab* tab : tabs_list)
if (tab->supports_printer_technology(printer_technology))
ret.push_back(tab->get_presets());
return ret;
}

View file

@ -253,7 +253,7 @@ public:
bool has_unsaved_preset_changes() const;
bool has_current_preset_changes() const;
void update_saved_preset_from_current_preset();
std::vector<std::pair<unsigned int, std::string>> get_selected_presets() const;
std::vector<const PresetCollection*> get_active_preset_collections() const;
bool check_and_save_current_preset_changes(const wxString& caption, const wxString& header, bool remember_choice = true, bool use_dont_save_insted_of_discard = false);
void apply_keeped_preset_modifications();
bool check_and_keep_current_preset_changes(const wxString& caption, const wxString& header, int action_buttons, bool* postponed_apply_of_keeped_changes = nullptr);

View file

@ -4,7 +4,7 @@
namespace Slic3r {
namespace GUI {
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
enum class ECoordinatesType : unsigned char
{
World,
@ -72,7 +72,8 @@ private:
Enum m_value;
};
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
} // namespace Slic3r
} // namespace GUI

View file

@ -52,7 +52,7 @@ static choice_ctrl* create_word_local_combo(wxWindow *parent)
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
if (!wxOSX) temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
temp->Append(ObjectManipulation::coordinate_type_str(ECoordinatesType::World));
temp->Append(ObjectManipulation::coordinate_type_str(ECoordinatesType::Instance));
temp->Append(ObjectManipulation::coordinate_type_str(ECoordinatesType::Local));
@ -62,7 +62,7 @@ static choice_ctrl* create_word_local_combo(wxWindow *parent)
temp->Append(_L("Local coordinates"));
temp->SetSelection(0);
temp->SetValue(temp->GetString(0));
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
temp->SetToolTip(_L("Select coordinate space, in which the transformation will be performed."));
return temp;
@ -88,14 +88,14 @@ void msw_rescale_word_local_combo(choice_ctrl* combo)
// Set rescaled size
combo->SetSize(size);
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
combo->Append(ObjectManipulation::coordinate_type_str(ECoordinatesType::World));
combo->Append(ObjectManipulation::coordinate_type_str(ECoordinatesType::Instance));
combo->Append(ObjectManipulation::coordinate_type_str(ECoordinatesType::Local));
#else
combo->Append(_L("World coordinates"));
combo->Append(_L("Local coordinates"));
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
combo->SetValue(selection);
#else
@ -171,12 +171,12 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
// Add world local combobox
m_word_local_combo = create_word_local_combo(parent);
m_word_local_combo->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent& evt) {
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
this->set_coordinates_type(evt.GetString());
#else
this->set_world_coordinates(evt.GetSelection() != 1);
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
}), m_word_local_combo->GetId());
#endif // ENABLE_WORLD_COORDINATE
}), m_word_local_combo->GetId());
// Small trick to correct layouting in different view_mode :
// Show empty string of a same height as a m_word_local_combo, when m_word_local_combo is hidden
@ -353,11 +353,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
if (selection.is_single_volume_or_modifier()) {
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
const double min_z = get_volume_min_z(*volume);
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (!is_world_coordinates()) {
#else
if (!m_world_coordinates) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
const Vec3d diff = m_cache.position - volume->get_instance_transformation().get_matrix(true).inverse() * (min_z * Vec3d::UnitZ());
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Drop to bed"));
@ -383,11 +379,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
else if (selection.is_single_full_instance()) {
#if ENABLE_WORLD_COORDINATE
const double min_z = selection.get_scaled_instance_bounding_box().min.z();
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (!is_world_coordinates()) {
#else
if (!m_world_coordinates) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
const Vec3d diff = m_cache.position - volume->get_instance_transformation().get_matrix(true).inverse() * (min_z * Vec3d::UnitZ());
@ -428,17 +420,14 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
Selection& selection = canvas->get_selection();
#if ENABLE_WORLD_COORDINATE
if (selection.is_single_volume_or_modifier()) {
if (selection.is_single_volume_or_modifier())
#else
if (selection.is_single_volume() || selection.is_single_modifier()) {
if (selection.is_single_volume() || selection.is_single_modifier())
#endif // ENABLE_WORLD_COORDINATE
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(*selection.get_volume_idxs().begin()));
volume->set_volume_rotation(Vec3d::Zero());
}
const_cast<GLVolume*>(selection.get_volume(*selection.get_volume_idxs().begin()))->set_volume_rotation(Vec3d::Zero());
else if (selection.is_single_full_instance()) {
for (unsigned int idx : selection.get_volume_idxs()) {
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(idx));
volume->set_instance_rotation(Vec3d::Zero());
const_cast<GLVolume*>(selection.get_volume(idx))->set_instance_rotation(Vec3d::Zero());
}
}
else
@ -513,7 +502,6 @@ void ObjectManipulation::Show(const bool show)
#if ENABLE_WORLD_COORDINATE
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
bool show_world_local_combo = wxGetApp().get_mode() != comSimple && (selection.is_single_full_instance() || selection.is_single_volume_or_modifier());
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (selection.is_single_volume_or_modifier() && m_word_local_combo->GetCount() < 3) {
#ifdef __linux__
m_word_local_combo->Insert(coordinate_type_str(ECoordinatesType::Instance), 1);
@ -528,7 +516,6 @@ void ObjectManipulation::Show(const bool show)
m_word_local_combo->Select((int)ECoordinatesType::World);
this->set_coordinates_type(m_word_local_combo->GetString(m_word_local_combo->GetSelection()));
}
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#else
bool show_world_local_combo = wxGetApp().plater()->canvas3D()->get_selection().is_single_full_instance() && wxGetApp().get_mode() != comSimple;
#endif // ENABLE_WORLD_COORDINATE
@ -576,8 +563,7 @@ void ObjectManipulation::update_ui_from_settings()
}
m_check_inch->SetValue(m_imperial_units);
if (m_use_colors != (wxGetApp().app_config->get("color_mapinulation_panel") == "1"))
{
if (m_use_colors != (wxGetApp().app_config->get("color_mapinulation_panel") == "1")) {
m_use_colors = wxGetApp().app_config->get("color_mapinulation_panel") == "1";
// update colors for edit-boxes
int axis_id = 0;
@ -609,10 +595,10 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
m_new_rotate_label_string = L("Rotation");
m_new_scale_label_string = L("Scale factors");
#if !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if !ENABLE_WORLD_COORDINATE
if (wxGetApp().get_mode() == comSimple)
m_world_coordinates = true;
#endif // !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // !ENABLE_WORLD_COORDINATE
ObjectList* obj_list = wxGetApp().obj_list();
if (selection.is_single_full_instance()) {
@ -623,41 +609,41 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
#endif // !ENABLE_WORLD_COORDINATE
// Verify whether the instance rotation is multiples of 90 degrees, so that the scaling in world coordinates is possible.
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
if (is_world_coordinates() && !m_uniform_scale &&
#else
if (m_world_coordinates && ! m_uniform_scale &&
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
! Geometry::is_rotation_ninety_degrees(volume->get_instance_rotation())) {
// Manipulating an instance in the world coordinate system, rotation is not multiples of ninety degrees, therefore enforce uniform scaling.
m_uniform_scale = true;
m_lock_bnt->SetLock(true);
}
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
if (is_world_coordinates()) {
m_new_position = volume->get_instance_offset();
#else
if (m_world_coordinates) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
m_new_position = volume->get_instance_offset();
#endif // ENABLE_WORLD_COORDINATE
m_new_rotate_label_string = L("Rotate");
#if ENABLE_WORLD_COORDINATE
m_new_rotation = volume->get_instance_rotation() * (180.0 / M_PI);
#else
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
m_new_scale_label_string = L("Scale");
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
m_new_rotation = Vec3d::Zero();
#endif // ENABLE_WORLD_COORDINATE
m_new_size = selection.get_scaled_instance_bounding_box().size();
m_new_scale = m_new_size.cwiseProduct(selection.get_unscaled_instance_bounding_box().size().cwiseInverse()) * 100.0;
}
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
m_new_scale = Vec3d(100.0, 100.0, 100.0);
#else
m_new_scale = m_new_size.cwiseProduct(selection.get_unscaled_instance_bounding_box().size().cwiseInverse()) * 100.0;
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
}
else {
#if ENABLE_WORLD_COORDINATE
m_new_move_label_string = L("Translate");
m_new_position = Vec3d::Zero();
m_new_rotation = Vec3d::Zero();
#else
m_new_rotation = volume->get_instance_rotation() * (180.0 / M_PI);
#endif // ENABLE_WORLD_COORDINATE
m_new_rotation = volume->get_instance_rotation() * (180.0 / M_PI);
m_new_size = volume->get_instance_scaling_factor().cwiseProduct(wxGetApp().model().objects[volume->object_idx()]->raw_mesh_bounding_box().size());
m_new_scale = volume->get_instance_scaling_factor() * 100.0;
}
@ -682,11 +668,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
// the selection contains a single volume
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
#if ENABLE_WORLD_COORDINATE
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (is_world_coordinates()) {
#else
if (m_world_coordinates) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
const Geometry::Transformation trafo(volume->world_matrix());
#if ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET
@ -694,34 +676,46 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
#else
const Vec3d& offset = trafo.get_offset();
#endif // ENABLE_WORLD_COORDINATE_VOLUMES_LOCAL_OFFSET
// const Vec3d& mirror = trafo.get_mirror();
m_new_position = offset;
m_new_rotation = trafo.get_rotation() * (180.0 / M_PI);
m_new_size = volume->transformed_convex_hull_bounding_box(trafo.get_matrix()).size();
m_new_scale = m_new_size.cwiseProduct(volume->transformed_convex_hull_bounding_box(volume->get_instance_transformation().get_matrix() * volume->get_volume_transformation().get_matrix(false, false, true, false)).size().cwiseInverse()) * 100.0;
}
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
else if (is_local_coordinates()) {
m_new_position = Vec3d::Zero();
m_new_rotate_label_string = L("Rotate");
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
m_new_scale_label_string = L("Scale");
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
m_new_rotation = Vec3d::Zero();
m_new_size = volume->transformed_convex_hull_bounding_box(trafo.get_matrix()).size();
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
m_new_scale = Vec3d(100.0, 100.0, 100.0);
#else
m_new_scale = m_new_size.cwiseProduct(volume->transformed_convex_hull_bounding_box(volume->get_instance_transformation().get_matrix() * volume->get_volume_transformation().get_matrix(false, false, true, false)).size().cwiseInverse()) * 100.0;
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
}
else if (is_local_coordinates()) {
m_new_move_label_string = L("Translate");
m_new_position = Vec3d::Zero();
m_new_rotation = volume->get_volume_rotation() * (180.0 / M_PI);
m_new_scale = volume->get_volume_scaling_factor() * 100.0;
m_new_size = volume->get_volume_scaling_factor().cwiseProduct(volume->bounding_box().size());
}
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
else {
#endif // ENABLE_WORLD_COORDINATE
m_new_position = volume->get_volume_offset();
m_new_rotation = volume->get_volume_rotation() * (180.0 / M_PI);
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
m_new_size = volume->transformed_convex_hull_bounding_box(volume->get_volume_transformation().get_matrix()).size();
m_new_scale = m_new_size.cwiseProduct(volume->transformed_convex_hull_bounding_box(volume->get_volume_transformation().get_matrix(false, false, true, false)).size().cwiseInverse()) * 100.0;
m_new_rotate_label_string = L("Rotate");
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
m_new_scale_label_string = L("Scale");
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
m_new_rotation = Vec3d::Zero();
#if ENABLE_WORLD_COORDINATE
m_new_size = volume->transformed_convex_hull_bounding_box(volume->get_volume_transformation().get_matrix()).size();
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
m_new_scale = Vec3d(100.0, 100.0, 100.0);
#else
m_new_scale = m_new_size.cwiseProduct(volume->transformed_convex_hull_bounding_box(volume->get_volume_transformation().get_matrix(false, false, true, false)).size().cwiseInverse()) * 100.0;
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
}
#else
m_new_scale = volume->get_volume_scaling_factor() * 100.0;
m_new_size = volume->get_instance_scaling_factor().cwiseProduct(volume->get_volume_scaling_factor().cwiseProduct(volume->bounding_box().size()));
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
}
#endif // ENABLE_WORLD_COORDINATE
m_new_enabled = true;
}
@ -788,39 +782,43 @@ void ObjectManipulation::update_if_dirty()
update(m_cache.rotation, m_cache.rotation_rounded, meRotation, m_new_rotation);
}
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
Selection::EUniformScaleRequiredReason reason;
if (selection.requires_uniform_scale(&reason)) {
#else
if (selection.requires_uniform_scale()) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
#if !ENABLE_WORLD_COORDINATE_SCALE_REVISITED
m_lock_bnt->SetLock(true);
#endif // !ENABLE_WORLD_COORDINATE_SCALE_REVISITED
#if ENABLE_WORLD_COORDINATE
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
wxString tooltip;
if (selection.is_single_volume_or_modifier()) {
#if !ENABLE_WORLD_COORDINATE_SCALE_REVISITED
if (reason == Selection::EUniformScaleRequiredReason::VolumeNotAxisAligned_Instance)
tooltip = _L("You cannot use non-uniform scaling mode for parts non aligned with the instance local axes");
else if (reason == Selection::EUniformScaleRequiredReason::VolumeNotAxisAligned_World)
tooltip = _L("You cannot use non-uniform scaling mode for parts non aligned with the printer axes");
#endif // !ENABLE_WORLD_COORDINATE_SCALE_REVISITED
}
else if (selection.is_single_full_instance()) {
#if !ENABLE_WORLD_COORDINATE_SCALE_REVISITED
if (reason == Selection::EUniformScaleRequiredReason::InstanceNotAxisAligned_World)
tooltip = _L("You cannot use non-uniform scaling mode for instances non aligned with the printer axes");
else if (reason == Selection::EUniformScaleRequiredReason::VolumeNotAxisAligned_Instance)
tooltip = _L("You cannot use non-uniform scaling mode for instances containing non locally axis-aligned parts");
#endif // !ENABLE_WORLD_COORDINATE_SCALE_REVISITED
}
else
tooltip = _L("You cannot use non-uniform scaling mode for multiple objects/parts selection");
m_lock_bnt->SetToolTip(tooltip);
#else
m_lock_bnt->SetToolTip(_L("You cannot use non-uniform scaling mode for multiple objects/parts selection or non axis-aligned objects/parts"));
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#else
m_lock_bnt->SetToolTip(_L("You cannot use non-uniform scaling mode for multiple objects/parts selection"));
#endif // ENABLE_WORLD_COORDINATE
#if !ENABLE_WORLD_COORDINATE_SCALE_REVISITED
m_lock_bnt->disable();
#endif // !ENABLE_WORLD_COORDINATE_SCALE_REVISITED
}
else {
m_lock_bnt->SetLock(m_uniform_scale);
@ -828,22 +826,22 @@ void ObjectManipulation::update_if_dirty()
m_lock_bnt->enable();
}
#if !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if !ENABLE_WORLD_COORDINATE
{
int new_selection = m_world_coordinates ? 0 : 1;
if (m_word_local_combo->GetSelection() != new_selection)
m_word_local_combo->SetSelection(new_selection);
}
#endif // !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // !ENABLE_WORLD_COORDINATE
if (m_new_enabled)
m_og->enable();
else
m_og->disable();
if (!selection.is_dragging()) {
if (!wxGetApp().plater()->canvas3D()->is_dragging()) {
update_reset_buttons_visibility();
update_mirror_buttons_visibility();
update_mirror_buttons_visibility();
}
m_dirty = false;
@ -863,11 +861,15 @@ void ObjectManipulation::update_reset_buttons_visibility()
bool show_drop_to_bed = false;
#if ENABLE_WORLD_COORDINATE
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (m_coordinates_type != ECoordinatesType::Local && (selection.is_single_full_instance() || selection.is_single_volume_or_modifier())) {
#else
if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if ((m_coordinates_type == ECoordinatesType::World && selection.is_single_full_instance()) ||
(m_coordinates_type == ECoordinatesType::Instance && selection.is_single_volume_or_modifier())) {
const double min_z = selection.is_single_full_instance() ? selection.get_scaled_instance_bounding_box().min.z() :
get_volume_min_z(*selection.get_volume(*selection.get_volume_idxs().begin()));
show_drop_to_bed = std::abs(min_z) > EPSILON;
}
if (m_coordinates_type == ECoordinatesType::Local && (selection.is_single_full_instance() || selection.is_single_volume_or_modifier())) {
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
Vec3d rotation = Vec3d::Zero();
Vec3d scale = Vec3d::Ones();
@ -876,26 +878,28 @@ void ObjectManipulation::update_reset_buttons_visibility()
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
Vec3d rotation;
Vec3d scale;
#endif // ENABLE_WORLD_COORDINATE
double min_z = 0.0;
#endif // ENABLE_WORLD_COORDINATE
if (selection.is_single_full_instance()) {
rotation = volume->get_instance_rotation();
scale = volume->get_instance_scaling_factor();
#if !ENABLE_WORLD_COORDINATE
min_z = selection.get_scaled_instance_bounding_box().min.z();
#endif // !ENABLE_WORLD_COORDINATE
}
else {
rotation = volume->get_volume_rotation();
scale = volume->get_volume_scaling_factor();
#if !ENABLE_WORLD_COORDINATE
min_z = get_volume_min_z(*volume);
#endif // !ENABLE_WORLD_COORDINATE
}
show_rotation = !rotation.isApprox(Vec3d::Zero());
show_scale = !scale.isApprox(Vec3d::Ones());
#if ENABLE_WORLD_COORDINATE
show_drop_to_bed = std::abs(min_z) > EPSILON;
#else
#if !ENABLE_WORLD_COORDINATE
show_drop_to_bed = std::abs(min_z) > SINKING_Z_THRESHOLD;
#endif // ENABLE_WORLD_COORDINATE
#endif // !ENABLE_WORLD_COORDINATE
}
wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed] {
@ -925,11 +929,11 @@ void ObjectManipulation::update_mirror_buttons_visibility()
Selection& selection = canvas->get_selection();
std::array<MirrorButtonState, 3> new_states = {mbHidden, mbHidden, mbHidden};
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
if (is_local_coordinates()) {
#else
if (!m_world_coordinates) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
#if ENABLE_WORLD_COORDINATE
if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) {
#else
@ -999,7 +1003,7 @@ void ObjectManipulation::update_warning_icon_state(const MeshErrorsInfo& warning
m_fix_throught_netfab_bitmap->SetToolTip(tooltip);
}
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
wxString ObjectManipulation::coordinate_type_str(ECoordinatesType type)
{
switch (type)
@ -1010,7 +1014,7 @@ wxString ObjectManipulation::coordinate_type_str(ECoordinatesType type)
default: { assert(false); return _L("Unknown"); }
}
}
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
void ObjectManipulation::reset_settings_value()
{
@ -1034,17 +1038,12 @@ void ObjectManipulation::change_position_value(int axis, double value)
auto canvas = wxGetApp().plater()->canvas3D();
Selection& selection = canvas->get_selection();
selection.start_dragging();
selection.setup_cache();
#if ENABLE_WORLD_COORDINATE
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
selection.translate(position - m_cache.position, get_coordinates_type());
#else
selection.translate(position - m_cache.position, !m_world_coordinates);
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#else
selection.translate(position - m_cache.position, selection.requires_local_axes());
#endif // ENABLE_WORLD_COORDINATE
selection.stop_dragging();
canvas->do_move(L("Set Position"));
m_cache.position = position;
@ -1068,15 +1067,13 @@ void ObjectManipulation::change_rotation_value(int axis, double value)
if (selection.is_single_full_instance())
transformation_type.set_independent();
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (!is_world_coordinates()) {
#else
if (!m_world_coordinates) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
//FIXME Selection::rotate() does not process absolute rotations correctly: It does not recognize the axis index, which was changed.
// transformation_type.set_absolute();
if (is_local_coordinates()) {
transformation_type.set_local();
transformation_type.set_absolute();
}
if (is_instance_coordinates())
transformation_type.set_instance();
#else
if (selection.is_single_full_instance() || selection.requires_local_axes())
transformation_type.set_independent();
@ -1087,11 +1084,10 @@ void ObjectManipulation::change_rotation_value(int axis, double value)
}
#endif // ENABLE_WORLD_COORDINATE
selection.start_dragging();
selection.setup_cache();
selection.rotate(
(M_PI / 180.0) * (transformation_type.absolute() ? rotation : rotation - m_cache.rotation),
transformation_type);
selection.stop_dragging();
canvas->do_rotate(L("Set Orientation"));
m_cache.rotation = rotation;
@ -1146,15 +1142,19 @@ void ObjectManipulation::change_size_value(int axis, double value)
ref_size = Vec3d::Ones();
}
else if (selection.is_single_full_instance())
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
ref_size = is_world_coordinates() ?
#else
ref_size = m_world_coordinates ?
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
selection.get_unscaled_instance_bounding_box().size() :
wxGetApp().model().objects[selection.get_volume(*selection.get_volume_idxs().begin())->object_idx()]->raw_mesh_bounding_box().size();
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
this->do_size(axis, size.cwiseQuotient(ref_size));
#else
this->do_scale(axis, size.cwiseQuotient(ref_size));
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
m_cache.size = size;
m_cache.size_rounded(axis) = DBL_MAX;
@ -1170,21 +1170,23 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const
#if ENABLE_WORLD_COORDINATE
TransformationType transformation_type;
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (!is_world_coordinates())
#else
if (!m_world_coordinates)
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
if (is_local_coordinates())
transformation_type.set_local();
else if (is_instance_coordinates())
transformation_type.set_instance();
if (!is_local_coordinates())
transformation_type.set_relative();
#else
if (!is_world_coordinates())
transformation_type.set_local();
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
bool uniform_scale = m_uniform_scale || selection.requires_uniform_scale();
Vec3d scaling_factor = uniform_scale ? scale(axis) * Vec3d::Ones() : scale;
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (!uniform_scale && is_world_coordinates()) {
#else
if (!uniform_scale && m_world_coordinates) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (selection.is_single_full_instance())
scaling_factor = (Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse() * scaling_factor).cwiseAbs();
else if (selection.is_single_volume_or_modifier()) {
@ -1205,12 +1207,41 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const
scaling_factor = scale(axis) * Vec3d::Ones();
#endif // ENABLE_WORLD_COORDINATE
selection.start_dragging();
selection.setup_cache();
selection.scale(scaling_factor, transformation_type);
selection.stop_dragging();
wxGetApp().plater()->canvas3D()->do_scale(L("Set Scale"));
}
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
void ObjectManipulation::do_size(int axis, const Vec3d& scale) const
{
Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
TransformationType transformation_type;
if (is_local_coordinates())
transformation_type.set_local();
else if (is_instance_coordinates())
transformation_type.set_instance();
bool uniform_scale = m_uniform_scale || selection.requires_uniform_scale();
Vec3d scaling_factor = uniform_scale ? scale(axis) * Vec3d::Ones() : scale;
if (!uniform_scale && is_world_coordinates()) {
if (selection.is_single_full_instance())
scaling_factor = (Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse() * scaling_factor).cwiseAbs();
else if (selection.is_single_volume_or_modifier()) {
const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse();
const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation()).inverse();
scaling_factor = (mv * mi * scaling_factor).cwiseAbs();
}
}
selection.setup_cache();
selection.scale(scaling_factor, transformation_type);
wxGetApp().plater()->canvas3D()->do_scale(L("Set Size"));
}
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
void ObjectManipulation::on_change(const std::string& opt_key, int axis, double new_value)
{
if (!m_cache.is_valid())
@ -1245,21 +1276,36 @@ void ObjectManipulation::on_change(const std::string& opt_key, int axis, double
}
}
void ObjectManipulation::set_uniform_scaling(const bool new_value)
void ObjectManipulation::set_uniform_scaling(const bool use_uniform_scale)
{
const Selection &selection = wxGetApp().plater()->canvas3D()->get_selection();
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (selection.is_single_full_instance() && is_world_coordinates() && !new_value) {
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
if (!use_uniform_scale) {
int res = selection.bake_transform_if_needed();
if (res == -1) {
// Enforce uniform scaling.
m_lock_bnt->SetLock(true);
return;
}
else if (res == 0)
// Recalculate cached values at this panel, refresh the screen.
this->UpdateAndShow(true);
}
m_uniform_scale = use_uniform_scale;
#else
if (selection.is_single_full_instance() && m_world_coordinates && !new_value) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
if (selection.is_single_full_instance() && is_world_coordinates() && !use_uniform_scale) {
#else
if (selection.is_single_full_instance() && m_world_coordinates && !use_uniform_scale) {
#endif // ENABLE_WORLD_COORDINATE
// Verify whether the instance rotation is multiples of 90 degrees, so that the scaling in world coordinates is possible.
// all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
// Is the angle close to a multiple of 90 degrees?
if (! Geometry::is_rotation_ninety_degrees(volume->get_instance_rotation())) {
if (!Geometry::is_rotation_ninety_degrees(volume->get_instance_rotation())) {
// Cannot apply scaling in the world coordinate system.
//wxMessageDialog dlg(GUI::wxGetApp().mainframe,
//wxMessageDialog dlg(GUI::wxGetApp().mainframe,
MessageDialog dlg(GUI::wxGetApp().mainframe,
_L("The currently manipulated object is tilted (rotation angles are not multiples of 90°).\n"
"Non-uniform scaling of tilted objects is only possible in the World coordinate system,\n"
@ -1267,7 +1313,7 @@ void ObjectManipulation::set_uniform_scaling(const bool new_value)
_L("This operation is irreversible.\n"
"Do you want to proceed?"),
SLIC3R_APP_NAME,
wxYES_NO | wxCANCEL | wxCANCEL_DEFAULT | wxICON_QUESTION);
wxYES_NO | wxCANCEL | wxCANCEL_DEFAULT | wxICON_QUESTION);
if (dlg.ShowModal() != wxID_YES) {
// Enforce uniform scaling.
m_lock_bnt->SetLock(true);
@ -1281,11 +1327,12 @@ void ObjectManipulation::set_uniform_scaling(const bool new_value)
this->UpdateAndShow(true);
}
}
m_uniform_scale = new_value;
m_uniform_scale = use_uniform_scale;
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
}
#if ENABLE_WORLD_COORDINATE
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
void ObjectManipulation::set_coordinates_type(ECoordinatesType type)
{
if (wxGetApp().get_mode() == comSimple)
@ -1301,24 +1348,6 @@ void ObjectManipulation::set_coordinates_type(ECoordinatesType type)
canvas->set_as_dirty();
canvas->request_extra_frame();
}
#else
void ObjectManipulation::set_world_coordinates(const bool world_coordinates)
{
m_world_coordinates = world_coordinates;
this->UpdateAndShow(true);
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
canvas->get_gizmos_manager().update_data();
canvas->set_as_dirty();
canvas->request_extra_frame();
}
bool ObjectManipulation::get_world_coordinates() const
{
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
return wxGetApp().get_mode() != comSimple && (selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier()) ?
m_world_coordinates : true;
}
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
void ObjectManipulation::msw_rescale()
@ -1384,7 +1413,7 @@ void ObjectManipulation::sys_color_changed()
m_mirror_buttons[id].first->msw_rescale();
}
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
void ObjectManipulation::set_coordinates_type(const wxString& type_string)
{
ECoordinatesType type = ECoordinatesType::World;
@ -1395,7 +1424,7 @@ void ObjectManipulation::set_coordinates_type(const wxString& type_string)
this->set_coordinates_type(type);
}
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
static const char axes[] = { 'x', 'y', 'z' };
ManipulationEditor::ManipulationEditor(ObjectManipulation* parent,
@ -1438,8 +1467,8 @@ ManipulationEditor::ManipulationEditor(ObjectManipulation* parent,
parent->set_focused_editor(nullptr);
#if ENABLE_OBJECT_MANIPULATOR_FOCUS
// if the widgets exchanging focus are both manipulator fields, call kill_focus
if (dynamic_cast<ManipulationEditor*>(e.GetEventObject()) != nullptr && dynamic_cast<ManipulationEditor*>(e.GetWindow()) != nullptr)
// if the widgets loosing focus is a manipulator field, call kill_focus
if (dynamic_cast<ManipulationEditor*>(e.GetEventObject()) != nullptr)
#else
if (!m_enter_pressed)
#endif // ENABLE_OBJECT_MANIPULATOR_FOCUS

View file

@ -5,9 +5,9 @@
#include "GUI_ObjectSettings.hpp"
#include "GUI_ObjectList.hpp"
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
#include "GUI_Geometry.hpp"
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
#include "libslic3r/Point.hpp"
#include <float.h>
@ -151,12 +151,12 @@ private:
Vec3d m_new_size;
bool m_new_enabled {true};
bool m_uniform_scale {true};
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
ECoordinatesType m_coordinates_type{ ECoordinatesType::World };
#else
// Does the object manipulation panel work in World or Local coordinates?
bool m_world_coordinates = true;
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
LockButton* m_lock_bnt{ nullptr };
choice_ctrl* m_word_local_combo { nullptr };
@ -196,20 +196,14 @@ public:
// Called from the App to update the UI if dirty.
void update_if_dirty();
void set_uniform_scaling(const bool uniform_scale);
void set_uniform_scaling(const bool use_uniform_scale);
bool get_uniform_scaling() const { return m_uniform_scale; }
#if ENABLE_WORLD_COORDINATE
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
void set_coordinates_type(ECoordinatesType type);
ECoordinatesType get_coordinates_type() const { return m_coordinates_type; }
bool is_world_coordinates() const { return m_coordinates_type == ECoordinatesType::World; }
bool is_instance_coordinates() const { return m_coordinates_type == ECoordinatesType::Instance; }
bool is_local_coordinates() const { return m_coordinates_type == ECoordinatesType::Local; }
#else
// Does the object manipulation panel work in World or Local coordinates?
void set_world_coordinates(const bool world_coordinates);
bool get_world_coordinates() const;
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#else
// Does the object manipulation panel work in World or Local coordinates?
void set_world_coordinates(const bool world_coordinates) { m_world_coordinates = world_coordinates; this->UpdateAndShow(true); }
@ -243,9 +237,9 @@ public:
ManipulationEditor* get_focused_editor() { return m_focused_editor; }
#endif // ENABLE_WORLD_COORDINATE
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
static wxString coordinate_type_str(ECoordinatesType type);
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
private:
void reset_settings_value();
@ -262,10 +256,13 @@ private:
void change_scale_value(int axis, double value);
void change_size_value(int axis, double value);
void do_scale(int axis, const Vec3d &scale) const;
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
void do_size(int axis, const Vec3d& scale) const;
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
void set_coordinates_type(const wxString& type_string);
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
};
}}

View file

@ -141,6 +141,8 @@ bool ObjectSettings::update_settings_list()
{
Option option = optgroup->get_option(opt);
option.opt.width = 12;
if (!option.opt.full_label.empty())
option.opt.label = option.opt.full_label;
if (is_extruders_cat)
option.opt.max = wxGetApp().extruders_edited_cnt();
optgroup->append_single_option_line(option);

View file

@ -274,9 +274,13 @@ static void generate_thumbnail_from_model(const std::string& filename)
GLVolumeCollection volumes;
volumes.volumes.push_back(new GLVolume());
GLVolume* volume = volumes.volumes[0];
GLVolume* volume = volumes.volumes.back();
#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume->model.init_from(model.mesh());
#else
volume->indexed_vertex_array.load_mesh(model.mesh());
volume->indexed_vertex_array.finalize_geometry(true);
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
volume->set_instance_transformation(model.objects[0]->instances[0]->get_transformation());
volume->set_volume_transformation(model.objects[0]->volumes[0]->get_transformation());

View file

@ -5,6 +5,8 @@
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
// TODO: Display tooltips quicker on Linux
namespace Slic3r {
@ -74,63 +76,19 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, u
, m_first_input_window_render(true)
, m_dirty(false)
{
m_base_color = DEFAULT_BASE_COLOR;
m_drag_color = DEFAULT_DRAG_COLOR;
m_highlight_color = DEFAULT_HIGHLIGHT_COLOR;
}
void GLGizmoBase::set_hover_id(int id)
{
if (m_grabbers.empty() || id < (int)m_grabbers.size()) {
m_hover_id = id;
on_set_hover_id();
}
}
// do not change hover id during dragging
assert(!m_dragging);
void GLGizmoBase::enable_grabber(unsigned int id)
{
if (id < m_grabbers.size())
m_grabbers[id].enabled = true;
on_enable_grabber(id);
}
void GLGizmoBase::disable_grabber(unsigned int id)
{
if (id < m_grabbers.size())
m_grabbers[id].enabled = false;
on_disable_grabber(id);
}
void GLGizmoBase::start_dragging()
{
m_dragging = true;
for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
m_grabbers[i].dragging = (m_hover_id == i);
}
on_start_dragging();
}
void GLGizmoBase::stop_dragging()
{
m_dragging = false;
for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
m_grabbers[i].dragging = false;
}
on_stop_dragging();
}
void GLGizmoBase::update(const UpdateData& data)
{
if (m_hover_id != -1)
on_update(data);
// allow empty grabbers when not using grabbers but use hover_id - flatten, rotate
if (!m_grabbers.empty() && id >= (int) m_grabbers.size())
return;
m_hover_id = id;
on_set_hover_id();
}
bool GLGizmoBase::update_items_state()
@ -138,7 +96,7 @@ bool GLGizmoBase::update_items_state()
bool res = m_dirty;
m_dirty = false;
return res;
};
}
ColorRGBA GLGizmoBase::picking_color_component(unsigned int id) const
{
@ -189,6 +147,113 @@ void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
}
// help function to process grabbers
// call start_dragging, stop_dragging, on_dragging
bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) {
bool is_dragging_finished = false;
if (mouse_event.Moving()) {
// it should not happen but for sure
assert(!m_dragging);
if (m_dragging) is_dragging_finished = true;
else return false;
}
if (mouse_event.LeftDown()) {
Selection &selection = m_parent.get_selection();
if (!selection.is_empty() && m_hover_id != -1 &&
(m_grabbers.empty() || m_hover_id < static_cast<int>(m_grabbers.size()))) {
selection.setup_cache();
m_dragging = true;
for (auto &grabber : m_grabbers) grabber.dragging = false;
if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size()))
m_grabbers[m_hover_id].dragging = true;
// prevent change of hover_id during dragging
m_parent.set_mouse_as_dragging();
on_start_dragging();
// Let the plater know that the dragging started
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED));
m_parent.set_as_dirty();
return true;
}
} else if (m_dragging) {
// when mouse cursor leave window than finish actual dragging operation
bool is_leaving = mouse_event.Leaving();
if (mouse_event.Dragging()) {
m_parent.set_mouse_as_dragging();
Point mouse_coord(mouse_event.GetX(), mouse_event.GetY());
auto ray = m_parent.mouse_ray(mouse_coord);
UpdateData data(ray, mouse_coord);
on_dragging(data);
wxGetApp().obj_manipul()->set_dirty();
m_parent.set_as_dirty();
return true;
}
else if (mouse_event.LeftUp() || is_leaving || is_dragging_finished) {
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
do_stop_dragging(is_leaving);
#else
for (auto &grabber : m_grabbers) grabber.dragging = false;
m_dragging = false;
// NOTE: This should be part of GLCanvas3D
// Reset hover_id when leave window
if (is_leaving) m_parent.mouse_up_cleanup();
on_stop_dragging();
// There is prediction that after draggign, data are changed
// Data are updated twice also by canvas3D::reload_scene.
// Should be fixed.
m_parent.get_gizmos_manager().update_data();
wxGetApp().obj_manipul()->set_dirty();
// Let the plater know that the dragging finished, so a delayed
// refresh of the scene with the background processing data should
// be performed.
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
// updates camera target constraints
m_parent.refresh_camera_scene_box();
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
return true;
}
}
return false;
}
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
void GLGizmoBase::do_stop_dragging(bool perform_mouse_cleanup)
{
for (auto& grabber : m_grabbers) grabber.dragging = false;
m_dragging = false;
// NOTE: This should be part of GLCanvas3D
// Reset hover_id when leave window
if (perform_mouse_cleanup) m_parent.mouse_up_cleanup();
on_stop_dragging();
// There is prediction that after draggign, data are changed
// Data are updated twice also by canvas3D::reload_scene.
// Should be fixed.
m_parent.get_gizmos_manager().update_data();
wxGetApp().obj_manipul()->set_dirty();
// Let the plater know that the dragging finished, so a delayed
// refresh of the scene with the background processing data should
// be performed.
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
// updates camera target constraints
m_parent.refresh_camera_scene_box();
}
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
std::string GLGizmoBase::format(float value, unsigned int decimals) const
{
return Slic3r::string_printf("%.*f", decimals, value);

View file

@ -10,6 +10,7 @@
#include <cereal/archives/binary.hpp>
class wxWindow;
class wxMouseEvent;
namespace Slic3r {
@ -29,7 +30,6 @@ class ImGuiWrapper;
class GLCanvas3D;
enum class CommonGizmosDataID;
class CommonGizmosDataPool;
class Selection;
class GLGizmoBase
{
@ -88,28 +88,22 @@ public:
protected:
GLCanvas3D& m_parent;
int m_group_id;
int m_group_id; // TODO: remove only for rotate
EState m_state;
int m_shortcut_key;
std::string m_icon_filename;
unsigned int m_sprite_id;
int m_hover_id;
bool m_dragging;
ColorRGBA m_base_color;
ColorRGBA m_drag_color;
ColorRGBA m_highlight_color;
mutable std::vector<Grabber> m_grabbers;
ImGuiWrapper* m_imgui;
bool m_first_input_window_render;
mutable std::string m_tooltip;
CommonGizmosDataPool* m_c;
public:
GLGizmoBase(GLCanvas3D& parent,
const std::string& icon_filename,
unsigned int sprite_id);
virtual ~GLGizmoBase() {}
virtual ~GLGizmoBase() = default;
bool init() { return on_init(); }
@ -118,9 +112,6 @@ public:
std::string get_name(bool include_shortcut = true) const;
int get_group_id() const { return m_group_id; }
void set_group_id(int id) { m_group_id = id; }
EState get_state() const { return m_state; }
void set_state(EState state) { m_state = state; on_set_state(); }
@ -142,36 +133,34 @@ public:
int get_hover_id() const { return m_hover_id; }
void set_hover_id(int id);
void set_highlight_color(const ColorRGBA& color) { m_highlight_color = color; }
void enable_grabber(unsigned int id);
void disable_grabber(unsigned int id);
void start_dragging();
void stop_dragging();
bool is_dragging() const { return m_dragging; }
void update(const UpdateData& data);
// returns True when Gizmo changed its state
bool update_items_state();
void render() { m_tooltip.clear(); on_render(); }
void render() { on_render(); }
void render_for_picking() { on_render_for_picking(); }
void render_input_window(float x, float y, float bottom_limit);
virtual std::string get_tooltip() const { return ""; }
/// <summary>
/// Implement when you want process mouse events in gizmo
/// Mouse tooltip text
/// </summary>
/// <returns>Text to be visible in mouse tooltip</returns>
virtual std::string get_tooltip() const { return ""; }
/// <summary>
/// Is called when data (Selection) is changed
/// </summary>
virtual void data_changed(){};
/// <summary>
/// Implement when want to process mouse events in gizmo
/// Click, Right click, move, drag, ...
/// </summary>
/// <param name="mouse_event">Keep information about mouse + some action key like alt, shift, ...</param>
/// <returns>Return True when Gizmo use the information and don't want to propagate it. Otherwise False.</returns>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information and don't want to propagate it otherwise False.</returns>
virtual bool on_mouse(const wxMouseEvent &mouse_event) { return false; }
protected:
protected:
virtual bool on_init() = 0;
virtual void on_load(cereal::BinaryInputArchive& ar) {}
virtual void on_save(cereal::BinaryOutputArchive& ar) const {}
@ -183,9 +172,12 @@ public:
virtual CommonGizmosDataID on_get_requirements() const { return CommonGizmosDataID(0); }
virtual void on_enable_grabber(unsigned int id) {}
virtual void on_disable_grabber(unsigned int id) {}
// called inside use_grabbers
virtual void on_start_dragging() {}
virtual void on_stop_dragging() {}
virtual void on_update(const UpdateData& data) {}
virtual void on_dragging(const UpdateData& data) {}
virtual void on_render() = 0;
virtual void on_render_for_picking() = 0;
virtual void on_render_input_window(float x, float y, float bottom_limit) {}
@ -202,6 +194,20 @@ public:
// Mark gizmo as dirty to Re-Render when idle()
void set_dirty();
/// <summary>
/// function which
/// Set up m_dragging and call functions
/// on_start_dragging / on_dragging / on_stop_dragging
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>same as on_mouse</returns>
bool use_grabbers(const wxMouseEvent &mouse_event);
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
void do_stop_dragging(bool perform_mouse_cleanup);
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
private:
// Flag for dirty visible state of Gizmo
// When True then need new rendering

View file

@ -38,6 +38,11 @@ std::string GLGizmoCut::get_tooltip() const
return (m_hover_id == 0 || m_grabbers[0].dragging) ? "Z: " + format(cut_z, 2) : "";
}
bool GLGizmoCut::on_mouse(const wxMouseEvent &mouse_event)
{
return use_grabbers(mouse_event);
}
bool GLGizmoCut::on_init()
{
m_grabbers.emplace_back();
@ -53,7 +58,7 @@ std::string GLGizmoCut::on_get_name() const
void GLGizmoCut::on_set_state()
{
// Reset m_cut_z on gizmo activation
if (get_state() == On)
if (m_state == On)
m_cut_z = bounding_box().center().z();
}
@ -76,10 +81,10 @@ void GLGizmoCut::on_start_dragging()
m_drag_center.z() = m_cut_z;
}
void GLGizmoCut::on_update(const UpdateData& data)
void GLGizmoCut::on_dragging(const UpdateData &data)
{
if (m_hover_id != -1)
set_cut_z(m_start_z + calc_projection(data.mouse_ray));
assert(m_hover_id != -1);
set_cut_z(m_start_z + calc_projection(data.mouse_ray));
}
void GLGizmoCut::on_render()
@ -105,11 +110,15 @@ void GLGizmoCut::on_render()
GLShaderProgram* shader = wxGetApp().get_shader("flat");
if (shader != nullptr) {
shader->start_using();
Vec3d diff = plane_center - m_old_center;
// Z changed when move with cut plane
// X and Y changed when move with cutted object
bool is_changed = std::abs(diff.x()) > EPSILON ||
std::abs(diff.y()) > EPSILON ||
std::abs(diff.z()) > EPSILON;
m_old_center = plane_center;
const bool z_changed = std::abs(plane_center.z() - m_old_z) > EPSILON;
m_old_z = plane_center.z();
if (!m_plane.is_initialized() || z_changed) {
if (!m_plane.is_initialized() || is_changed) {
m_plane.reset();
GLModel::Geometry init_data;
@ -154,7 +163,7 @@ void GLGizmoCut::on_render()
glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f));
#if ENABLE_GLBEGIN_GLEND_REMOVAL
if (!m_grabber_connection.is_initialized() || z_changed) {
if (!m_grabber_connection.is_initialized() || is_changed) {
m_grabber_connection.reset();
GLModel::Geometry init_data;
@ -329,6 +338,8 @@ BoundingBoxf3 GLGizmoCut::bounding_box() const
BoundingBoxf3 ret;
const Selection& selection = m_parent.get_selection();
const Selection::IndicesList& idxs = selection.get_volume_idxs();
return selection.get_bounding_box();
for (unsigned int i : idxs) {
const GLVolume* volume = selection.get_volume(i);
if (!volume->is_modifier)
@ -367,6 +378,8 @@ void GLGizmoCut::update_contours()
MeshSlicingParams slicing_params;
slicing_params.trafo = first_glvolume->get_instance_transformation().get_matrix();
slicing_params.trafo.pretranslate(Vec3d(0., 0., first_glvolume->get_sla_shift_z()));
const Polygons polys = slice_mesh(m_cut_contours.mesh.its, m_cut_z, slicing_params);
if (!polys.empty()) {
m_cut_contours.contours.init_from(polys, static_cast<float>(m_cut_z));

View file

@ -8,6 +8,7 @@
namespace Slic3r {
namespace GUI {
class Selection;
class GLGizmoCut : public GLGizmoBase
{
@ -25,7 +26,7 @@ class GLGizmoCut : public GLGizmoBase
#if ENABLE_GLBEGIN_GLEND_REMOVAL
GLModel m_plane;
GLModel m_grabber_connection;
float m_old_z{ 0.0f };
Vec3d m_old_center;
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
struct CutContours
@ -50,6 +51,13 @@ public:
std::string get_tooltip() const override;
/// <summary>
/// Drag of plane
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
protected:
virtual bool on_init() override;
virtual void on_load(cereal::BinaryInputArchive& ar) override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); }
@ -58,7 +66,7 @@ protected:
virtual void on_set_state() override;
virtual bool on_is_activable() const override;
virtual void on_start_dragging() override;
virtual void on_update(const UpdateData& data) override;
virtual void on_dragging(const UpdateData& data) override;
virtual void on_render() override;
virtual void on_render_for_picking() override;
virtual void on_render_input_window(float x, float y, float bottom_limit) override;

View file

@ -21,9 +21,52 @@ static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
, m_normal(Vec3d::Zero())
, m_starting_center(Vec3d::Zero())
{}
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:
selection.flattening_rotate(m_planes[m_hover_id].normal);
m_parent.do_rotate(L("Gizmo-Place on Face"));
}
return true;
}
// fix: prevent restart gizmo when reselect object
// take responsibility for left up
if (m_parent.get_first_hover_volume_idx() >= 0) m_mouse_left_down = 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;
}
return false;
}
void GLGizmoFlatten::data_changed()
{
const Selection & selection = m_parent.get_selection();
const ModelObject *model_object = nullptr;
if (selection.is_single_full_instance() ||
selection.is_from_single_object() ) {
model_object = selection.get_model()->objects[selection.get_object_idx()];
}
set_flattening_data(model_object);
}
bool GLGizmoFlatten::on_init()
@ -53,15 +96,6 @@ bool GLGizmoFlatten::on_is_activable() const
return m_parent.get_selection().is_single_full_instance();
}
void GLGizmoFlatten::on_start_dragging()
{
if (m_hover_id != -1) {
assert(m_planes_valid);
m_normal = m_planes[m_hover_id].normal;
m_starting_center = m_parent.get_selection().get_bounding_box().center();
}
}
void GLGizmoFlatten::on_render()
{
const Selection& selection = m_parent.get_selection();
@ -149,7 +183,6 @@ void GLGizmoFlatten::on_render_for_picking()
void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
{
m_starting_center = Vec3d::Zero();
if (model_object != m_old_model_object) {
m_planes.clear();
m_planes_valid = false;
@ -415,13 +448,5 @@ bool GLGizmoFlatten::is_plane_update_necessary() const
return false;
}
Vec3d GLGizmoFlatten::get_flattening_normal() const
{
Vec3d out = m_normal;
m_normal = Vec3d::Zero();
m_starting_center = Vec3d::Zero();
return out;
}
} // namespace GUI
} // namespace Slic3r

View file

@ -22,7 +22,6 @@ class GLGizmoFlatten : public GLGizmoBase
// This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself.
private:
mutable Vec3d m_normal;
struct PlaneData {
std::vector<Vec3d> vertices; // should be in fact local in update_planes()
@ -42,8 +41,8 @@ private:
Vec3d m_first_instance_mirror;
std::vector<PlaneData> m_planes;
bool m_mouse_left_down = false; // for detection left_up of this gizmo
bool m_planes_valid = false;
mutable Vec3d m_starting_center;
const ModelObject* m_old_model_object = nullptr;
std::vector<const Transform3d*> instances_matrices;
@ -54,17 +53,23 @@ public:
GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
void set_flattening_data(const ModelObject* model_object);
Vec3d get_flattening_normal() const;
/// <summary>
/// Apply rotation on select plane
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
void data_changed() override;
protected:
virtual bool on_init() override;
virtual std::string on_get_name() const override;
virtual bool on_is_activable() const override;
virtual void on_start_dragging() override;
virtual void on_render() override;
virtual void on_render_for_picking() override;
virtual void on_set_state() override;
virtual CommonGizmosDataID on_get_requirements() const override;
bool on_init() override;
std::string on_get_name() const override;
bool on_is_activable() const override;
void on_render() override;
void on_render_for_picking() override;
void on_set_state() override;
CommonGizmosDataID on_get_requirements() const override;
};
} // namespace GUI

View file

@ -42,7 +42,7 @@ bool GLGizmoHollow::on_init()
return true;
}
void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&)
void GLGizmoHollow::data_changed()
{
if (! m_c->selection_info())
return;
@ -417,20 +417,70 @@ void GLGizmoHollow::delete_selected_points()
select_point(NoPoints);
}
void GLGizmoHollow::on_update(const UpdateData& data)
bool GLGizmoHollow::on_mouse(const wxMouseEvent &mouse_event)
{
sla::DrainHoles& drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
if (mouse_event.Moving()) return false;
if (use_grabbers(mouse_event)) return true;
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;
drain_holes[m_hover_id].pos = pos_and_normal.first;
drain_holes[m_hover_id].normal = -pos_and_normal.second;
// wxCoord == int --> wx/types.h
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
Vec2d mouse_pos = mouse_coord.cast<double>();
static bool pending_right_up = false;
if (mouse_event.LeftDown()) {
bool control_down = mouse_event.CmdDown();
bool grabber_contains_mouse = (get_hover_id() != -1);
if ((!control_down || grabber_contains_mouse) &&
gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false))
// the gizmo got the event and took some action, there is no need
// to do anything more
return true;
} else if (mouse_event.Dragging()) {
if (m_parent.get_move_volume_id() != -1)
// don't allow dragging objects with the Sla gizmo on
return true;
bool control_down = mouse_event.CmdDown();
if (control_down) {
// CTRL has been pressed while already dragging -> stop current action
if (mouse_event.LeftIsDown())
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), true);
else if (mouse_event.RightIsDown()) {
pending_right_up = false;
}
} else if(gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false)) {
// the gizmo got the event and took some action, no need to do
// anything more here
m_parent.set_as_dirty();
return true;
}
} else if (mouse_event.LeftUp()) {
if (!m_parent.is_mouse_dragging()) {
bool control_down = mouse_event.CmdDown();
// in case gizmo is selected, we just pass the LeftUp event
// and stop processing - neither object moving or selecting is
// suppressed in that case
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), control_down);
return true;
}
} else if (mouse_event.RightDown()) {
if (m_parent.get_selection().get_object_idx() != -1 &&
gizmo_event(SLAGizmoEventType::RightDown, mouse_pos, false, false, false)) {
// 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;
// event was taken care of by the SlaSupports gizmo
return true;
}
} else if (mouse_event.RightUp()) {
if (pending_right_up) {
pending_right_up = false;
return true;
}
}
return false;
}
void GLGizmoHollow::hollow_mesh(bool postpone_error_messages)
{
wxGetApp().CallAfter([this, postpone_error_messages]() {
@ -820,6 +870,17 @@ void GLGizmoHollow::on_stop_dragging()
}
void GLGizmoHollow::on_dragging(const UpdateData &data)
{
assert(m_hover_id != -1);
std::pair<Vec3f, Vec3f> pos_and_normal;
if (!unproject_on_mesh(data.mouse_pos.cast<double>(), pos_and_normal))
return;
sla::DrainHoles &drain_holes = m_c->selection_info()->model_object()->sla_drain_holes;
drain_holes[m_hover_id].pos = pos_and_normal.first;
drain_holes[m_hover_id].normal = -pos_and_normal.second;
}
void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar)
{

View file

@ -19,7 +19,7 @@ class ConfigOptionDef;
namespace GUI {
enum class SLAGizmoEventType : unsigned char;
class Selection;
class GLGizmoHollow : public GLGizmoBase
{
private:
@ -29,16 +29,22 @@ private:
public:
GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
virtual ~GLGizmoHollow() = default;
void set_sla_support_data(ModelObject* model_object, const Selection& selection);
void data_changed() override;
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
void delete_selected_points();
bool is_selection_rectangle_dragging() const {
return m_selection_rectangle.is_dragging();
}
/// <summary>
/// Postpone to Grabber for move
/// Detect move of object by dragging
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
private:
bool on_init() override;
void on_update(const UpdateData& data) override;
void on_render() override;
void on_render_for_picking() override;
@ -94,6 +100,7 @@ protected:
void on_set_hover_id() override;
void on_start_dragging() override;
void on_stop_dragging() override;
void on_dragging(const UpdateData &data) override;
void on_render_input_window(float x, float y, float bottom_limit) override;
virtual CommonGizmosDataID on_get_requirements() const override;

View file

@ -146,10 +146,9 @@ void GLGizmoMmuSegmentation::render_painter_gizmo()
glsafe(::glDisable(GL_BLEND));
}
void GLGizmoMmuSegmentation::set_painter_gizmo_data(const Selection &selection)
void GLGizmoMmuSegmentation::data_changed()
{
GLGizmoPainterBase::set_painter_gizmo_data(selection);
GLGizmoPainterBase::data_changed();
if (m_state != On || wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptFFF || wxGetApp().extruders_edited_cnt() <= 1)
return;

View file

@ -87,7 +87,7 @@ public:
void render_painter_gizmo() override;
void set_painter_gizmo_data(const Selection& selection) override;
void data_changed() override;
void render_triangles(const Selection& selection) const override;

View file

@ -46,6 +46,14 @@ std::string GLGizmoMove3D::get_tooltip() const
#endif // ENABLE_WORLD_COORDINATE
}
bool GLGizmoMove3D::on_mouse(const wxMouseEvent &mouse_event) {
return use_grabbers(mouse_event);
}
void GLGizmoMove3D::data_changed() {
m_grabbers[2].enabled = !m_parent.get_selection().is_wipe_tower();
}
bool GLGizmoMove3D::on_init()
{
for (int i = 0; i < 3; ++i) {
@ -69,46 +77,41 @@ bool GLGizmoMove3D::on_is_activable() const
void GLGizmoMove3D::on_start_dragging()
{
if (m_hover_id != -1) {
m_displacement = Vec3d::Zero();
assert(m_hover_id != -1);
m_displacement = Vec3d::Zero();
#if ENABLE_WORLD_COORDINATE
const Selection& selection = m_parent.get_selection();
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type();
if (coordinates_type == ECoordinatesType::World)
#else
if (wxGetApp().obj_manipul()->get_world_coordinates())
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
m_starting_drag_position = m_center + m_grabbers[m_hover_id].center;
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
else if (coordinates_type == ECoordinatesType::Local && selection.is_single_volume_or_modifier()) {
const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin());
m_starting_drag_position = m_center + Geometry::assemble_transform(Vec3d::Zero(), v.get_instance_rotation()) * Geometry::assemble_transform(Vec3d::Zero(), v.get_volume_rotation()) * m_grabbers[m_hover_id].center;
}
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
else {
const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin());
m_starting_drag_position = m_center + Geometry::assemble_transform(Vec3d::Zero(), v.get_instance_rotation()) * m_grabbers[m_hover_id].center;
}
m_starting_box_center = m_center;
m_starting_box_bottom_center = m_center;
m_starting_box_bottom_center.z() = m_bounding_box.min.z();
#else
const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box();
m_starting_drag_position = m_grabbers[m_hover_id].center;
m_starting_box_center = box.center();
m_starting_box_bottom_center = box.center();
m_starting_box_bottom_center.z() = box.min.z();
#endif // ENABLE_WORLD_COORDINATE
const Selection& selection = m_parent.get_selection();
const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type();
if (coordinates_type == ECoordinatesType::World)
m_starting_drag_position = m_center + m_grabbers[m_hover_id].center;
else if (coordinates_type == ECoordinatesType::Local && selection.is_single_volume_or_modifier()) {
const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin());
m_starting_drag_position = m_center + Geometry::assemble_transform(Vec3d::Zero(), v.get_instance_rotation()) * Geometry::assemble_transform(Vec3d::Zero(), v.get_volume_rotation()) * m_grabbers[m_hover_id].center;
}
else {
const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin());
m_starting_drag_position = m_center + Geometry::assemble_transform(Vec3d::Zero(), v.get_instance_rotation()) * m_grabbers[m_hover_id].center;
}
m_starting_box_center = m_center;
m_starting_box_bottom_center = m_center;
m_starting_box_bottom_center.z() = m_bounding_box.min.z();
#else
const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box();
m_starting_drag_position = m_grabbers[m_hover_id].center;
m_starting_box_center = box.center();
m_starting_box_bottom_center = box.center();
m_starting_box_bottom_center.z() = box.min.z();
#endif // ENABLE_WORLD_COORDINATE
}
void GLGizmoMove3D::on_stop_dragging()
{
m_parent.do_move(L("Gizmo-Move"));
m_displacement = Vec3d::Zero();
}
void GLGizmoMove3D::on_update(const UpdateData& data)
void GLGizmoMove3D::on_dragging(const UpdateData& data)
{
if (m_hover_id == 0)
m_displacement.x() = calc_projection(data);
@ -116,6 +119,13 @@ void GLGizmoMove3D::on_update(const UpdateData& data)
m_displacement.y() = calc_projection(data);
else if (m_hover_id == 2)
m_displacement.z() = calc_projection(data);
Selection &selection = m_parent.get_selection();
#if ENABLE_WORLD_COORDINATE
selection.translate(m_displacement, wxGetApp().obj_manipul()->get_coordinates_type());
#else
selection.translate(m_displacement);
#endif // ENABLE_WORLD_COORDINATE
}
void GLGizmoMove3D::on_render()
@ -397,7 +407,6 @@ void GLGizmoMove3D::transform_to_local(const Selection& selection) const
{
glsafe(::glTranslated(m_center.x(), m_center.y(), m_center.z()));
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (!wxGetApp().obj_manipul()->is_world_coordinates()) {
const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin());
Transform3d orient_matrix = v.get_instance_transformation().get_matrix(true, false, true, true);
@ -405,33 +414,21 @@ void GLGizmoMove3D::transform_to_local(const Selection& selection) const
orient_matrix = orient_matrix * v.get_volume_transformation().get_matrix(true, false, true, true);
glsafe(::glMultMatrixd(orient_matrix.data()));
}
#else
if (!wxGetApp().obj_manipul()->get_world_coordinates()) {
const Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true);
glsafe(::glMultMatrixd(orient_matrix.data()));
}
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
}
void GLGizmoMove3D::calc_selection_box_and_center()
{
const Selection& selection = m_parent.get_selection();
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type();
if (coordinates_type == ECoordinatesType::World) {
#else
if (wxGetApp().obj_manipul()->get_world_coordinates()) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
m_bounding_box = selection.get_bounding_box();
m_center = m_bounding_box.center();
}
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
else if (coordinates_type == ECoordinatesType::Local && selection.is_single_volume_or_modifier()) {
const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin());
m_bounding_box = v.transformed_convex_hull_bounding_box(v.get_instance_transformation().get_matrix(true, true, false, true) * v.get_volume_transformation().get_matrix(true, true, false, true));
m_center = v.world_matrix() * m_bounding_box.center();
}
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
else {
m_bounding_box.reset();
const Selection::IndicesList& ids = selection.get_volume_idxs();

View file

@ -7,6 +7,10 @@
namespace Slic3r {
namespace GUI {
#if ENABLE_WORLD_COORDINATE
class Selection;
#endif // ENABLE_WORLD_COORDINATE
class GLGizmoMove3D : public GLGizmoBase
{
static const double Offset;
@ -38,19 +42,28 @@ public:
double get_snap_step(double step) const { return m_snap_step; }
void set_snap_step(double step) { m_snap_step = step; }
const Vec3d& get_displacement() const { return m_displacement; }
std::string get_tooltip() const override;
/// <summary>
/// Postpone to Grabber for move
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
/// <summary>
/// Detect reduction of move for wipetover on selection change
/// </summary>
void data_changed() override;
protected:
virtual bool on_init() override;
virtual std::string on_get_name() const override;
virtual bool on_is_activable() const override;
virtual void on_start_dragging() override;
virtual void on_stop_dragging() override;
virtual void on_update(const UpdateData& data) override;
virtual void on_render() override;
virtual void on_render_for_picking() override;
bool on_init() override;
std::string on_get_name() const override;
bool on_is_activable() const override;
void on_start_dragging() override;
void on_stop_dragging() override;
void on_dragging(const UpdateData& data) override;
void on_render() override;
void on_render_for_picking() override;
private:
double calc_projection(const UpdateData& data) const;

View file

@ -40,13 +40,13 @@ GLGizmoPainterBase::~GLGizmoPainterBase()
#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL
}
void GLGizmoPainterBase::set_painter_gizmo_data(const Selection& selection)
void GLGizmoPainterBase::data_changed()
{
if (m_state != On)
return;
const ModelObject* mo = m_c->selection_info() ? m_c->selection_info()->model_object() : nullptr;
const Selection & selection = m_parent.get_selection();
if (mo && selection.is_from_single_instance()
&& (m_schedule_update || mo->id() != m_old_mo_id || mo->volumes.size() != m_old_volumes_size))
{
@ -641,7 +641,72 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
return false;
}
bool GLGizmoPainterBase::on_mouse(const wxMouseEvent &mouse_event)
{
// wxCoord == int --> wx/types.h
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
Vec2d mouse_pos = mouse_coord.cast<double>();
if (mouse_event.Moving()) {
gizmo_event(SLAGizmoEventType::Moving, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false);
return false;
}
// when control is down we allow scene pan and rotation even when clicking
// over some object
bool control_down = mouse_event.CmdDown();
bool grabber_contains_mouse = (get_hover_id() != -1);
const Selection &selection = m_parent.get_selection();
int selected_object_idx = selection.get_object_idx();
if (mouse_event.LeftDown()) {
if ((!control_down || grabber_contains_mouse) &&
gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false))
// the gizmo got the event and took some action, there is no need
// to do anything more
return true;
} else if (mouse_event.RightDown()){
if (!control_down && selected_object_idx != -1 &&
gizmo_event(SLAGizmoEventType::RightDown, mouse_pos, false, false, false))
// event was taken care of
return true;
} else if (mouse_event.Dragging()) {
if (m_parent.get_move_volume_id() != -1)
// don't allow dragging objects with the Sla gizmo on
return true;
if (!control_down && gizmo_event(SLAGizmoEventType::Dragging,
mouse_pos, mouse_event.ShiftDown(),
mouse_event.AltDown(), false)) {
// the gizmo got the event and took some action, no need to do
// anything more here
m_parent.set_as_dirty();
return true;
}
if(control_down && (mouse_event.LeftIsDown() || mouse_event.RightIsDown()))
{
// CTRL has been pressed while already dragging -> stop current action
if (mouse_event.LeftIsDown())
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), true);
else if (mouse_event.RightIsDown())
gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), true);
return false;
}
} else if (mouse_event.LeftUp()) {
if (!m_parent.is_mouse_dragging()) {
// in case SLA/FDM gizmo is selected, we just pass the LeftUp
// event and stop processing - neither object moving or selecting
// is suppressed in that case
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), control_down);
return true;
}
} else if (mouse_event.RightUp()) {
if (!m_parent.is_mouse_dragging()) {
gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), control_down);
return true;
}
}
return false;
}
void GLGizmoPainterBase::update_raycast_cache(const Vec2d& mouse_position,
const Camera& camera,

View file

@ -25,6 +25,7 @@ enum class SLAGizmoEventType : unsigned char;
class ClippingPlane;
struct Camera;
class GLGizmoMmuSegmentation;
class Selection;
enum class PainterGizmoType {
FDM_SUPPORTS,
@ -135,8 +136,8 @@ private:
void on_render_for_picking() override {}
public:
GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
virtual ~GLGizmoPainterBase() override;
virtual void set_painter_gizmo_data(const Selection& selection);
~GLGizmoPainterBase() override;
void data_changed() override;
virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
// Following function renders the triangles and cursor. Having this separated
@ -149,6 +150,15 @@ public:
virtual const float get_cursor_radius_max() const { return CursorRadiusMax; }
virtual const float get_cursor_radius_step() const { return CursorRadiusStep; }
/// <summary>
/// Implement when want to process mouse events in gizmo
/// Click, Right click, move, drag, ...
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information and don't want to
/// propagate it otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
protected:
virtual void render_triangles(const Selection& selection) const;
void render_cursor();
@ -257,9 +267,6 @@ private:
protected:
void on_set_state() override;
void on_start_dragging() override {}
void on_stop_dragging() override {}
virtual void on_opening() = 0;
virtual void on_shutdown() = 0;
virtual PainterGizmoType get_painter_type() const = 0;

View file

@ -31,7 +31,23 @@ const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius
GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis)
: GLGizmoBase(parent, "", -1)
, m_axis(axis)
{}
, m_angle(0.0)
, m_center(0.0, 0.0, 0.0)
, m_radius(0.0f)
, m_snap_coarse_in_radius(0.0f)
, m_snap_coarse_out_radius(0.0f)
, m_snap_fine_in_radius(0.0f)
, m_snap_fine_out_radius(0.0f)
, m_drag_color(DEFAULT_DRAG_COLOR)
, m_highlight_color(DEFAULT_HIGHLIGHT_COLOR)
{
m_group_id = static_cast<int>(axis);
}
void GLGizmoRotate::set_highlight_color(const ColorRGBA &color)
{
m_highlight_color = color;
}
void GLGizmoRotate::set_angle(double angle)
{
@ -53,6 +69,28 @@ std::string GLGizmoRotate::get_tooltip() const
return (m_hover_id == 0 || m_grabbers.front().dragging) ? axis + ": " + format(float(Geometry::rad2deg(m_angle)), 4) : "";
}
bool GLGizmoRotate::on_mouse(const wxMouseEvent &mouse_event)
{
return use_grabbers(mouse_event);
}
void GLGizmoRotate::dragging(const UpdateData &data) { on_dragging(data); }
void GLGizmoRotate::start_dragging()
{
m_grabbers[0].dragging = true;
on_start_dragging();
}
void GLGizmoRotate::stop_dragging()
{
m_grabbers[0].dragging = false;
on_stop_dragging();
}
void GLGizmoRotate::enable_grabber() { m_grabbers[0].enabled = true; }
void GLGizmoRotate::disable_grabber() { m_grabbers[0].enabled = false; }
bool GLGizmoRotate::on_init()
{
m_grabbers.push_back(Grabber());
@ -74,7 +112,7 @@ void GLGizmoRotate::on_start_dragging()
#endif // ENABLE_WORLD_COORDINATE
}
void GLGizmoRotate::on_update(const UpdateData& data)
void GLGizmoRotate::on_dragging(const UpdateData &data)
{
const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, m_parent.get_selection()));
@ -218,22 +256,16 @@ void GLGizmoRotate::on_render_for_picking()
#if ENABLE_WORLD_COORDINATE
void GLGizmoRotate::init_data_from_selection(const Selection& selection)
{
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type();
if (coordinates_type == ECoordinatesType::World) {
#else
if (wxGetApp().obj_manipul()->get_world_coordinates()) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
m_bounding_box = selection.get_bounding_box();
m_center = m_bounding_box.center();
}
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
else if (coordinates_type == ECoordinatesType::Local && selection.is_single_volume_or_modifier()) {
const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin());
m_bounding_box = v.transformed_convex_hull_bounding_box(v.get_instance_transformation().get_matrix(true, true, false, true) * v.get_volume_transformation().get_matrix(true, true, false, true));
m_center = v.world_matrix() * m_bounding_box.center();
}
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
else {
m_bounding_box.reset();
const Selection::IndicesList& ids = selection.get_volume_idxs();
@ -251,7 +283,6 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection)
m_snap_fine_in_radius = m_radius;
m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth;
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (coordinates_type == ECoordinatesType::World)
m_orient_matrix = Transform3d::Identity();
else if (coordinates_type == ECoordinatesType::Local && selection.is_single_volume_or_modifier()) {
@ -262,12 +293,6 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection)
const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin());
m_orient_matrix = v.get_instance_transformation().get_matrix(true, false, true, true);
}
#else
if (wxGetApp().obj_manipul()->get_world_coordinates())
m_orient_matrix = Transform3d::Identity();
else
m_orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true);
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
}
#endif // ENABLE_WORLD_COORDINATE
@ -710,25 +735,60 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, cons
GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
, m_gizmos({ GLGizmoRotate(parent, GLGizmoRotate::X), GLGizmoRotate(parent, GLGizmoRotate::Y), GLGizmoRotate(parent, GLGizmoRotate::Z) })
{
for (unsigned int i = 0; i < 3; ++i) {
m_gizmos[i].set_group_id(i);
}
, m_gizmos({
GLGizmoRotate(parent, GLGizmoRotate::X),
GLGizmoRotate(parent, GLGizmoRotate::Y),
GLGizmoRotate(parent, GLGizmoRotate::Z) })
{}
load_rotoptimize_state();
bool GLGizmoRotate3D::on_mouse(const wxMouseEvent &mouse_event)
{
if (mouse_event.Dragging() && m_dragging) {
// Apply new temporary rotations
#if ENABLE_WORLD_COORDINATE
TransformationType transformation_type;
switch (wxGetApp().obj_manipul()->get_coordinates_type())
{
default:
case ECoordinatesType::World: { transformation_type = TransformationType::World_Relative_Joint; break; }
case ECoordinatesType::Instance: { transformation_type = TransformationType::Instance_Relative_Joint; break; }
case ECoordinatesType::Local: { transformation_type = TransformationType::Local_Relative_Joint; break; }
}
#else
TransformationType transformation_type(TransformationType::World_Relative_Joint);
#endif // ENABLE_WORLD_COORDINATE
if (mouse_event.AltDown()) transformation_type.set_independent();
m_parent.get_selection().rotate(get_rotation(), transformation_type);
}
return use_grabbers(mouse_event);
}
void GLGizmoRotate3D::data_changed() {
const Selection &selection = m_parent.get_selection();
bool is_wipe_tower = selection.is_wipe_tower();
if (is_wipe_tower) {
DynamicPrintConfig& config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
float wipe_tower_rotation_angle =
dynamic_cast<const ConfigOptionFloat *>(
config.option("wipe_tower_rotation_angle"))
->value;
set_rotation(Vec3d(0., 0., (M_PI / 180.) * wipe_tower_rotation_angle));
m_gizmos[0].disable_grabber();
m_gizmos[1].disable_grabber();
} else {
set_rotation(Vec3d::Zero());
m_gizmos[0].enable_grabber();
m_gizmos[1].enable_grabber();
}
}
bool GLGizmoRotate3D::on_init()
{
for (GLGizmoRotate& g : m_gizmos) {
if (!g.init())
return false;
}
for (GLGizmoRotate& g : m_gizmos)
if (!g.init()) return false;
for (unsigned int i = 0; i < 3; ++i) {
for (unsigned int i = 0; i < 3; ++i)
m_gizmos[i].set_highlight_color(AXES_COLOR[i]);
}
m_shortcut_key = WXK_CONTROL_R;
@ -747,14 +807,21 @@ bool GLGizmoRotate3D::on_is_activable() const
void GLGizmoRotate3D::on_start_dragging()
{
if (0 <= m_hover_id && m_hover_id < 3)
m_gizmos[m_hover_id].start_dragging();
assert(0 <= m_hover_id && m_hover_id < 3);
m_gizmos[m_hover_id].start_dragging();
}
void GLGizmoRotate3D::on_stop_dragging()
{
if (0 <= m_hover_id && m_hover_id < 3)
m_gizmos[m_hover_id].stop_dragging();
assert(0 <= m_hover_id && m_hover_id < 3);
m_parent.do_rotate(L("Gizmo-Rotate"));
m_gizmos[m_hover_id].stop_dragging();
}
void GLGizmoRotate3D::on_dragging(const UpdateData &data)
{
assert(0 <= m_hover_id && m_hover_id < 3);
m_gizmos[m_hover_id].dragging(data);
}
void GLGizmoRotate3D::on_render()

View file

@ -5,7 +5,7 @@
namespace Slic3r {
namespace GUI {
class Selection;
class GLGizmoRotate : public GLGizmoBase
{
static const float Offset;
@ -20,9 +20,9 @@ class GLGizmoRotate : public GLGizmoBase
public:
enum Axis : unsigned char
{
X,
Y,
Z
X=0,
Y=1,
Z=2
};
private:
@ -56,6 +56,8 @@ private:
float m_old_hover_radius{ 0.0f };
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
ColorRGBA m_drag_color;
ColorRGBA m_highlight_color;
public:
GLGizmoRotate(GLCanvas3D& parent, Axis axis);
virtual ~GLGizmoRotate() = default;
@ -65,11 +67,27 @@ public:
std::string get_tooltip() const override;
void start_dragging();
void stop_dragging();
void enable_grabber();
void disable_grabber();
void set_highlight_color(const ColorRGBA &color);
/// <summary>
/// Postpone to Grabber for move
/// Detect move of object by dragging
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
void dragging(const UpdateData &data);
protected:
bool on_init() override;
std::string on_get_name() const override { return ""; }
void on_start_dragging() override;
void on_update(const UpdateData& data) override;
void on_dragging(const UpdateData &data) override;
void on_render() override;
void on_render_for_picking() override;
@ -119,6 +137,14 @@ public:
return tooltip;
}
/// <summary>
/// Postpone to Rotation
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
void data_changed() override;
protected:
bool on_init() override;
std::string on_get_name() const override;
@ -132,20 +158,17 @@ protected:
}
void on_enable_grabber(unsigned int id) override {
if (id < 3)
m_gizmos[id].enable_grabber(0);
m_gizmos[id].enable_grabber();
}
void on_disable_grabber(unsigned int id) override {
if (id < 3)
m_gizmos[id].disable_grabber(0);
m_gizmos[id].disable_grabber();
}
bool on_is_activable() const override;
void on_start_dragging() override;
void on_stop_dragging() override;
void on_update(const UpdateData& data) override {
for (GLGizmoRotate& g : m_gizmos) {
g.update(data);
}
}
void on_dragging(const UpdateData &data) override;
void on_render() override;
void on_render_for_picking() override {
for (GLGizmoRotate& g : m_gizmos) {

View file

@ -8,7 +8,7 @@
#include <GL/glew.h>
#include <wx/utils.h>
#include <wx/utils.h>
namespace Slic3r {
namespace GUI {
@ -18,6 +18,12 @@ const double GLGizmoScale3D::Offset = 5.0;
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
, m_scale(Vec3d::Ones())
, m_offset(Vec3d::Zero())
, m_snap_step(0.05)
, m_base_color(DEFAULT_BASE_COLOR)
, m_drag_color(DEFAULT_DRAG_COLOR)
, m_highlight_color(DEFAULT_HIGHLIGHT_COLOR)
{
#if ENABLE_GLBEGIN_GLEND_REMOVAL
m_grabber_connections[0].grabber_indices = { 0, 1 };
@ -62,6 +68,84 @@ std::string GLGizmoScale3D::get_tooltip() const
return "";
}
bool GLGizmoScale3D::on_mouse(const wxMouseEvent &mouse_event)
{
if (mouse_event.Dragging()) {
if (m_dragging) {
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
int res = 1;
if (m_scale.x() != m_scale.y() || m_scale.x() != m_scale.z())
res = m_parent.get_selection().bake_transform_if_needed();
if (res != 1) {
do_stop_dragging(true);
return true;
}
else {
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
// Apply new temporary scale factors
#if ENABLE_WORLD_COORDINATE
TransformationType transformation_type;
if (!wxGetApp().obj_manipul()->is_world_coordinates())
transformation_type.set_local();
#else
TransformationType transformation_type(TransformationType::Local_Absolute_Joint);
#endif // ENABLE_WORLD_COORDINATE
if (mouse_event.AltDown()) transformation_type.set_independent();
Selection &selection = m_parent.get_selection();
selection.scale(m_scale, transformation_type);
#if ENABLE_WORLD_COORDINATE
if (mouse_event.CmdDown()) selection.translate(m_offset, wxGetApp().obj_manipul()->get_coordinates_type());
#else
if (mouse_event.CmdDown()) selection.translate(m_offset, true);
#endif // ENABLE_WORLD_COORDINATE
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
}
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
}
}
return use_grabbers(mouse_event);
}
void GLGizmoScale3D::data_changed()
{
const Selection &selection = m_parent.get_selection();
#if ENABLE_WORLD_COORDINATE
#if !ENABLE_WORLD_COORDINATE_SCALE_REVISITED
bool enable_scale_xyz = !selection.requires_uniform_scale();
#endif // !ENABLE_WORLD_COORDINATE_SCALE_REVISITED
#else
bool enable_scale_xyz = selection.is_single_full_instance() ||
selection.is_single_volume() ||
selection.is_single_modifier();
#endif // ENABLE_WORLD_COORDINATE
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) {
#else
for (unsigned int i = 0; i < 6; ++i)
m_grabbers[i].enabled = enable_scale_xyz;
if (enable_scale_xyz) {
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
// all volumes in the selection belongs to the same instance, any of
// them contains the needed data, so we take the first
const GLVolume *volume = selection.get_volume(*selection.get_volume_idxs().begin());
if (selection.is_single_full_instance())
set_scale(volume->get_instance_scaling_factor());
#if ENABLE_WORLD_COORDINATE
else if (selection.is_single_volume_or_modifier())
#else
else if (selection.is_single_volume() ||
selection.is_single_modifier())
#endif // ENABLE_WORLD_COORDINATE
set_scale(volume->get_volume_scaling_factor());
}
else
set_scale(Vec3d::Ones());
}
bool GLGizmoScale3D::on_init()
{
for (int i = 0; i < 10; ++i) {
@ -98,29 +182,32 @@ bool GLGizmoScale3D::on_is_activable() const
void GLGizmoScale3D::on_start_dragging()
{
if (m_hover_id != -1) {
m_starting.ctrl_down = wxGetKeyState(WXK_CONTROL);
assert(m_hover_id != -1);
m_starting.ctrl_down = wxGetKeyState(WXK_CONTROL);
#if ENABLE_WORLD_COORDINATE
m_starting.drag_position = m_grabbers_transform * m_grabbers[m_hover_id].center;
m_starting.box = m_bounding_box;
m_starting.center = m_center;
m_starting.instance_center = m_instance_center;
m_starting.drag_position = m_grabbers_transform * m_grabbers[m_hover_id].center;
m_starting.box = m_bounding_box;
m_starting.center = m_center;
m_starting.instance_center = m_instance_center;
#else
m_starting.drag_position = m_grabbers[m_hover_id].center;
m_starting.box = (m_starting.ctrl_down && m_hover_id < 6) ? m_bounding_box : m_parent.get_selection().get_bounding_box();
m_starting.drag_position = m_grabbers[m_hover_id].center;
m_starting.box = (m_starting.ctrl_down && m_hover_id < 6) ? m_box : m_parent.get_selection().get_bounding_box();
const Vec3d& center = m_starting.box.center();
m_starting.pivots[0] = m_transform * Vec3d(m_starting.box.max.x(), center.y(), center.z());
m_starting.pivots[1] = m_transform * Vec3d(m_starting.box.min.x(), center.y(), center.z());
m_starting.pivots[2] = m_transform * Vec3d(center.x(), m_starting.box.max.y(), center.z());
m_starting.pivots[3] = m_transform * Vec3d(center.x(), m_starting.box.min.y(), center.z());
m_starting.pivots[4] = m_transform * Vec3d(center.x(), center.y(), m_starting.box.max.z());
m_starting.pivots[5] = m_transform * Vec3d(center.x(), center.y(), m_starting.box.min.z());
const Vec3d& center = m_starting.box.center();
m_starting.pivots[0] = m_transform * Vec3d(m_starting.box.max.x(), center.y(), center.z());
m_starting.pivots[1] = m_transform * Vec3d(m_starting.box.min.x(), center.y(), center.z());
m_starting.pivots[2] = m_transform * Vec3d(center.x(), m_starting.box.max.y(), center.z());
m_starting.pivots[3] = m_transform * Vec3d(center.x(), m_starting.box.min.y(), center.z());
m_starting.pivots[4] = m_transform * Vec3d(center.x(), center.y(), m_starting.box.max.z());
m_starting.pivots[5] = m_transform * Vec3d(center.x(), center.y(), m_starting.box.min.z());
#endif // ENABLE_WORLD_COORDINATE
}
}
void GLGizmoScale3D::on_update(const UpdateData& data)
void GLGizmoScale3D::on_stop_dragging() {
m_parent.do_scale(L("Gizmo-Scale"));
}
void GLGizmoScale3D::on_dragging(const UpdateData& data)
{
if (m_hover_id == 0 || m_hover_id == 1)
do_scale_along_axis(X, data);
@ -144,12 +231,7 @@ void GLGizmoScale3D::on_render()
m_grabbers_transform = Transform3d::Identity();
m_center = Vec3d::Zero();
m_instance_center = Vec3d::Zero();
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (selection.is_single_full_instance() && !wxGetApp().obj_manipul()->is_world_coordinates()) {
#else
bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates();
if (selection.is_single_full_instance() && !world_coordinates) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#else
m_transform = Transform3d::Identity();
// Transforms grabbers' offsets to world refefence system
@ -180,6 +262,8 @@ void GLGizmoScale3D::on_render()
m_grabbers_transform = v.get_instance_transformation().get_matrix(false, false, true) * Geometry::assemble_transform(m_bounding_box.center());
m_center = selection.get_volume(*idxs.begin())->get_instance_transformation().get_matrix(false, false, true, false) * m_bounding_box.center();
m_instance_center = v.get_instance_offset();
}
else if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_instance_coordinates()) {
#else
m_transform = v.get_instance_transformation().get_matrix();
@ -188,32 +272,18 @@ void GLGizmoScale3D::on_render()
// consider rotation+mirror only components of the transform for offsets
offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v.get_instance_mirror());
m_offsets_transform = offsets_transform;
#endif // ENABLE_WORLD_COORDINATE
}
#if ENABLE_WORLD_COORDINATE
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
else if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_instance_coordinates()) {
#else
else if (selection.is_single_volume_or_modifier() && !world_coordinates) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#else
else if (selection.is_single_modifier() || selection.is_single_volume()) {
#endif // ENABLE_WORLD_COORDINATE
const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin());
#if ENABLE_WORLD_COORDINATE
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
m_bounding_box.merge(v.transformed_convex_hull_bounding_box(v.get_instance_transformation().get_matrix(true, true, false, true) * v.get_volume_transformation().get_matrix(true, false, false, true)));
Geometry::Transformation trafo(v.get_instance_transformation().get_matrix(true, false, true, true));
#else
m_bounding_box.merge(v.transformed_convex_hull_bounding_box(v.get_instance_transformation().get_matrix(true, true, false, true) * v.get_volume_transformation().get_matrix(true, true, false, true)));
Geometry::Transformation trafo(v.get_instance_transformation().get_matrix(true, false, true) * v.get_volume_transformation().get_matrix(true, false, true));
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
trafo.set_offset(v.world_matrix().translation());
m_grabbers_transform = trafo.get_matrix();
m_center = v.world_matrix() * m_bounding_box.center();
m_instance_center = m_center;
}
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
else if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates()) {
const GLVolume& v = *selection.get_volume(*selection.get_volume_idxs().begin());
m_bounding_box.merge(v.transformed_convex_hull_bounding_box(v.get_instance_transformation().get_matrix(true, true, false, true) * v.get_volume_transformation().get_matrix(true, true, false, true)));
@ -223,7 +293,6 @@ void GLGizmoScale3D::on_render()
m_center = v.world_matrix() * m_bounding_box.center();
m_instance_center = m_center;
}
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
else {
m_bounding_box = selection.get_bounding_box();
m_grabbers_transform = Geometry::assemble_transform(m_bounding_box.center());
@ -568,13 +637,8 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data)
Vec3d curr_scale = m_scale;
Vec3d starting_scale = m_starting.scale;
const Selection& selection = m_parent.get_selection();
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type();
if (coordinates_type == ECoordinatesType::World) {
#else
const bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates();
if (world_coordinates) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (selection.is_single_full_instance()) {
const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation());
curr_scale = (m * curr_scale).cwiseAbs();
@ -591,11 +655,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data)
curr_scale(axis) = starting_scale(axis) * ratio;
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (coordinates_type == ECoordinatesType::World) {
#else
if (world_coordinates) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (selection.is_single_full_instance())
m_scale = (Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse() * curr_scale).cwiseAbs();
else if (selection.is_single_volume_or_modifier()) {
@ -625,11 +685,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data)
#if ENABLE_WORLD_COORDINATE
Vec3d center_offset = m_starting.instance_center - m_starting.center;
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (selection.is_single_full_instance() && coordinates_type != ECoordinatesType::World) {
#else
if (selection.is_single_full_instance() && !world_coordinates) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse();
center_offset = m * center_offset;
}
@ -643,14 +699,6 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data)
case Z: { m_offset = local_offset * Vec3d::UnitZ(); break; }
default: { m_offset = Vec3d::Zero(); break; }
}
#if !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (selection.is_single_volume_or_modifier() && !world_coordinates) {
const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation());
const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor()).inverse();
m_offset = mv * mi * m_offset;
}
#endif // !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#else
Vec3d local_offset_vec;
switch (axis)
@ -684,29 +732,14 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData& data)
m_offset.y() *= -1.0;
const Selection& selection = m_parent.get_selection();
#if !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
const bool world_coordinates = wxGetApp().obj_manipul()->get_world_coordinates();
#endif // !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
Vec3d center_offset = m_starting.instance_center - m_starting.center;
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (selection.is_single_full_instance() && !wxGetApp().obj_manipul()->is_world_coordinates()) {
#else
if (selection.is_single_full_instance() && !world_coordinates) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_rotation()).inverse();
center_offset = m * center_offset;
}
m_offset += (ratio - 1.0) * center_offset;
#if !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (selection.is_single_volume_or_modifier() && !world_coordinates) {
const Transform3d mv = Geometry::assemble_transform(Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_rotation());
const Transform3d mi = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor()).inverse();
m_offset = mv * mi * m_offset;
}
#endif // !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
}
else
#endif // ENABLE_WORLD_COORDINATE
@ -754,17 +787,9 @@ void GLGizmoScale3D::transform_to_local(const Selection& selection) const
{
glsafe(::glTranslated(m_center.x(), m_center.y(), m_center.z()));
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (!wxGetApp().obj_manipul()->is_world_coordinates()) {
#else
if (!wxGetApp().obj_manipul()->get_world_coordinates()) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
Transform3d orient_matrix = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(true, false, true, true);
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates())
#else
if (selection.is_single_volume_or_modifier())
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
orient_matrix = orient_matrix * selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_transformation().get_matrix(true, false, true, true);
glsafe(::glMultMatrixd(orient_matrix.data()));
}

View file

@ -3,11 +3,17 @@
#include "GLGizmoBase.hpp"
#if !ENABLE_WORLD_COORDINATE
#include "libslic3r/BoundingBox.hpp"
#endif // !ENABLE_WORLD_COORDINATE
namespace Slic3r {
namespace GUI {
#if ENABLE_WORLD_COORDINATE
class Selection;
#endif // ENABLE_WORLD_COORDINATE
class GLGizmoScale3D : public GLGizmoBase
{
static const double Offset;
@ -53,6 +59,9 @@ class GLGizmoScale3D : public GLGizmoBase
std::array<GrabberConnection, 7> m_grabber_connections;
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
ColorRGBA m_base_color;
ColorRGBA m_drag_color;
ColorRGBA m_highlight_color;
public:
GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
@ -62,16 +71,23 @@ public:
const Vec3d& get_scale() const { return m_scale; }
void set_scale(const Vec3d& scale) { m_starting.scale = scale; m_scale = scale; }
const Vec3d& get_offset() const { return m_offset; }
std::string get_tooltip() const override;
/// <summary>
/// Postpone to Grabber for scale
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
void data_changed() override;
protected:
virtual bool on_init() override;
virtual std::string on_get_name() const override;
virtual bool on_is_activable() const override;
virtual void on_start_dragging() override;
virtual void on_update(const UpdateData& data) override;
virtual void on_stop_dragging() override;
virtual void on_dragging(const UpdateData& data) override;
virtual void on_render() override;
virtual void on_render_for_picking() override;

View file

@ -28,9 +28,7 @@ namespace GUI {
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
{
}
{}
bool GLGizmoSlaSupports::on_init()
{
@ -48,11 +46,15 @@ bool GLGizmoSlaSupports::on_init()
m_desc["manual_editing"] = _L("Manual editing");
m_desc["clipping_of_view"] = _L("Clipping of view")+ ": ";
m_desc["reset_direction"] = _L("Reset direction");
m_cone.init_from(its_make_cone(1., 1., 2 * PI / 24));
m_cylinder.init_from(its_make_cylinder(1., 1., 2 * PI / 24.));
m_sphere.init_from(its_make_sphere(1., (2 * M_PI) / 24.));
return true;
}
void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const Selection& selection)
void GLGizmoSlaSupports::data_changed()
{
if (! m_c->selection_info())
return;
@ -553,22 +555,6 @@ void GLGizmoSlaSupports::delete_selected_points(bool force)
select_point(NoPoints);
}
void GLGizmoSlaSupports::on_update(const UpdateData& data)
{
if (! m_editing_mode)
return;
else {
if (m_hover_id != -1 && (! m_editing_cache[m_hover_id].support_point.is_new_island || !m_lock_unique_islands)) {
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].support_point.pos = pos_and_normal.first;
m_editing_cache[m_hover_id].support_point.is_new_island = false;
m_editing_cache[m_hover_id].normal = pos_and_normal.second;
}
}
}
std::vector<const ConfigOption*> GLGizmoSlaSupports::get_config_options(const std::vector<std::string>& keys) const
{
std::vector<const ConfigOption*> out;
@ -985,7 +971,21 @@ void GLGizmoSlaSupports::on_stop_dragging()
m_point_before_drag = CacheEntry();
}
void GLGizmoSlaSupports::on_dragging(const UpdateData &data)
{
assert(m_hover_id != -1);
if (!m_editing_mode) return;
if (m_editing_cache[m_hover_id].support_point.is_new_island && m_lock_unique_islands)
return;
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].support_point.pos = pos_and_normal.first;
m_editing_cache[m_hover_id].support_point.is_new_island = false;
m_editing_cache[m_hover_id].normal = pos_and_normal.second;
}
void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar)
{
@ -1122,6 +1122,61 @@ void GLGizmoSlaSupports::reslice_SLA_supports(bool postpone_error_messages) cons
});
}
bool GLGizmoSlaSupports::on_mouse(const wxMouseEvent &mouse_event){
if (mouse_event.Moving()) return false;
if (use_grabbers(mouse_event)) return true;
// wxCoord == int --> wx/types.h
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
Vec2d mouse_pos = mouse_coord.cast<double>();
static bool pending_right_up = false;
if (mouse_event.LeftDown()) {
bool grabber_contains_mouse = (get_hover_id() != -1);
bool control_down = mouse_event.CmdDown();
if ((!control_down || grabber_contains_mouse) &&
gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false))
return true;
} else if (mouse_event.Dragging()) {
bool control_down = mouse_event.CmdDown();
if (m_parent.get_move_volume_id() != -1) {
// don't allow dragging objects with the Sla gizmo on
return true;
} else if (!control_down &&
gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false)) {
// the gizmo got the event and took some action, no need to do
// anything more here
m_parent.set_as_dirty();
return true;
} else if (control_down && (mouse_event.LeftIsDown() || mouse_event.RightIsDown())){
// CTRL has been pressed while already dragging -> stop current action
if (mouse_event.LeftIsDown())
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), true);
else if (mouse_event.RightIsDown())
pending_right_up = false;
}
} else if (mouse_event.LeftUp() && !m_parent.is_mouse_dragging()) {
// in case SLA/FDM gizmo is selected, we just pass the LeftUp event
// and stop processing - neither object moving or selecting is
// suppressed in that case
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown());
return true;
}else if (mouse_event.RightDown()){
if (m_parent.get_selection().get_object_idx() != -1 &&
gizmo_event(SLAGizmoEventType::RightDown, mouse_pos, false, false, false)) {
// 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;
// event was taken care of by the SlaSupports gizmo
return true;
}
} else if (pending_right_up && mouse_event.RightUp()) {
pending_right_up = false;
return true;
}
return false;
}
void GLGizmoSlaSupports::get_data_from_backend()
{
if (! has_backend_supports())

View file

@ -16,7 +16,7 @@ namespace Slic3r {
class ConfigOption;
namespace GUI {
class Selection;
enum class SLAGizmoEventType : unsigned char;
class GLGizmoSlaSupports : public GLGizmoBase
@ -57,7 +57,7 @@ private:
public:
GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
virtual ~GLGizmoSlaSupports() = default;
void set_sla_support_data(ModelObject* model_object, const Selection& selection);
void data_changed() override;
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
void delete_selected_points(bool force = false);
//ClippingPlane get_sla_clipping_plane() const;
@ -70,10 +70,15 @@ public:
bool wants_enter_leave_snapshots() const override { return true; }
std::string get_gizmo_entering_text() const override { return _u8L("Entering SLA support points"); }
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving SLA support points"); }
/// <summary>
/// Process mouse event
/// </summary>
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
private:
bool on_init() override;
void on_update(const UpdateData& data) override;
void on_render() override;
void on_render_for_picking() override;
@ -107,7 +112,6 @@ private:
std::vector<const ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
bool is_mesh_point_clipped(const Vec3d& point) const;
bool is_point_in_hole(const Vec3f& pt) const;
//void find_intersecting_facets(const igl::AABB<Eigen::MatrixXf, 3>* aabb, const Vec3f& normal, double offset, std::vector<unsigned int>& out) const;
// Methods that do the model_object and editing cache synchronization,
@ -130,13 +134,13 @@ private:
protected:
void on_set_state() override;
void on_set_hover_id() override
{
if (! m_editing_mode || (int)m_editing_cache.size() <= m_hover_id)
m_hover_id = -1;
}
void on_start_dragging() override;
void on_stop_dragging() override;
void on_dragging(const UpdateData &data) override;
void on_render_input_window(float x, float y, float bottom_limit) override;
std::string on_get_name() const override;

View file

@ -39,6 +39,7 @@ GLGizmosManager::GLGizmosManager(GLCanvas3D& parent)
, m_enabled(false)
, m_icons_texture_dirty(true)
, m_current(Undefined)
, m_hover(Undefined)
, m_tooltip("")
, m_serializing(false)
{
@ -47,33 +48,34 @@ GLGizmosManager::GLGizmosManager(GLCanvas3D& parent)
std::vector<size_t> GLGizmosManager::get_selectable_idxs() const
{
std::vector<size_t> out;
out.reserve(m_gizmos.size());
for (size_t i=0; i<m_gizmos.size(); ++i)
if (m_gizmos[i]->is_selectable())
out.push_back(i);
return out;
}
size_t GLGizmosManager::get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const
GLGizmosManager::EType GLGizmosManager::get_gizmo_from_mouse(const Vec2d &mouse_pos) const
{
if (! m_enabled)
return Undefined;
if (!m_enabled) return Undefined;
float cnv_h = (float)m_parent.get_canvas_size().get_height();
float height = get_scaled_total_height();
float cnv_h = (float) m_parent.get_canvas_size().get_height();
float height = get_scaled_total_height();
float icons_size = m_layout.scaled_icons_size();
float border = m_layout.scaled_border();
float stride_y = m_layout.scaled_stride_y();
float top_y = 0.5f * (cnv_h - height) + border;
float border = m_layout.scaled_border();
float stride_y = m_layout.scaled_stride_y();
float top_y = 0.5f * (cnv_h - height) + border;
// is mouse horizontally in the area?
if ((border <= (float)mouse_pos(0) && ((float)mouse_pos(0) <= border + icons_size))) {
if ((border <= (float) mouse_pos(0) &&
((float) mouse_pos(0) <= border + icons_size))) {
// which icon is it on?
size_t from_top = (size_t)((float)mouse_pos(1) - top_y) / stride_y;
size_t from_top = (size_t) ((float) mouse_pos(1) - top_y) / stride_y;
// is it really on the icon or already past the border?
if ((float)mouse_pos(1) <= top_y + from_top * stride_y + icons_size) {
if ((float) mouse_pos(1) <= top_y + from_top * stride_y + icons_size) {
std::vector<size_t> selectable = get_selectable_idxs();
if (from_top < selectable.size())
return selectable[from_top];
if (from_top < selectable.size())
return static_cast<EType>(selectable[from_top]);
}
}
return Undefined;
@ -163,9 +165,9 @@ void GLGizmosManager::refresh_on_off_state()
if (m_serializing || m_current == Undefined || m_gizmos.empty())
return;
if (m_current != Undefined
&& ! m_gizmos[m_current]->is_activable() && activate_gizmo(Undefined))
update_data();
// FS: Why update data after Undefined gizmo activation?
if (!m_gizmos[m_current]->is_activable() && activate_gizmo(Undefined))
update_data();
}
void GLGizmosManager::reset_all_states()
@ -179,9 +181,13 @@ void GLGizmosManager::reset_all_states()
bool GLGizmosManager::open_gizmo(EType type)
{
int idx = int(type);
if (m_gizmos[idx]->is_activable()
&& activate_gizmo(m_current == idx ? Undefined : (EType)idx)) {
int idx = static_cast<int>(type);
// re-open same type cause closing
if (m_current == type) type = Undefined;
if (m_gizmos[idx]->is_activable() && activate_gizmo(type)) {
// remove update data into gizmo itself
update_data();
return true;
}
@ -210,90 +216,14 @@ void GLGizmosManager::set_hover_id(int id)
m_gizmos[m_current]->set_hover_id(id);
}
void GLGizmosManager::enable_grabber(EType type, unsigned int id, bool enable)
{
if (!m_enabled || type == Undefined || m_gizmos.empty())
return;
if (enable)
m_gizmos[type]->enable_grabber(id);
else
m_gizmos[type]->disable_grabber(id);
}
void GLGizmosManager::update(const Linef3& mouse_ray, const Point& mouse_pos)
{
if (!m_enabled)
return;
GLGizmoBase* curr = get_current();
if (curr != nullptr)
curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos));
}
void GLGizmosManager::update_data()
{
if (!m_enabled)
return;
const Selection& selection = m_parent.get_selection();
bool is_wipe_tower = selection.is_wipe_tower();
enable_grabber(Move, 2, !is_wipe_tower);
enable_grabber(Rotate, 0, !is_wipe_tower);
enable_grabber(Rotate, 1, !is_wipe_tower);
#if ENABLE_WORLD_COORDINATE
bool enable_scale_xyz = !selection.requires_uniform_scale();
#else
bool enable_scale_xyz = selection.is_single_full_instance() || selection.is_single_volume() || selection.is_single_modifier();
#endif // ENABLE_WORLD_COORDINATE
for (unsigned int i = 0; i < 6; ++i) {
enable_grabber(Scale, i, enable_scale_xyz);
}
if (!m_enabled) return;
if (m_common_gizmos_data)
m_common_gizmos_data->update(get_current()
? get_current()->get_requirements()
: CommonGizmosDataID(0));
if (selection.is_single_full_instance()) {
// all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
set_scale(volume->get_instance_scaling_factor());
set_rotation(Vec3d::Zero());
ModelObject* model_object = selection.get_model()->objects[selection.get_object_idx()];
set_flattening_data(model_object);
set_sla_support_data(model_object);
set_painter_gizmo_data();
}
#if ENABLE_WORLD_COORDINATE
else if (selection.is_single_volume_or_modifier()) {
#else
else if (selection.is_single_volume() || selection.is_single_modifier()) {
#endif // ENABLE_WORLD_COORDINATE
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
set_scale(volume->get_volume_scaling_factor());
set_rotation(Vec3d::Zero());
set_flattening_data(nullptr);
set_sla_support_data(nullptr);
set_painter_gizmo_data();
}
else if (is_wipe_tower) {
DynamicPrintConfig& config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
set_scale(Vec3d::Ones());
set_rotation(Vec3d(0., 0., (M_PI/180.) * dynamic_cast<const ConfigOptionFloat*>(config.option("wipe_tower_rotation_angle"))->value));
set_flattening_data(nullptr);
set_sla_support_data(nullptr);
set_painter_gizmo_data();
}
else {
set_scale(Vec3d::Ones());
set_rotation(Vec3d::Zero());
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_painter_gizmo_data();
}
if (m_current != Undefined) m_gizmos[m_current]->data_changed();
}
bool GLGizmosManager::is_running() const
@ -333,107 +263,6 @@ bool GLGizmosManager::is_dragging() const
return m_gizmos[m_current]->is_dragging();
}
void GLGizmosManager::start_dragging()
{
if (! m_enabled || m_current == Undefined)
return;
m_gizmos[m_current]->start_dragging();
}
void GLGizmosManager::stop_dragging()
{
if (! m_enabled || m_current == Undefined)
return;
m_gizmos[m_current]->stop_dragging();
}
Vec3d GLGizmosManager::get_displacement() const
{
if (!m_enabled)
return Vec3d::Zero();
return dynamic_cast<GLGizmoMove3D*>(m_gizmos[Move].get())->get_displacement();
}
Vec3d GLGizmosManager::get_scale() const
{
if (!m_enabled)
return Vec3d::Ones();
return dynamic_cast<GLGizmoScale3D*>(m_gizmos[Scale].get())->get_scale();
}
void GLGizmosManager::set_scale(const Vec3d& scale)
{
if (!m_enabled || m_gizmos.empty())
return;
dynamic_cast<GLGizmoScale3D*>(m_gizmos[Scale].get())->set_scale(scale);
}
Vec3d GLGizmosManager::get_scale_offset() const
{
if (!m_enabled || m_gizmos.empty())
return Vec3d::Zero();
return dynamic_cast<GLGizmoScale3D*>(m_gizmos[Scale].get())->get_offset();
}
Vec3d GLGizmosManager::get_rotation() const
{
if (!m_enabled || m_gizmos.empty())
return Vec3d::Zero();
return dynamic_cast<GLGizmoRotate3D*>(m_gizmos[Rotate].get())->get_rotation();
}
void GLGizmosManager::set_rotation(const Vec3d& rotation)
{
if (!m_enabled || m_gizmos.empty())
return;
dynamic_cast<GLGizmoRotate3D*>(m_gizmos[Rotate].get())->set_rotation(rotation);
}
Vec3d GLGizmosManager::get_flattening_normal() const
{
if (!m_enabled || m_gizmos.empty())
return Vec3d::Zero();
return dynamic_cast<GLGizmoFlatten*>(m_gizmos[Flatten].get())->get_flattening_normal();
}
void GLGizmosManager::set_flattening_data(const ModelObject* model_object)
{
if (!m_enabled || m_gizmos.empty())
return;
dynamic_cast<GLGizmoFlatten*>(m_gizmos[Flatten].get())->set_flattening_data(model_object);
}
void GLGizmosManager::set_sla_support_data(ModelObject* model_object)
{
if (! m_enabled
|| m_gizmos.empty()
|| wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA)
return;
auto* gizmo_hollow = dynamic_cast<GLGizmoHollow*>(m_gizmos[Hollow].get());
auto* gizmo_supports = dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get());
gizmo_hollow->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_painter_gizmo_data()
{
if (!m_enabled || m_gizmos.empty())
return;
dynamic_cast<GLGizmoFdmSupports*>(m_gizmos[FdmSupports].get())->set_painter_gizmo_data(m_parent.get_selection());
dynamic_cast<GLGizmoSeam*>(m_gizmos[Seam].get())->set_painter_gizmo_data(m_parent.get_selection());
dynamic_cast<GLGizmoMmuSegmentation*>(m_gizmos[MmuSegmentation].get())->set_painter_gizmo_data(m_parent.get_selection());
}
// Returns true if the gizmo used the event to do something, false otherwise.
bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down)
{
@ -522,7 +351,7 @@ std::string GLGizmosManager::get_tooltip() const
return (curr != nullptr) ? curr->get_tooltip() : "";
}
bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt)
bool GLGizmosManager::on_mouse_wheel(const wxMouseEvent &evt)
{
bool processed = false;
@ -535,271 +364,114 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt)
return processed;
}
bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
{
if (m_current != Undefined && m_gizmos[m_current]->on_mouse(evt))
return true;
// used to set a right up event as processed when needed
static bool pending_right_up = false;
Point pos(evt.GetX(), evt.GetY());
Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY());
Selection& selection = m_parent.get_selection();
int selected_object_idx = selection.get_object_idx();
bool processed = false;
// when control is down we allow scene pan and rotation even when clicking over some object
bool control_down = evt.CmdDown();
// mouse anywhere
if (evt.Moving()) {
m_tooltip = update_hover_state(mouse_pos);
if (m_current == MmuSegmentation || m_current == FdmSupports)
gizmo_event(SLAGizmoEventType::Moving, mouse_pos, evt.ShiftDown(), evt.AltDown());
} else if (evt.LeftUp()) {
if (m_mouse_capture.left) {
processed = true;
m_mouse_capture.left = false;
bool GLGizmosManager::gizmos_toolbar_on_mouse(const wxMouseEvent &mouse_event) {
assert(m_enabled);
// keep information about events to process
struct MouseCapture
{
bool left = false;
bool middle = false;
bool right = false;
bool exist_tooltip = false;
MouseCapture() = default;
bool any() const { return left || middle || right; }
void reset() {
left = false;
middle = false;
right = false;
}
else if (is_dragging()) {
switch (m_current) {
case Move: { m_parent.do_move(L("Gizmo-Move")); break; }
case Scale: { m_parent.do_scale(L("Gizmo-Scale")); break; }
case Rotate: { m_parent.do_rotate(L("Gizmo-Rotate")); break; }
default: break;
}
};
static MouseCapture mc;
stop_dragging();
update_data();
// wxCoord == int --> wx/types.h
Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY());
Vec2d mouse_pos = mouse_coord.cast<double>();
wxGetApp().obj_manipul()->set_dirty();
// Let the plater know that the dragging finished, so a delayed refresh
// of the scene with the background processing data should be performed.
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
// updates camera target constraints
m_parent.refresh_camera_scene_box();
EType gizmo = get_gizmo_from_mouse(mouse_pos);
bool selected_gizmo = gizmo != Undefined;
processed = true;
}
// else
// return false;
}
else if (evt.MiddleUp()) {
if (m_mouse_capture.middle) {
processed = true;
m_mouse_capture.middle = false;
}
else
// fast reaction on move mouse
if (mouse_event.Moving()) {
assert(!mc.any());
if (selected_gizmo) {
mc.exist_tooltip = true;
update_hover_state(gizmo);
// at this moment is enebled to process mouse move under gizmo
// tools bar e.g. Do not interupt dragging.
return false;
}
else if (mc.exist_tooltip) {
// first move out of gizmo tool bar - unselect tooltip
mc.exist_tooltip = false;
update_hover_state(Undefined);
return false;
}
return false;
}
else if (evt.RightUp()) {
if (pending_right_up) {
pending_right_up = false;
if (selected_gizmo) {
// mouse is above toolbar
if (mouse_event.LeftDown() || mouse_event.LeftDClick()) {
mc.left = true;
open_gizmo(gizmo);
return true;
}
if (m_mouse_capture.right) {
processed = true;
m_mouse_capture.right = false;
else if (mouse_event.RightDown()) {
mc.right = true;
return true;
}
else if (mouse_event.MiddleDown()) {
mc.middle = true;
return true;
}
// else
// return false;
}
else if (evt.Dragging() && !is_dragging()) {
if (m_mouse_capture.any())
// if the button down was done on this toolbar, prevent from dragging into the scene
processed = true;
// else
// return false;
}
else if (evt.Dragging() && is_dragging()) {
if (!m_parent.get_wxglcanvas()->HasCapture())
m_parent.get_wxglcanvas()->CaptureMouse();
m_parent.set_mouse_as_dragging();
update(m_parent.mouse_ray(pos), pos);
switch (m_current)
{
case Move:
{
// Apply new temporary offset
#if ENABLE_WORLD_COORDINATE
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
selection.translate(get_displacement(), wxGetApp().obj_manipul()->get_coordinates_type());
#else
selection.translate(get_displacement(), !wxGetApp().obj_manipul()->get_world_coordinates());
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#else
selection.translate(get_displacement());
#endif // ENABLE_WORLD_COORDINATE
wxGetApp().obj_manipul()->set_dirty();
break;
}
case Scale:
{
// Apply new temporary scale factors
#if ENABLE_WORLD_COORDINATE
TransformationType transformation_type;
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (!wxGetApp().obj_manipul()->is_world_coordinates())
#else
if (!wxGetApp().obj_manipul()->get_world_coordinates())
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
transformation_type.set_local();
#else
TransformationType transformation_type(TransformationType::Local_Absolute_Joint);
#endif // ENABLE_WORLD_COORDINATE
if (evt.AltDown())
transformation_type.set_independent();
selection.scale(get_scale(), transformation_type);
if (control_down)
#if ENABLE_WORLD_COORDINATE
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
selection.translate(get_scale_offset(), wxGetApp().obj_manipul()->get_coordinates_type());
#else
selection.translate(get_scale_offset(), !wxGetApp().obj_manipul()->get_world_coordinates());
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#else
selection.translate(get_scale_offset(), true);
#endif // ENABLE_WORLD_COORDINATE
wxGetApp().obj_manipul()->set_dirty();
break;
}
case Rotate:
{
// Apply new temporary rotations
#if ENABLE_WORLD_COORDINATE
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
TransformationType transformation_type;
switch (wxGetApp().obj_manipul()->get_coordinates_type())
{
default:
case ECoordinatesType::World: { transformation_type = TransformationType::World_Relative_Joint; break; }
case ECoordinatesType::Instance: { transformation_type = TransformationType::Instance_Relative_Joint; break; }
case ECoordinatesType::Local: { transformation_type = TransformationType::Local_Relative_Joint; break; }
if (mc.any()) {
// Check if exist release of event started above toolbar?
if (mouse_event.Dragging()) {
if (!selected_gizmo && mc.exist_tooltip) {
// dragging out of gizmo let tooltip disapear
mc.exist_tooltip = false;
update_hover_state(Undefined);
}
#else
TransformationType transformation_type(wxGetApp().obj_manipul()->get_world_coordinates() ? TransformationType::World_Relative_Joint : TransformationType::Local_Relative_Joint);
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#else
TransformationType transformation_type(TransformationType::World_Relative_Joint);
#endif // ENABLE_WORLD_COORDINATE
if (evt.AltDown())
transformation_type.set_independent();
selection.rotate(get_rotation(), transformation_type);
wxGetApp().obj_manipul()->set_dirty();
break;
// draging start on toolbar so no propagation into scene
return true;
}
default:
break;
else if (mc.left && mouse_event.LeftUp()) {
mc.left = false;
return true;
}
m_parent.set_as_dirty();
processed = true;
else if (mc.right && mouse_event.RightUp()) {
mc.right = false;
return true;
}
else if (mc.middle && mouse_event.MiddleUp()) {
mc.middle = false;
return true;
}
// event out of window is not porocessed
// left down on gizmo -> keep down -> move out of window -> release left
if (mouse_event.Leaving()) mc.reset();
}
return false;
}
if (get_gizmo_idx_from_mouse(mouse_pos) == Undefined) {
// mouse is outside the toolbar
m_tooltip.clear();
bool exist_mouse_grabber = grabber_contains_mouse();
if (evt.LeftDown() && (!control_down || exist_mouse_grabber)) {
if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation)
&& 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
processed = true;
else if (!selection.is_empty() && exist_mouse_grabber) {
update_data();
selection.start_dragging();
start_dragging();
bool GLGizmosManager::on_mouse(const wxMouseEvent &mouse_event)
{
if (!m_enabled) return false;
// Let the plater know that the dragging started
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED));
// tool bar wants to use event?
if (gizmos_toolbar_on_mouse(mouse_event)) return true;
if (m_current == Flatten) {
// Rotate the object so the normal points downward:
m_parent.do_flatten(get_flattening_normal(), L("Gizmo-Place on Face"));
wxGetApp().obj_manipul()->set_dirty();
}
m_parent.set_as_dirty();
processed = true;
}
}
else if (evt.RightDown() && selected_object_idx != -1 && (m_current == SlaSupports || m_current == Hollow)
&& 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
pending_right_up = true;
// event was taken care of by the SlaSupports gizmo
processed = true;
}
else if (evt.RightDown() && !control_down && selected_object_idx != -1 && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation)
&& gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) {
// event was taken care of by the FdmSupports / Seam / MMUPainting gizmo
processed = true;
}
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))
// don't allow dragging objects with the Sla gizmo on
processed = true;
else if (evt.Dragging() && !control_down && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation)
&& 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
m_parent.set_as_dirty();
processed = true;
}
else if (evt.Dragging() && control_down && (evt.LeftIsDown() || evt.RightIsDown())) {
// CTRL has been pressed while already dragging -> stop current action
if (evt.LeftIsDown())
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), true);
else if (evt.RightIsDown())
gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), true);
}
else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && !m_parent.is_mouse_dragging()) {
// in case SLA/FDM gizmo is selected, we just pass the LeftUp event and stop processing - neither
// object moving or selecting is suppressed in that case
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down);
processed = true;
}
else if (evt.LeftUp() && m_current == Flatten && m_gizmos[m_current]->get_hover_id() != -1) {
// to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active
selection.stop_dragging();
wxGetApp().obj_manipul()->set_dirty();
processed = true;
}
else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && !m_parent.is_mouse_dragging()) {
gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down);
processed = true;
}
else if (evt.LeftUp()) {
selection.stop_dragging();
wxGetApp().obj_manipul()->set_dirty();
}
}
else {
// mouse inside toolbar
if (evt.LeftDown() || evt.LeftDClick()) {
m_mouse_capture.left = true;
m_mouse_capture.parent = &m_parent;
processed = true;
if (!selection.is_empty()) {
update_on_off_state(mouse_pos);
update_data();
m_parent.set_as_dirty();
}
}
else if (evt.MiddleDown()) {
m_mouse_capture.middle = true;
m_mouse_capture.parent = &m_parent;
}
else if (evt.RightDown()) {
m_mouse_capture.right = true;
m_mouse_capture.parent = &m_parent;
}
}
return processed;
// current gizmo wants to use event?
if (m_current != Undefined &&
// check if gizmo override method could be slower than simple call virtual function
// &m_gizmos[m_current]->on_mouse != &GLGizmoBase::on_mouse &&
m_gizmos[m_current]->on_mouse(mouse_event))
return true;
return false;
}
bool GLGizmosManager::on_char(wxKeyEvent& evt)
@ -1230,68 +902,64 @@ bool GLGizmosManager::generate_icons_texture() const
return res;
}
void GLGizmosManager::update_on_off_state(const Vec2d& mouse_pos)
void GLGizmosManager::update_hover_state(const EType &type)
{
if (!m_enabled)
assert(m_enabled);
if (type == Undefined) {
m_hover = Undefined;
m_tooltip.clear();
return;
size_t idx = get_gizmo_idx_from_mouse(mouse_pos);
if (idx != Undefined && m_gizmos[idx]->is_activable() && m_hover == idx) {
activate_gizmo(m_current == idx ? Undefined : (EType)idx);
wxGetApp().obj_list()->select_object_item((EType)idx <= Rotate);
}
}
std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos)
{
std::string name = "";
if (!m_enabled)
return name;
m_hover = Undefined;
size_t idx = get_gizmo_idx_from_mouse(mouse_pos);
if (idx != Undefined) {
name = m_gizmos[idx]->get_name();
if (m_gizmos[idx]->is_activable())
m_hover = (EType)idx;
}
return name;
const GLGizmoBase &hovered_gizmo = *m_gizmos[type];
m_hover = hovered_gizmo.is_activable() ? type : Undefined;
m_tooltip = hovered_gizmo.get_name();
}
bool GLGizmosManager::activate_gizmo(EType type)
{
if (m_gizmos.empty() || m_current == type)
return true;
assert(!m_gizmos.empty());
GLGizmoBase* old_gizmo = m_current == Undefined ? nullptr : m_gizmos[m_current].get();
GLGizmoBase* new_gizmo = type == Undefined ? nullptr : m_gizmos[type].get();
// already activated
if (m_current == type) return true;
if (old_gizmo) {
old_gizmo->set_state(GLGizmoBase::Off);
if (old_gizmo->get_state() != GLGizmoBase::Off)
if (m_current != Undefined) {
// clean up previous gizmo
GLGizmoBase &old_gizmo = *m_gizmos[m_current];
old_gizmo.set_state(GLGizmoBase::Off);
if (old_gizmo.get_state() != GLGizmoBase::Off)
return false; // gizmo refused to be turned off, do nothing.
if (! m_parent.get_gizmos_manager().is_serializing()
&& old_gizmo->wants_enter_leave_snapshots())
Plater::TakeSnapshot snapshot(wxGetApp().plater(),
old_gizmo->get_gizmo_leaving_text(),
UndoRedo::SnapshotType::LeavingGizmoWithAction);
if (!m_serializing && old_gizmo.wants_enter_leave_snapshots())
Plater::TakeSnapshot
snapshot(wxGetApp().plater(),
old_gizmo.get_gizmo_leaving_text(),
UndoRedo::SnapshotType::LeavingGizmoWithAction);
}
if (new_gizmo && ! m_parent.get_gizmos_manager().is_serializing()
&& new_gizmo->wants_enter_leave_snapshots())
if (type == Undefined) {
// it is deactivation of gizmo
m_current = Undefined;
return true;
}
// set up new gizmo
GLGizmoBase& new_gizmo = *m_gizmos[type];
if (!new_gizmo.is_activable()) return false;
if (!m_serializing && new_gizmo.wants_enter_leave_snapshots())
Plater::TakeSnapshot snapshot(wxGetApp().plater(),
new_gizmo->get_gizmo_entering_text(),
UndoRedo::SnapshotType::EnteringGizmo);
new_gizmo.get_gizmo_entering_text(),
UndoRedo::SnapshotType::EnteringGizmo);
m_current = type;
new_gizmo.set_state(GLGizmoBase::On);
if (new_gizmo.get_state() != GLGizmoBase::On) {
m_current = Undefined;
return false; // gizmo refused to be turned on.
}
if (new_gizmo)
new_gizmo->set_state(GLGizmoBase::On);
// sucessful activation of gizmo
return true;
}
@ -1329,11 +997,5 @@ bool GLGizmosManager::is_hiding_instances() const
&& m_common_gizmos_data->instances_hider()->is_valid());
}
int GLGizmosManager::get_shortcut_key(GLGizmosManager::EType type) const
{
return m_gizmos[type]->get_shortcut_key();
}
} // namespace GUI
} // namespace Slic3r

View file

@ -113,28 +113,22 @@ private:
std::pair<EType, bool> m_highlight; // bool true = higlightedShown, false = highlightedHidden
std::vector<size_t> get_selectable_idxs() const;
size_t get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const;
EType get_gizmo_from_mouse(const Vec2d &mouse_pos) const;
bool activate_gizmo(EType type);
struct MouseCapture
{
bool left;
bool middle;
bool right;
GLCanvas3D* parent;
MouseCapture() { reset(); }
bool any() const { return left || middle || right; }
void reset() { left = middle = right = false; parent = nullptr; }
};
MouseCapture m_mouse_capture;
std::string m_tooltip;
bool m_serializing;
std::unique_ptr<CommonGizmosDataPool> m_common_gizmos_data;
/// <summary>
/// Process mouse event on gizmo toolbar
/// </summary>
/// <param name="mouse_event">Event descriptor</param>
/// <returns>TRUE when take responsibility for event otherwise FALSE.
/// On true, event should not be process by others.
/// On false, event should be process by others.</returns>
bool gizmos_toolbar_on_mouse(const wxMouseEvent &mouse_event);
public:
explicit GLGizmosManager(GLCanvas3D& parent);
@ -185,14 +179,15 @@ public:
void refresh_on_off_state();
void reset_all_states();
bool is_serializing() const { return m_serializing; }
bool open_gizmo(EType type);
bool check_gizmos_closed_except(EType) const;
void set_hover_id(int id);
void enable_grabber(EType type, unsigned int id, bool enable);
void update(const Linef3& mouse_ray, const Point& mouse_pos);
/// <summary>
/// Distribute information about different data into active gizmo
/// Should be called when selection changed
/// </summary>
void update_data();
EType get_current_type() const { return m_current; }
@ -203,28 +198,7 @@ public:
bool handle_shortcut(int key);
bool is_dragging() const;
void start_dragging();
void stop_dragging();
Vec3d get_displacement() const;
Vec3d get_scale() const;
void set_scale(const Vec3d& scale);
Vec3d get_scale_offset() const;
Vec3d get_rotation() const;
void set_rotation(const Vec3d& rotation);
Vec3d get_flattening_normal() const;
void set_flattening_data(const ModelObject* model_object);
void set_sla_support_data(ModelObject* model_object);
void set_painter_gizmo_data();
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false, bool alt_down = false, bool control_down = false);
ClippingPlane get_clipping_plane() const;
bool wants_reslice_supports_on_undo() const;
@ -241,21 +215,26 @@ public:
std::string get_tooltip() const;
bool on_mouse(wxMouseEvent& evt);
bool on_mouse_wheel(wxMouseEvent& evt);
bool on_mouse(const wxMouseEvent &mouse_event);
bool on_mouse_wheel(const wxMouseEvent &evt);
bool on_char(wxKeyEvent& evt);
bool on_key(wxKeyEvent& evt);
void update_after_undo_redo(const UndoRedo::Snapshot& snapshot);
int get_selectable_icons_cnt() const { return get_selectable_idxs().size(); }
int get_shortcut_key(GLGizmosManager::EType) const;
// To end highlight set gizmo = undefined
void set_highlight(EType gizmo, bool highlight_shown) { m_highlight = std::pair<EType, bool>(gizmo, highlight_shown); }
bool get_highlight_state() const { return m_highlight.second; }
private:
bool gizmo_event(SLAGizmoEventType action,
const Vec2d & mouse_position = Vec2d::Zero(),
bool shift_down = false,
bool alt_down = false,
bool control_down = false);
void render_background(float left, float top, float right, float bottom, float border) const;
void do_render_overlay() const;
@ -265,8 +244,7 @@ private:
bool generate_icons_texture() const;
void update_on_off_state(const Vec2d& mouse_pos);
std::string update_hover_state(const Vec2d& mouse_pos);
void update_hover_state(const EType &type);
bool grabber_contains_mouse() const;
};

View file

@ -1733,8 +1733,8 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data)
{
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
ImGuiIO& io = ImGui::GetIO();
int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y);
const int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x);
const int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y);
if (fb_width == 0 || fb_height == 0)
return;
draw_data->ScaleClipRects(io.DisplayFramebufferScale);
@ -1777,8 +1777,7 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data)
// Render command lists
ImVec2 pos = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
for (int n = 0; n < draw_data->CmdListsCount; ++n) {
const ImDrawList* cmd_list = draw_data->CmdLists[n];
const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;
const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;
@ -1786,19 +1785,14 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data)
glsafe(::glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv))));
glsafe(::glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col))));
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; ++cmd_i) {
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback)
{
// User callback (registered via ImDrawList::AddCallback)
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
else {
ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y);
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
{
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) {
// Apply scissor/clipping rectangle
glsafe(::glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)));

View file

@ -859,7 +859,10 @@ bool MainFrame::save_project_as(const wxString& filename)
{
bool ret = (m_plater != nullptr) ? m_plater->export_3mf(into_path(filename)) : false;
if (ret) {
// wxGetApp().update_saved_preset_from_current_preset();
// Make a copy of the active presets for detecting changes in preset values.
wxGetApp().update_saved_preset_from_current_preset();
// Save the names of active presets and project specific config into ProjectDirtyStateManager.
// Reset ProjectDirtyStateManager's state as saved, mark active UndoRedo step as saved with project.
m_plater->reset_project_dirty_after_save();
}
return ret;

View file

@ -311,7 +311,11 @@ wxString get_wraped_wxString(const wxString& in, size_t line_len /*=80*/)
overwrite = true;
if (newline)
break;
} else if (in[j] == '/') {
} else if (in[j] == '/'
#ifdef _WIN32
|| in[j] == '\\'
#endif // _WIN32
) {
// Insert after the slash.
ibreak = ++ j;
overwrite = false;

View file

@ -245,7 +245,7 @@ public:
GetBtnsListCtrl()->Rescale();
}
void Notebook::OnNavigationKey(wxNavigationKeyEvent& event)
void OnNavigationKey(wxNavigationKeyEvent& event)
{
if (event.IsWindowChange()) {
// change pages

View file

@ -57,9 +57,9 @@
#include "GUI_ObjectManipulation.hpp"
#include "GUI_ObjectLayers.hpp"
#include "GUI_Utils.hpp"
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
#include "GUI_Geometry.hpp"
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
#include "GUI_Factories.hpp"
#include "wxExtensions.hpp"
#include "MainFrame.hpp"
@ -502,6 +502,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
std::vector<float> extruders = dlg.get_extruders();
(project_config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = std::vector<double>(matrix.begin(), matrix.end());
(project_config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values = std::vector<double>(extruders.begin(), extruders.end());
// Update Project dirty state, update application title bar.
wxGetApp().plater()->update_project_dirty_from_presets();
wxPostEvent(parent, SimpleEvent(EVT_SCHEDULE_BACKGROUND_PROCESS, parent));
}
@ -1521,10 +1522,10 @@ void Sidebar::update_mode()
wxWindowUpdateLocker noUpdates(this);
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
if (m_mode == comSimple)
p->object_manipulation->set_coordinates_type(ECoordinatesType::World);
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
p->object_list->get_sizer()->Show(m_mode > comSimple);
@ -2359,7 +2360,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
const auto loading = _L("Loading") + dots;
wxProgressDialog dlg(loading, "", 100, find_toplevel_parent(q), wxPD_AUTO_HIDE);
wxProgressDialog progress_dlg(loading, "", 100, find_toplevel_parent(q), wxPD_AUTO_HIDE);
wxBusyCursor busy;
auto *new_model = (!load_model || one_by_one) ? nullptr : new Slic3r::Model();
@ -2368,7 +2369,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
int answer_convert_from_meters = wxOK_DEFAULT;
int answer_convert_from_imperial_units = wxOK_DEFAULT;
for (size_t i = 0; i < input_files.size(); ++i) {
size_t input_files_size = input_files.size();
for (size_t i = 0; i < input_files_size; ++i) {
#ifdef _WIN32
auto path = input_files[i];
// On Windows, we swap slashes to back slashes, see GH #6803 as read_from_file() does not understand slashes on Windows thus it assignes full path to names of loaded objects.
@ -2378,8 +2380,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
const auto &path = input_files[i];
#endif // _WIN32
const auto filename = path.filename();
dlg.Update(static_cast<int>(100.0f * static_cast<float>(i) / static_cast<float>(input_files.size())), _L("Loading file") + ": " + from_path(filename));
dlg.Fit();
progress_dlg.Update(static_cast<int>(100.0f * static_cast<float>(i) / static_cast<float>(input_files.size())), _L("Loading file") + ": " + from_path(filename));
progress_dlg.Fit();
const bool type_3mf = std::regex_match(path.string(), pattern_3mf);
const bool type_zip_amf = !type_3mf && std::regex_match(path.string(), pattern_zip_amf);
@ -2390,17 +2392,28 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
bool is_project_file = type_prusa;
try {
if (type_3mf || type_zip_amf) {
#ifdef __linux__
// On Linux Constructor of the ProgressDialog calls DisableOtherWindows() function which causes a disabling of all children of the find_toplevel_parent(q)
// And a destructor of the ProgressDialog calls ReenableOtherWindows() function which revert previously disabled children.
// But if printer technology will be changes during project loading,
// then related SLA Print and Materials Settings or FFF Print and Filaments Settings will be unparent from the wxNoteBook
// and that is why they will never be enabled after destruction of the ProgressDialog.
// So, distroy progress_gialog if we are loading project file
if (input_files_size == 1)
progress_dlg.Destroy();
#endif
DynamicPrintConfig config;
PrinterTechnology loaded_printer_technology {ptFFF};
{
DynamicPrintConfig config_loaded;
ConfigSubstitutionContext config_substitutions{ ForwardCompatibilitySubstitutionRule::Enable };
model = Slic3r::Model::read_from_archive(path.string(), &config_loaded, &config_substitutions, only_if(load_config, Model::LoadAttribute::CheckVersion));
if (load_config && !config_loaded.empty()) {
// Based on the printer technology field found in the loaded config, select the base for the config,
PrinterTechnology printer_technology = Preset::printer_technology(config_loaded);
loaded_printer_technology = Preset::printer_technology(config_loaded);
// We can't to load SLA project if there is at least one multi-part object on the bed
if (printer_technology == ptSLA) {
if (loaded_printer_technology == ptSLA) {
const ModelObjectPtrs& objects = q->model().objects;
for (auto object : objects)
if (object->volumes.size() > 1) {
@ -2412,7 +2425,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
}
config.apply(printer_technology == ptFFF ?
config.apply(loaded_printer_technology == ptFFF ?
static_cast<const ConfigBase&>(FullPrintConfig::defaults()) :
static_cast<const ConfigBase&>(SLAFullPrintConfig::defaults()));
// and place the loaded config over the base.
@ -2443,7 +2456,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
};
std::vector<std::string> names;
if (printer_technology == ptFFF) {
if (loaded_printer_technology == ptFFF) {
update_selected_preset_visibility(preset_bundle->prints, names);
for (const std::string& filament : preset_bundle->filament_presets) {
Preset* preset = preset_bundle->filaments.find_preset(filament);
@ -2474,7 +2487,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
}
if (printer_technology == ptFFF)
if (loaded_printer_technology == ptFFF)
CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, &preset_bundle->project_config);
// For exporting from the amf/3mf we shouldn't check printer_presets for the containing information about "Print Host upload"
@ -5275,8 +5288,11 @@ void Plater::new_project()
take_snapshot(_L("New Project"), UndoRedo::SnapshotType::ProjectSeparator);
Plater::SuppressSnapshots suppress(this);
reset();
// Save the names of active presets and project specific config into ProjectDirtyStateManager.
reset_project_dirty_initial_presets();
// Make a copy of the active presets for detecting changes in preset values.
wxGetApp().update_saved_preset_from_current_preset();
// Update Project dirty state, update application title bar.
update_project_dirty_from_presets();
}
@ -5305,7 +5321,11 @@ void Plater::load_project(const wxString& filename)
if (! load_files({ into_path(filename) }).empty()) {
// At least one file was loaded.
p->set_project_filename(filename);
// Save the names of active presets and project specific config into ProjectDirtyStateManager.
reset_project_dirty_initial_presets();
// Make a copy of the active presets for detecting changes in preset values.
wxGetApp().update_saved_preset_from_current_preset();
// Update Project dirty state, update application title bar.
update_project_dirty_from_presets();
}
}
@ -5990,7 +6010,6 @@ void Plater::export_stl_obj(bool extended, bool selection_only)
}
else if (0 <= instance_id && instance_id < int(mo.instances.size()))
mesh.transform(mo.instances[instance_id]->get_matrix(), true);
return mesh;
};

View file

@ -17,9 +17,11 @@ namespace GUI {
void ProjectDirtyStateManager::update_from_undo_redo_stack(bool dirty)
{
m_plater_dirty = dirty;
if (const Plater *plater = wxGetApp().plater(); plater && wxGetApp().initialized())
wxGetApp().mainframe->update_title();
if (m_plater_dirty != dirty) {
m_plater_dirty = dirty;
if (const Plater *plater = wxGetApp().plater(); plater && wxGetApp().initialized())
wxGetApp().mainframe->update_title();
}
}
void ProjectDirtyStateManager::update_from_presets()
@ -27,11 +29,11 @@ void ProjectDirtyStateManager::update_from_presets()
m_presets_dirty = false;
// check switching of the presets only for exist/loaded project, but not for new
GUI_App &app = wxGetApp();
if (!app.plater()->get_project_filename().IsEmpty()) {
for (const auto& [type, name] : app.get_selected_presets())
m_presets_dirty |= !m_initial_presets[type].empty() && m_initial_presets[type] != name;
bool has_project = ! app.plater()->get_project_filename().IsEmpty();
for (const PresetCollection *preset_collection : app.get_active_preset_collections()) {
auto type = preset_collection->type();
m_presets_dirty |= (has_project && !m_initial_presets[type].empty() && m_initial_presets[type] != preset_collection->get_selected_preset_name()) || preset_collection->saved_is_dirty();
}
m_presets_dirty |= app.has_unsaved_preset_changes();
m_project_config_dirty = m_initial_project_config != app.preset_bundle->project_config;
app.mainframe->update_title();
}
@ -49,8 +51,8 @@ void ProjectDirtyStateManager::reset_initial_presets()
{
m_initial_presets.fill(std::string{});
GUI_App &app = wxGetApp();
for (const auto& [type, name] : app.get_selected_presets())
m_initial_presets[type] = name;
for (const PresetCollection *preset_collection : app.get_active_preset_collections())
m_initial_presets[preset_collection->type()] = preset_collection->get_selected_preset_name();
m_initial_project_config = app.preset_bundle->project_config;
}

View file

@ -7,9 +7,14 @@
#include "GUI.hpp"
#include "GUI_ObjectManipulation.hpp"
#include "GUI_ObjectList.hpp"
#include "Gizmos/GLGizmoBase.hpp"
#include "Camera.hpp"
#include "Plater.hpp"
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
#include "MsgDialog.hpp"
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
#include "Gizmos/GLGizmoBase.hpp"
#include "slic3r/Utils/UndoRedo.hpp"
#include "libslic3r/LocalesUtils.hpp"
@ -115,9 +120,14 @@ Selection::Selection()
, m_type(Empty)
, m_valid(false)
, m_scale_factor(1.0f)
, m_dragging(false)
{
this->set_bounding_boxes_dirty();
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
m_axes.set_stem_radius(0.15f);
m_axes.set_stem_length(3.0f);
m_axes.set_tip_radius(0.45f);
m_axes.set_tip_length(1.5f);
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES
}
@ -592,19 +602,18 @@ bool Selection::matches(const std::vector<unsigned int>& volume_idxs) const
return count == (unsigned int)m_list.size();
}
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
bool Selection::requires_uniform_scale(EUniformScaleRequiredReason* reason) const
#else
bool Selection::requires_uniform_scale() const
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
{
#if ENABLE_WORLD_COORDINATE
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (is_empty())
return false;
ECoordinatesType coord_type = wxGetApp().obj_manipul()->get_coordinates_type();
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (is_single_volume_or_modifier())
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
{
if (is_single_volume_or_modifier()) {
if (coord_type == ECoordinatesType::World) {
if (!Geometry::is_rotation_ninety_degrees(Geometry::Transformation(get_volume(*m_list.begin())->world_matrix()).get_rotation())) {
if (reason != nullptr)
@ -621,11 +630,7 @@ bool Selection::requires_uniform_scale() const
}
return false;
}
#else
return !Geometry::is_rotation_ninety_degrees(Geometry::Transformation(get_volume(*m_list.begin())->world_matrix()).get_rotation());
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
else if (is_single_full_instance()) {
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (coord_type == ECoordinatesType::World) {
if (!Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_instance_rotation())) {
if (reason != nullptr)
@ -641,7 +646,6 @@ bool Selection::requires_uniform_scale() const
}
}
}
return false;
}
else {
for (unsigned int i : m_list) {
@ -651,17 +655,12 @@ bool Selection::requires_uniform_scale() const
return true;
}
}
return false;
}
return false;
}
if (reason != nullptr)
*reason = EUniformScaleRequiredReason::MultipleSelection;
#else
return wxGetApp().obj_manipul()->get_world_coordinates() ?
!Geometry::is_rotation_ninety_degrees(get_volume(*m_list.begin())->get_instance_rotation()) : false;
}
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
return true;
#else
@ -751,20 +750,19 @@ const BoundingBoxf3& Selection::get_scaled_instance_bounding_box() const
return *m_scaled_instance_bounding_box;
}
void Selection::start_dragging()
void Selection::setup_cache()
{
if (!m_valid)
return;
m_dragging = true;
set_caches();
}
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
void Selection::translate(const Vec3d& displacement, ECoordinatesType type)
#else
void Selection::translate(const Vec3d& displacement, bool local)
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
{
if (!m_valid)
return;
@ -774,19 +772,19 @@ void Selection::translate(const Vec3d& displacement, bool local)
for (unsigned int i : m_list) {
GLVolume& v = *(*m_volumes)[i];
if (m_mode == Volume || v.is_wipe_tower) {
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
if (type == ECoordinatesType::Instance)
#else
if (local)
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
v.set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement);
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
else if (type == ECoordinatesType::Local) {
const VolumeCache& volume_data = m_cache.volumes_data[i];
const Vec3d local_displacement = volume_data.get_volume_rotation_matrix() * displacement;
v.set_volume_offset(volume_data.get_volume_position() + local_displacement);
}
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
else {
#if ENABLE_WORLD_COORDINATE
const VolumeCache& volume_data = m_cache.volumes_data[i];
@ -801,15 +799,9 @@ void Selection::translate(const Vec3d& displacement, bool local)
else if (m_mode == Instance) {
#if ENABLE_WORLD_COORDINATE
if (is_from_fully_selected_instance(i)) {
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (type == ECoordinatesType::Local) {
const VolumeCache& volume_data = m_cache.volumes_data[i];
const Vec3d world_displacement = volume_data.get_instance_rotation_matrix() * displacement;
#else
if (local) {
const VolumeCache& volume_data = m_cache.volumes_data[i];
const Vec3d world_displacement = (volume_data.get_instance_rotation_matrix() * volume_data.get_instance_mirror_matrix()) * displacement;
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
v.set_instance_offset(volume_data.get_instance_position() + world_displacement);
}
else
@ -918,19 +910,15 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
rotate_instance(v, i);
#if ENABLE_WORLD_COORDINATE
else if (is_single_volume_or_modifier()) {
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (transformation_type.local()) {
const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
v.set_volume_rotation(Geometry::extract_euler_angles(m_cache.volumes_data[i].get_volume_rotation_matrix() * m));
const Vec3d new_rotation = transformation_type.absolute() ? rotation : Geometry::extract_euler_angles(m_cache.volumes_data[i].get_volume_rotation_matrix() * m);
v.set_volume_rotation(new_rotation);
}
else if (transformation_type.instance()) {
const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
v.set_volume_rotation(Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()));
}
#else
if (transformation_type.local())
v.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation() + rotation);
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
else {
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
m = m * m_cache.volumes_data[i].get_instance_rotation_matrix();
@ -1123,12 +1111,12 @@ void Selection::scale_to_fit_print_volume(const BuildVolume& volume)
type.set_joint();
// apply scale
start_dragging();
setup_cache();
scale(s * Vec3d::Ones(), type);
wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot
// center selection on print bed
start_dragging();
setup_cache();
offset.z() = -get_bounding_box().min.z();
translate(offset);
wxGetApp().plater()->canvas3D()->do_move(""); // avoid storing another snapshot
@ -1318,6 +1306,59 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co
this->set_bounding_boxes_dirty();
}
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
int Selection::bake_transform_if_needed() const
{
if ((is_single_full_instance() && wxGetApp().obj_manipul()->is_world_coordinates()) ||
(is_single_volume_or_modifier() && !wxGetApp().obj_manipul()->is_local_coordinates())) {
// Verify whether the instance rotation is multiples of 90 degrees, so that the scaling in world coordinates is possible.
// all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one
const GLVolume& volume = *get_volume(*get_volume_idxs().begin());
bool needs_baking = false;
if (is_single_full_instance()) {
// Is the instance angle close to a multiple of 90 degrees?
needs_baking |= !Geometry::is_rotation_ninety_degrees(volume.get_instance_rotation());
// Are all volumes angles close to a multiple of 90 degrees?
for (unsigned int id : get_volume_idxs()) {
if (needs_baking)
break;
needs_baking |= !Geometry::is_rotation_ninety_degrees(get_volume(id)->get_volume_rotation());
}
}
else if (is_single_volume_or_modifier()) {
// is the volume angle close to a multiple of 90 degrees?
needs_baking |= !Geometry::is_rotation_ninety_degrees(volume.get_volume_rotation());
if (wxGetApp().obj_manipul()->is_world_coordinates())
// Is the instance angle close to a multiple of 90 degrees?
needs_baking |= !Geometry::is_rotation_ninety_degrees(volume.get_instance_rotation());
}
if (needs_baking) {
MessageDialog dlg((wxWindow*)wxGetApp().mainframe,
_L("The currently manipulated object is tilted or contains tilted parts (rotation angles are not multiples of 90°). "
"Non-uniform scaling of tilted objects is only possible in non-local coordinate systems, "
"once the rotation is embedded into the object coordinates.") + "\n" +
_L("This operation is irreversible.") + "\n" +
_L("Do you want to proceed?"),
SLIC3R_APP_NAME,
wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
if (dlg.ShowModal() != wxID_YES)
return -1;
wxGetApp().plater()->take_snapshot(_("Bake transform"));
// Bake the rotation into the meshes of the object.
wxGetApp().model().objects[volume.composite_id.object_id]->bake_xy_rotation_into_meshes(volume.composite_id.instance_id);
// Update the 3D scene, selections etc.
wxGetApp().plater()->update();
return 0;
}
}
return 1;
}
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
void Selection::erase()
{
if (!m_valid)
@ -1431,7 +1472,33 @@ void Selection::render(float scale_factor)
m_scale_factor = scale_factor;
// render cumulative bounding box of selected volumes
#if ENABLE_GLBEGIN_GLEND_REMOVAL
#if ENABLE_COORDINATE_DEPENDENT_SELECTION_BOX
BoundingBoxf3 box;
Transform3d trafo;
const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type();
if (coordinates_type == ECoordinatesType::World) {
box = get_bounding_box();
trafo = Transform3d::Identity();
}
else if (coordinates_type == ECoordinatesType::Local && is_single_volume_or_modifier()) {
const GLVolume& v = *get_volume(*get_volume_idxs().begin());
box = v.transformed_convex_hull_bounding_box(v.get_instance_transformation().get_matrix(true, true, false, true) * v.get_volume_transformation().get_matrix(true, true, false, true));
trafo = v.get_instance_transformation().get_matrix(false, false, true, false) * v.get_volume_transformation().get_matrix(false, false, true, false);
}
else {
const Selection::IndicesList& ids = get_volume_idxs();
for (unsigned int id : ids) {
const GLVolume& v = *get_volume(id);
box.merge(v.transformed_convex_hull_bounding_box(v.get_volume_transformation().get_matrix()));
}
box = box.transformed(get_volume(*ids.begin())->get_instance_transformation().get_matrix(true, true, false, true));
trafo = get_volume(*ids.begin())->get_instance_transformation().get_matrix(false, false, true, false);
}
render_bounding_box(box, trafo, ColorRGB::WHITE());
#else
render_bounding_box(get_bounding_box(), ColorRGB::WHITE());
#endif // ENABLE_COORDINATE_DEPENDENT_SELECTION_BOX
#else
render_selected_volumes();
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
@ -1500,38 +1567,36 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field)
glsafe(::glEnable(GL_DEPTH_TEST));
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
const Vec3d center = get_bounding_box().center();
Vec3d axes_center = center;
Transform3d orient_matrix = Transform3d::Identity();
#else
glsafe(::glPushMatrix());
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES
if (!boost::starts_with(sidebar_field, "layer")) {
#if !ENABLE_WORLD_COORDINATE_SHOW_AXES
const Vec3d center = get_bounding_box().center();
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // !ENABLE_WORLD_COORDINATE_SHOW_AXES
#if ENABLE_WORLD_COORDINATE
if (is_single_full_instance() && !wxGetApp().obj_manipul()->is_world_coordinates()) {
#else
if (is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates()) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
#if !ENABLE_WORLD_COORDINATE_SHOW_AXES
glsafe(::glTranslated(center.x(), center.y(), center.z()));
#endif // !ENABLE_WORLD_COORDINATE_SHOW_AXES
#if ENABLE_WORLD_COORDINATE
#if !ENABLE_WORLD_COORDINATE_SHOW_AXES
Transform3d orient_matrix = Transform3d::Identity();
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // !ENABLE_WORLD_COORDINATE_SHOW_AXES
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
axes_center = (*m_volumes)[*m_list.begin()]->get_instance_offset();
#else
if (boost::starts_with(sidebar_field, "position") || boost::starts_with(sidebar_field, "scale"))
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
else if (boost::starts_with(sidebar_field, "rotation")) {
if (boost::ends_with(sidebar_field, "x"))
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
else if (boost::ends_with(sidebar_field, "y")) {
const Vec3d& rotation = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation();
if (rotation.x() == 0.0)
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
else
orient_matrix.rotate(Eigen::AngleAxisd(rotation.z(), Vec3d::UnitZ()));
}
}
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
glsafe(::glMultMatrixd(orient_matrix.data()));
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES
#else
if (!boost::starts_with(sidebar_field, "position")) {
Transform3d orient_matrix = Transform3d::Identity();
@ -1558,23 +1623,30 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field)
#else
else if (is_single_volume() || is_single_modifier()) {
#endif // ENABLE_WORLD_COORDINATE
#if !ENABLE_WORLD_COORDINATE_SHOW_AXES
glsafe(::glTranslated(center.x(), center.y(), center.z()));
#endif // !ENABLE_WORLD_COORDINATE_SHOW_AXES
#if ENABLE_WORLD_COORDINATE
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
if (!wxGetApp().obj_manipul()->is_world_coordinates()) {
#if !ENABLE_WORLD_COORDINATE_SHOW_AXES
Transform3d orient_matrix = Transform3d::Identity();
#endif // !ENABLE_WORLD_COORDINATE_SHOW_AXES
if (wxGetApp().obj_manipul()->is_local_coordinates()) {
#else
if (!wxGetApp().obj_manipul()->get_world_coordinates()) {
Transform3d orient_matrix = Transform3d::Identity();
if (boost::starts_with(sidebar_field, "scale")) {
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
const GLVolume* v = (*m_volumes)[*m_list.begin()];
orient_matrix = v->get_instance_transformation().get_matrix(true, false, true, true) * v->get_volume_transformation().get_matrix(true, false, true, true);
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
axes_center = (*m_volumes)[*m_list.begin()]->world_matrix().translation();
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES
}
else
else {
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
axes_center = (*m_volumes)[*m_list.begin()]->get_instance_offset();
}
#else
}
glsafe(::glMultMatrixd(orient_matrix.data()));
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES
}
#else
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
@ -1584,11 +1656,16 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field)
#endif // ENABLE_WORLD_COORDINATE
}
else {
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
if (requires_local_axes())
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
#else
glsafe(::glTranslated(center.x(), center.y(), center.z()));
if (requires_local_axes()) {
const Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
glsafe(::glMultMatrixd(orient_matrix.data()));
}
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES
}
}
@ -1597,6 +1674,15 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field)
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
if (!boost::starts_with(sidebar_field, "layer")) {
shader->set_uniform("emission_factor", 0.1f);
glsafe(::glPushMatrix());
glsafe(::glTranslated(center.x(), center.y(), center.z()));
glsafe(::glMultMatrixd(orient_matrix.data()));
}
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES
if (boost::starts_with(sidebar_field, "position"))
render_sidebar_position_hints(sidebar_field);
else if (boost::starts_with(sidebar_field, "rotation"))
@ -1606,7 +1692,19 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field)
else if (boost::starts_with(sidebar_field, "layer"))
render_sidebar_layers_hints(sidebar_field);
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
if (!boost::starts_with(sidebar_field, "layer")) {
glsafe(::glPopMatrix());
glsafe(::glPushMatrix());
glsafe(::glTranslated(axes_center.x(), axes_center.y(), axes_center.z()));
glsafe(::glMultMatrixd(orient_matrix.data()));
if (!wxGetApp().obj_manipul()->is_world_coordinates())
m_axes.render(0.25f);
glsafe(::glPopMatrix());
}
#else
glsafe(::glPopMatrix());
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES
#if !ENABLE_GLBEGIN_GLEND_REMOVAL
if (!boost::starts_with(sidebar_field, "layer"))
@ -2042,6 +2140,12 @@ void Selection::render_synchronized_volumes()
float color[3] = { 1.0f, 1.0f, 0.0f };
#endif // !ENABLE_GLBEGIN_GLEND_REMOVAL
#if ENABLE_COORDINATE_DEPENDENT_SELECTION_BOX
const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type();
BoundingBoxf3 box;
Transform3d trafo;
#endif // ENABLE_COORDINATE_DEPENDENT_SELECTION_BOX
for (unsigned int i : m_list) {
const GLVolume& volume = *(*m_volumes)[i];
int object_idx = volume.object_idx();
@ -2055,7 +2159,23 @@ void Selection::render_synchronized_volumes()
continue;
#if ENABLE_GLBEGIN_GLEND_REMOVAL
#if ENABLE_COORDINATE_DEPENDENT_SELECTION_BOX
if (coordinates_type == ECoordinatesType::World) {
box = v.transformed_convex_hull_bounding_box();
trafo = Transform3d::Identity();
}
else if (coordinates_type == ECoordinatesType::Local) {
box = v.bounding_box();
trafo = v.world_matrix();
}
else {
box = v.transformed_convex_hull_bounding_box(v.get_volume_transformation().get_matrix());
trafo = v.get_instance_transformation().get_matrix();
}
render_bounding_box(box, trafo, ColorRGB::YELLOW());
#else
render_bounding_box(v.transformed_convex_hull_bounding_box(), ColorRGB::YELLOW());
#endif // ENABLE_COORDINATE_DEPENDENT_SELECTION_BOX
#else
render_bounding_box(v.transformed_convex_hull_bounding_box(), color);
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
@ -2064,7 +2184,11 @@ void Selection::render_synchronized_volumes()
}
#if ENABLE_GLBEGIN_GLEND_REMOVAL
#if ENABLE_COORDINATE_DEPENDENT_SELECTION_BOX
void Selection::render_bounding_box(const BoundingBoxf3& box, const Transform3d& trafo, const ColorRGB& color)
#else
void Selection::render_bounding_box(const BoundingBoxf3& box, const ColorRGB& color)
#endif // ENABLE_COORDINATE_DEPENDENT_SELECTION_BOX
{
#else
void Selection::render_bounding_box(const BoundingBoxf3 & box, float* color) const
@ -2083,6 +2207,7 @@ void Selection::render_bounding_box(const BoundingBoxf3 & box, float* color) con
#if ENABLE_GLBEGIN_GLEND_REMOVAL
const BoundingBoxf3& curr_box = m_box.get_bounding_box();
if (!m_box.is_initialized() || !is_approx(box.min, curr_box.min) || !is_approx(box.max, curr_box.max)) {
m_box.reset();
@ -2168,10 +2293,19 @@ void Selection::render_bounding_box(const BoundingBoxf3 & box, float* color) con
if (shader == nullptr)
return;
#if ENABLE_COORDINATE_DEPENDENT_SELECTION_BOX
glsafe(::glPushMatrix());
glsafe(::glMultMatrixd(trafo.data()));
#endif // ENABLE_COORDINATE_DEPENDENT_SELECTION_BOX
shader->start_using();
m_box.set_color(to_rgba(color));
m_box.render();
shader->stop_using();
#if ENABLE_COORDINATE_DEPENDENT_SELECTION_BOX
glsafe(::glPopMatrix());
#endif // ENABLE_COORDINATE_DEPENDENT_SELECTION_BOX
#else
::glBegin(GL_LINES);
@ -2308,9 +2442,11 @@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field)
#else
m_arrow.set_color(-1, uniform_scale ? UNIFORM_SCALE_COLOR : get_color(axis));
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
#if !ENABLE_WORLD_COORDINATE_SHOW_AXES
GLShaderProgram* shader = wxGetApp().get_current_shader();
if (shader != nullptr)
shader->set_uniform("emission_factor", 0.0f);
#endif // !ENABLE_WORLD_COORDINATE_SHOW_AXES
glsafe(::glTranslated(0.0, 5.0, 0.0));
m_arrow.render();

View file

@ -2,10 +2,16 @@
#define slic3r_GUI_Selection_hpp_
#include "libslic3r/Geometry.hpp"
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#include "slic3r/GUI/GUI_Geometry.hpp"
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
#include "GUI_Geometry.hpp"
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
#include "CoordAxes.hpp"
#else
#include "GLModel.hpp"
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES
#else
#include "GLModel.hpp"
#endif // ENABLE_WORLD_COORDINATE
#include <set>
#include <optional>
@ -27,7 +33,7 @@ using ModelObjectPtrs = std::vector<ModelObject*>;
namespace GUI {
#if !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if !ENABLE_WORLD_COORDINATE
class TransformationType
{
public:
@ -80,7 +86,7 @@ private:
Enum m_value;
};
#endif // !ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // !ENABLE_WORLD_COORDINATE
class Selection
{
@ -221,6 +227,9 @@ private:
GLModel m_vbo_sphere;
#endif // ENABLE_RENDER_SELECTION_CENTER
#if ENABLE_WORLD_COORDINATE_SHOW_AXES
CoordAxes m_axes;
#endif // ENABLE_WORLD_COORDINATE_SHOW_AXES
GLModel m_arrow;
GLModel m_curved_arrow;
#if ENABLE_GLBEGIN_GLEND_REMOVAL
@ -234,7 +243,6 @@ private:
#endif // ENABLE_GLBEGIN_GLEND_REMOVAL
float m_scale_factor;
bool m_dragging;
public:
Selection();
@ -308,7 +316,7 @@ public:
// returns true if the selection contains all and only the given indices
bool matches(const std::vector<unsigned int>& volume_idxs) const;
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
enum class EUniformScaleRequiredReason : unsigned char
{
NotRequired,
@ -320,7 +328,7 @@ public:
bool requires_uniform_scale(EUniformScaleRequiredReason* reason = nullptr) const;
#else
bool requires_uniform_scale() const;
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
// Returns the the object id if the selection is from a single object, otherwise is -1
int get_object_idx() const;
@ -342,15 +350,13 @@ public:
const BoundingBoxf3& get_unscaled_instance_bounding_box() const;
const BoundingBoxf3& get_scaled_instance_bounding_box() const;
void start_dragging();
void stop_dragging() { m_dragging = false; }
bool is_dragging() const { return m_dragging; }
void setup_cache();
#if ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#if ENABLE_WORLD_COORDINATE
void translate(const Vec3d& displacement, ECoordinatesType type = ECoordinatesType::World);
#else
void translate(const Vec3d& displacement, bool local = false);
#endif // ENABLE_INSTANCE_COORDINATES_FOR_VOLUMES
#endif // ENABLE_WORLD_COORDINATE
void rotate(const Vec3d& rotation, TransformationType transformation_type);
void flattening_rotate(const Vec3d& normal);
void scale(const Vec3d& scale, TransformationType transformation_type);
@ -360,6 +366,14 @@ public:
void translate(unsigned int object_idx, const Vec3d& displacement);
void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement);
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
// returns:
// -1 if the user refused to proceed with baking when asked
// 0 if the baking was performed
// 1 if no baking was needed
int bake_transform_if_needed() const;
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
void erase();
void render(float scale_factor = 1.0);
@ -398,7 +412,11 @@ private:
void set_bounding_boxes_dirty() { m_bounding_box.reset(); m_unscaled_instance_bounding_box.reset(); m_scaled_instance_bounding_box.reset(); }
void render_synchronized_volumes();
#if ENABLE_GLBEGIN_GLEND_REMOVAL
#if ENABLE_COORDINATE_DEPENDENT_SELECTION_BOX
void render_bounding_box(const BoundingBoxf3& box, const Transform3d& trafo, const ColorRGB& color);
#else
void render_bounding_box(const BoundingBoxf3& box, const ColorRGB& color);
#endif // ENABLE_COORDINATE_DEPENDENT_SELECTION_BOX
#else
void render_selected_volumes() const;
void render_bounding_box(const BoundingBoxf3& box, float* color) const;

View file

@ -1247,6 +1247,7 @@ void Tab::on_presets_changed()
// to avoid needless preset loading from update() function
m_dependent_tabs.clear();
// Update Project dirty state, update application title bar.
wxGetApp().plater()->update_project_dirty_from_presets();
}
@ -4113,7 +4114,7 @@ wxSizer* TabPrint::create_manage_substitution_widget(wxWindow* parent)
create_btn(&m_del_all_substitutions_btn, _L("Delete all"), "cross");
m_del_all_substitutions_btn->Bind(wxEVT_BUTTON, [this, parent](wxCommandEvent e) {
if (MessageDialog(parent, _L("Are you sure you want to delete all substitutions?"), SLIC3R_APP_NAME, wxYES_NO | wxICON_QUESTION).
if (MessageDialog(parent, _L("Are you sure you want to delete all substitutions?"), SLIC3R_APP_NAME, wxYES_NO | wxCANCEL | wxICON_QUESTION).
ShowModal() != wxID_YES)
return;
m_subst_manager.delete_all();

View file

@ -371,6 +371,7 @@ public:
DynamicPrintConfig* get_config() { return m_config; }
PresetCollection* get_presets() { return m_presets; }
const PresetCollection* get_presets() const { return m_presets; }
void on_value_change(const std::string& opt_key, const boost::any& value);

View file

@ -581,8 +581,12 @@ void LockButton::OnButton(wxCommandEvent& event)
if (m_disabled)
return;
#if ENABLE_WORLD_COORDINATE_SCALE_REVISITED
SetLock(!m_is_pushed);
#else
m_is_pushed = !m_is_pushed;
update_button_bitmaps();
#endif // ENABLE_WORLD_COORDINATE_SCALE_REVISITED
event.Skip();
}

View file

@ -1,3 +1,18 @@
#include <catch_main.hpp>
#include "libslic3r/libslic3r.h"
// __has_feature() is used later for Clang, this is for compatibility with other compilers (such as GCC and MSVC)
#ifndef __has_feature
# define __has_feature(x) 0
#endif
// Print reports about memory leaks but exit with zero exit code when any memory leaks is found to make unit tests pass.
// After merging the stable branch (2.4.1) with the master branch, this should be deleted.
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
extern "C" {
const char *__lsan_default_options() {
return "exitcode=0";
}
}
#endif