Merge branch 'master' into tm_clang_mingw

This commit is contained in:
tamasmeszaros 2019-08-16 16:37:02 +02:00
commit 65368db49b
70 changed files with 6746 additions and 5247 deletions

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="eye_x5F_close">
<path fill="none" stroke="#808080" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
M2,8c0,0,2,4,6,4s6-4,6-4s-2-4-6-4S2,8,2,8z"/>
<circle fill="none" stroke="#808080" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" cx="8" cy="8" r="1"/>
<line fill="none" stroke="#ED6B21" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="2" y1="14" x2="14" y2="2"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 856 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="eye_x5F_open">
<path fill="none" stroke="#808080" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="
M2,8c0,0,2,4,6,4s6-4,6-4s-2-4-6-4S2,8,2,8z"/>
<circle fill="none" stroke="#808080" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" cx="8" cy="8" r="1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 697 B

File diff suppressed because it is too large Load diff

View file

@ -26,6 +26,7 @@
#include <boost/nowide/args.hpp>
#include <boost/nowide/cenv.hpp>
#include <boost/nowide/iostream.hpp>
#include <boost/nowide/integration/filesystem.hpp>
#include "unix/fhs.hpp" // Generated by CMake from ../platform/unix/fhs.hpp.in
@ -59,6 +60,9 @@ PrinterTechnology get_printer_technology(const DynamicConfig &config)
int CLI::run(int argc, char **argv)
{
// Switch boost::filesystem to utf8.
boost::nowide::nowide_filesystem();
if (! this->setup(argc, argv))
return 1;
@ -499,6 +503,7 @@ int CLI::run(int argc, char **argv)
bool CLI::setup(int argc, char **argv)
{
{
Slic3r::set_logging_level(1);
const char *loglevel = boost::nowide::getenv("SLIC3R_LOGLEVEL");
if (loglevel != nullptr) {
if (loglevel[0] >= '0' && loglevel[0] <= '9' && loglevel[1] == 0)

View file

@ -6,7 +6,7 @@
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "ray_box_intersect.h"
#include <vector>
#include <array>
template <
typename Derivedsource,
@ -101,11 +101,11 @@ IGL_INLINE bool igl::ray_box_intersect(
// This should be precomputed and provided as input
typedef Matrix<Scalar,1,3> RowVector3S;
const RowVector3S inv_dir( 1./dir(0),1./dir(1),1./dir(2));
const std::vector<bool> sign = { inv_dir(0)<0, inv_dir(1)<0, inv_dir(2)<0};
const std::array<bool, 3> sign = { inv_dir(0)<0, inv_dir(1)<0, inv_dir(2)<0};
// http://people.csail.mit.edu/amy/papers/box-jgt.pdf
// "An Efficient and Robust RayBox Intersection Algorithm"
Scalar tymin, tymax, tzmin, tzmax;
std::vector<RowVector3S> bounds = {box.min(),box.max()};
std::array<RowVector3S, 2> bounds = {box.min(),box.max()};
tmin = ( bounds[sign[0]](0) - origin(0)) * inv_dir(0);
tmax = ( bounds[1-sign[0]](0) - origin(0)) * inv_dir(0);
tymin = (bounds[sign[1]](1) - origin(1)) * inv_dir(1);

View file

@ -30,6 +30,8 @@ IGL_INLINE bool igl::ray_mesh_intersect(
Vector3d s_d = s.template cast<double>();
Vector3d dir_d = dir.template cast<double>();
hits.clear();
hits.reserve(F.rows());
// loop over all triangles
for(int f = 0;f<F.rows();f++)
{

View file

@ -507,8 +507,11 @@ BedShapeHint::BedShapeHint(const Polyline &bed) {
m_type = BedShapes::bsCircle;
m_bed.circ = c;
} else {
if (m_type == BedShapes::bsIrregular)
m_bed.polygon.Slic3r::Polyline::~Polyline();
m_type = BedShapes::bsIrregular;
m_bed.polygon = bed;
::new (&m_bed.polygon) Polyline(bed);
}
}

View file

@ -45,12 +45,12 @@ class BedShapeHint {
Polyline polygon;
InfiniteBed infbed{};
~BedShape_u() {}
BedShape_u() {};
BedShape_u() {}
} m_bed;
public:
BedShapeHint(){};
BedShapeHint(){}
/// Get a bed shape hint for arrange() from a naked Polyline.
explicit BedShapeHint(const Polyline &polyl);
@ -73,7 +73,7 @@ public:
{
if (m_type == BedShapes::bsIrregular)
m_bed.polygon.Slic3r::Polyline::~Polyline();
};
}
BedShapeHint(const BedShapeHint &cpy) { *this = cpy; }
BedShapeHint(BedShapeHint &&cpy) { *this = std::move(cpy); }

View file

@ -375,7 +375,7 @@ public:
this->values[i] = rhs_vec->values[i];
modified = true;
}
return false;
return modified;
}
private:

View file

@ -76,10 +76,14 @@ float Flow::spacing() const
return this->width + BRIDGE_EXTRA_SPACING;
// rectangle with semicircles at the ends
float min_flow_spacing = this->width - this->height * (1. - 0.25 * PI);
return this->width - PERIMETER_LINE_OVERLAP_FACTOR * (this->width - min_flow_spacing);
float res = this->width - PERIMETER_LINE_OVERLAP_FACTOR * (this->width - min_flow_spacing);
#else
return float(this->bridge ? (this->width + BRIDGE_EXTRA_SPACING) : (this->width - this->height * (1. - 0.25 * PI)));
float res = float(this->bridge ? (this->width + BRIDGE_EXTRA_SPACING) : (this->width - this->height * (1. - 0.25 * PI)));
#endif
// assert(res > 0.f);
if (res <= 0.f)
throw std::runtime_error("Flow::spacing() produced negative spacing. Did you set some extrusion width too small?");
return res;
}
// This method returns the centerline spacing between an extrusion using this
@ -89,20 +93,26 @@ float Flow::spacing(const Flow &other) const
{
assert(this->height == other.height);
assert(this->bridge == other.bridge);
return float(this->bridge ?
float res = float(this->bridge ?
0.5 * this->width + 0.5 * other.width + BRIDGE_EXTRA_SPACING :
0.5 * this->spacing() + 0.5 * other.spacing());
// assert(res > 0.f);
if (res <= 0.f)
throw std::runtime_error("Flow::spacing() produced negative spacing. Did you set some extrusion width too small?");
return res;
}
// This method returns extrusion volume per head move unit.
double Flow::mm3_per_mm() const
{
double res = this->bridge ?
float res = this->bridge ?
// Area of a circle with dmr of this->width.
(this->width * this->width) * 0.25 * PI :
// Rectangle with semicircles at the ends. ~ h (w - 0.215 h)
this->height * (this->width - this->height * (1. - 0.25 * PI));
assert(res > 0.);
//assert(res > 0.);
if (res <= 0.)
throw std::runtime_error("Flow::mm3_per_mm() produced negative flow. Did you set some extrusion width too small?");
return res;
}

View file

@ -71,6 +71,7 @@ const char* V2_ATTR = "v2";
const char* V3_ATTR = "v3";
const char* OBJECTID_ATTR = "objectid";
const char* TRANSFORM_ATTR = "transform";
const char* PRINTABLE_ATTR = "printable";
const char* KEY_ATTR = "key";
const char* VALUE_ATTR = "value";
@ -131,6 +132,12 @@ int get_attribute_value_int(const char** attributes, unsigned int attributes_siz
return (text != nullptr) ? ::atoi(text) : 0;
}
bool get_attribute_value_bool(const char** attributes, unsigned int attributes_size, const char* attribute_key)
{
const char* text = get_attribute_value_charptr(attributes, attributes_size, attribute_key);
return (text != nullptr) ? (bool)::atoi(text) : true;
}
Slic3r::Transform3d get_transform_from_string(const std::string& mat_str)
{
if (mat_str.empty())
@ -428,7 +435,7 @@ namespace Slic3r {
bool _handle_start_metadata(const char** attributes, unsigned int num_attributes);
bool _handle_end_metadata();
bool _create_object_instance(int object_id, const Transform3d& transform, unsigned int recur_counter);
bool _create_object_instance(int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter);
void _apply_transform(ModelInstance& instance, const Transform3d& transform);
@ -1367,8 +1374,9 @@ namespace Slic3r {
int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR);
Transform3d transform = get_transform_from_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
int printable = get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR);
return _create_object_instance(object_id, transform, 1);
return _create_object_instance(object_id, transform, printable, 1);
}
bool _3MF_Importer::_handle_end_item()
@ -1396,7 +1404,7 @@ namespace Slic3r {
return true;
}
bool _3MF_Importer::_create_object_instance(int object_id, const Transform3d& transform, unsigned int recur_counter)
bool _3MF_Importer::_create_object_instance(int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter)
{
static const unsigned int MAX_RECURSIONS = 10;
@ -1432,6 +1440,7 @@ namespace Slic3r {
add_error("Unable to add object instance");
return false;
}
instance->printable = printable;
m_instances.emplace_back(instance, transform);
}
@ -1441,7 +1450,7 @@ namespace Slic3r {
// recursively process nested components
for (const Component& component : it->second)
{
if (!_create_object_instance(component.object_id, transform * component.transform, recur_counter + 1))
if (!_create_object_instance(component.object_id, transform * component.transform, printable, recur_counter + 1))
return false;
}
}
@ -1655,10 +1664,12 @@ namespace Slic3r {
{
unsigned int id;
Transform3d transform;
bool printable;
BuildItem(unsigned int id, const Transform3d& transform)
BuildItem(unsigned int id, const Transform3d& transform, const bool printable)
: id(id)
, transform(transform)
, printable(printable)
{
}
};
@ -1951,7 +1962,7 @@ namespace Slic3r {
Transform3d t = instance->get_matrix();
// instance_id is just a 1 indexed index in build_items.
assert(instance_id == build_items.size() + 1);
build_items.emplace_back(instance_id, t);
build_items.emplace_back(instance_id, t, instance->printable);
stream << " </" << OBJECT_TAG << ">\n";
@ -2059,7 +2070,7 @@ namespace Slic3r {
stream << " ";
}
}
stream << "\" />\n";
stream << "\" printable =\"" << item.printable << "\" />\n";
}
stream << " </" << BUILD_TAG << ">\n";

View file

@ -137,6 +137,7 @@ struct AMFParserContext
NODE_TYPE_MIRRORX, // amf/constellation/instance/mirrorx
NODE_TYPE_MIRRORY, // amf/constellation/instance/mirrory
NODE_TYPE_MIRRORZ, // amf/constellation/instance/mirrorz
NODE_TYPE_PRINTABLE, // amf/constellation/instance/mirrorz
NODE_TYPE_METADATA, // anywhere under amf/*/metadata
};
@ -145,7 +146,8 @@ struct AMFParserContext
: deltax_set(false), deltay_set(false), deltaz_set(false)
, rx_set(false), ry_set(false), rz_set(false)
, scalex_set(false), scaley_set(false), scalez_set(false)
, mirrorx_set(false), mirrory_set(false), mirrorz_set(false) {}
, mirrorx_set(false), mirrory_set(false), mirrorz_set(false)
, printable(true) {}
// Shift in the X axis.
float deltax;
bool deltax_set;
@ -178,6 +180,8 @@ struct AMFParserContext
bool mirrory_set;
float mirrorz;
bool mirrorz_set;
// printable property
bool printable;
bool anything_set() const { return deltax_set || deltay_set || deltaz_set ||
rx_set || ry_set || rz_set ||
@ -321,6 +325,8 @@ void AMFParserContext::startElement(const char *name, const char **atts)
node_type_new = NODE_TYPE_MIRRORY;
else if (strcmp(name, "mirrorz") == 0)
node_type_new = NODE_TYPE_MIRRORZ;
else if (strcmp(name, "printable") == 0)
node_type_new = NODE_TYPE_PRINTABLE;
}
else if (m_path[2] == NODE_TYPE_LAYER_CONFIG && strcmp(name, "range") == 0) {
assert(m_object);
@ -397,7 +403,8 @@ void AMFParserContext::characters(const XML_Char *s, int len)
m_path.back() == NODE_TYPE_SCALE ||
m_path.back() == NODE_TYPE_MIRRORX ||
m_path.back() == NODE_TYPE_MIRRORY ||
m_path.back() == NODE_TYPE_MIRRORZ)
m_path.back() == NODE_TYPE_MIRRORZ ||
m_path.back() == NODE_TYPE_PRINTABLE)
m_value[0].append(s, len);
break;
case 6:
@ -507,6 +514,11 @@ void AMFParserContext::endElement(const char * /* name */)
m_instance->mirrorz_set = true;
m_value[0].clear();
break;
case NODE_TYPE_PRINTABLE:
assert(m_instance);
m_instance->printable = bool(atoi(m_value[0].c_str()));
m_value[0].clear();
break;
// Object vertices:
case NODE_TYPE_VERTEX:
@ -685,6 +697,7 @@ void AMFParserContext::endDocument()
mi->set_rotation(Vec3d(instance.rx_set ? (double)instance.rx : 0.0, instance.ry_set ? (double)instance.ry : 0.0, instance.rz_set ? (double)instance.rz : 0.0));
mi->set_scaling_factor(Vec3d(instance.scalex_set ? (double)instance.scalex : 1.0, instance.scaley_set ? (double)instance.scaley : 1.0, instance.scalez_set ? (double)instance.scalez : 1.0));
mi->set_mirror(Vec3d(instance.mirrorx_set ? (double)instance.mirrorx : 1.0, instance.mirrory_set ? (double)instance.mirrory : 1.0, instance.mirrorz_set ? (double)instance.mirrorz : 1.0));
mi->printable = instance.printable;
}
}
}
@ -1037,6 +1050,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
" <mirrorx>%lf</mirrorx>\n"
" <mirrory>%lf</mirrory>\n"
" <mirrorz>%lf</mirrorz>\n"
" <printable>%d</printable>\n"
" </instance>\n",
object_id,
instance->get_offset(X),
@ -1050,7 +1064,8 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
instance->get_scaling_factor(Z),
instance->get_mirror(X),
instance->get_mirror(Y),
instance->get_mirror(Z));
instance->get_mirror(Z),
instance->printable);
//FIXME missing instance->scaling_factor
instances.append(buf);

View file

@ -1,4 +1,5 @@
#include "libslic3r.h"
#include "I18N.hpp"
#include "GCode.hpp"
#include "ExtrusionEntity.hpp"
#include "EdgeGrid.hpp"
@ -36,6 +37,11 @@
namespace Slic3r {
//! macro used to mark string used at localization,
//! return same string
#define L(s) (s)
#define _(s) Slic3r::I18N::translate(s)
// Only add a newline in case the current G-code does not end with a newline.
static inline void check_add_eol(std::string &gcode)
{
@ -405,7 +411,8 @@ std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id,
assert(m_layer_idx >= 0 && size_t(m_layer_idx) <= m_tool_changes.size());
if (! m_brim_done || gcodegen.writer().need_toolchange(extruder_id) || finish_layer) {
if (m_layer_idx < (int)m_tool_changes.size()) {
assert(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size());
if (! (size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size()))
throw std::runtime_error("Wipe tower generation failed, possibly due to empty first layer.");
gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id);
}
m_brim_done = true;
@ -435,6 +442,7 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
// Pair the object layers with the support layers by z.
size_t idx_object_layer = 0;
size_t idx_support_layer = 0;
const LayerToPrint* last_extrusion_layer = nullptr;
while (idx_object_layer < object.layers().size() || idx_support_layer < object.support_layers().size()) {
LayerToPrint layer_to_print;
layer_to_print.object_layer = (idx_object_layer < object.layers().size()) ? object.layers()[idx_object_layer ++] : nullptr;
@ -448,7 +456,29 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
-- idx_object_layer;
}
}
layers_to_print.emplace_back(layer_to_print);
// In case there are extrusions on this layer, check there is a layer to lay it on.
if ((layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
|| (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions())) {
double support_contact_z = (last_extrusion_layer && last_extrusion_layer->support_layer)
? object.config().support_material_contact_distance
: 0.;
double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.)
+ layer_to_print.layer()->height
+ std::max(0., support_contact_z);
// Negative support_contact_z is not taken into account, it can result in false positives in cases
// where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752)
if (layer_to_print.print_z() > maximal_print_z + EPSILON)
throw std::runtime_error(_(L("Empty layers detected, the output would not be printable.")) + "\n\n" +
_(L("Object name: ")) + object.model_object()->name + "\n" + _(L("Print z: ")) +
std::to_string(layers_to_print.back().print_z()));
// Remember last layer with extrusions.
last_extrusion_layer = &layers_to_print.back();
}
}
return layers_to_print;
@ -561,11 +591,11 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
}
if (print->config().remaining_times.value) {
BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode";
BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode" << log_memory_info();
m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
m_normal_time_estimator.reset();
if (m_silent_time_estimator_enabled) {
BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode";
BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode" << log_memory_info();
m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
m_silent_time_estimator.reset();
}
@ -573,7 +603,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
// starts analyzer calculations
if (m_enable_analyzer) {
BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data";
BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data" << log_memory_info();
m_analyzer.calc_gcode_preview_data(*preview_data, [print]() { print->throw_if_canceled(); });
m_analyzer.reset();
}
@ -748,7 +778,7 @@ void GCode::_do_export(Print &print, FILE *file)
mm3_per_mm.erase(std::remove_if(mm3_per_mm.begin(), mm3_per_mm.end(), [](double v) { return v < 0.000001; }), mm3_per_mm.end());
if (! mm3_per_mm.empty()) {
// In order to honor max_print_speed we need to find a target volumetric
// speed that we can use throughout the print. So we define this target
// speed that we can use throughout the print. So we define this target
// volumetric speed as the volumetric speed produced by printing the
// smallest cross-section at the maximum speed: any larger cross-section
// will need slower feedrates.
@ -815,7 +845,7 @@ void GCode::_do_export(Print &print, FILE *file)
_writeln(file, GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag);
}
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
m_placeholder_parser = print.placeholder_parser();
m_placeholder_parser.update_timestamp();
print.update_object_placeholders(m_placeholder_parser.config_writable(), ".gcode");
@ -1138,9 +1168,9 @@ void GCode::_do_export(Print &print, FILE *file)
print.m_print_statistics.clear();
print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
print.m_print_statistics.estimated_normal_color_print_times = m_normal_time_estimator.get_color_times_dhms();
print.m_print_statistics.estimated_normal_color_print_times = m_normal_time_estimator.get_color_times_dhms(true);
if (m_silent_time_estimator_enabled)
print.m_print_statistics.estimated_silent_color_print_times = m_silent_time_estimator.get_color_times_dhms();
print.m_print_statistics.estimated_silent_color_print_times = m_silent_time_estimator.get_color_times_dhms(true);
std::vector<Extruder> extruders = m_writer.extruders();
if (! extruders.empty()) {
@ -1820,7 +1850,8 @@ void GCode::process_layer(
", time estimator memory: " <<
format_memsize_MB(m_normal_time_estimator.memory_used() + m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0) <<
", analyzer memory: " <<
format_memsize_MB(m_analyzer.memory_used());
format_memsize_MB(m_analyzer.memory_used()) <<
log_memory_info();
}
void GCode::apply_print_config(const PrintConfig &print_config)
@ -2598,7 +2629,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm
);
}
double F = speed * 60; // convert mm/sec to mm/min
double F = speed * 60; // convert mm/sec to mm/min
// extrude arc or line
if (m_enable_extrusion_role_markers)

View file

@ -78,8 +78,13 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
zs.emplace_back(layer->print_z);
for (auto layer : object->support_layers())
zs.emplace_back(layer->print_z);
if (! object->layers().empty())
object_bottom_z = object->layers().front()->print_z - object->layers().front()->height;
// Find first object layer that is not empty and save its print_z
for (const Layer* layer : object->layers())
if (layer->has_extrusions()) {
object_bottom_z = layer->print_z - layer->height;
break;
}
}
this->initialize_layers(zs);
}
@ -307,6 +312,7 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_
m_layer_tools[j].has_wipe_tower = true;
} else {
LayerTools &lt_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new);
//LayerTools &lt_prev = m_layer_tools[j];
LayerTools &lt_next = m_layer_tools[j + 1];
assert(! m_layer_tools[j - 1].extruders.empty() && ! lt_next.extruders.empty());
// FIXME: Following assert tripped when running combine_infill.t. I decided to comment it out for now.

View file

@ -694,22 +694,39 @@ namespace Slic3r {
return m_color_times;
}
std::vector<std::string> GCodeTimeEstimator::get_color_times_dhms() const
std::vector<std::string> GCodeTimeEstimator::get_color_times_dhms(bool include_remaining) const
{
std::vector<std::string> ret;
float total_time = 0.0f;
for (float t : m_color_times)
{
ret.push_back(_get_time_dhms(t));
std::string time = _get_time_dhms(t);
if (include_remaining)
{
time += " (";
time += _get_time_dhms(m_time - total_time);
time += ")";
}
total_time += t;
ret.push_back(time);
}
return ret;
}
std::vector<std::string> GCodeTimeEstimator::get_color_times_minutes() const
std::vector<std::string> GCodeTimeEstimator::get_color_times_minutes(bool include_remaining) const
{
std::vector<std::string> ret;
float total_time = 0.0f;
for (float t : m_color_times)
{
ret.push_back(_get_time_minutes(t));
std::string time = _get_time_minutes(t);
if (include_remaining)
{
time += " (";
time += _get_time_minutes(m_time - total_time);
time += ")";
}
total_time += t;
}
return ret;
}

View file

@ -350,10 +350,12 @@ namespace Slic3r {
std::vector<float> get_color_times() const;
// Returns the estimated time, in format DDd HHh MMm SSs, for each color
std::vector<std::string> get_color_times_dhms() const;
// If include_remaining==true the strings will be formatted as: "time for color (remaining time at color start)"
std::vector<std::string> get_color_times_dhms(bool include_remaining) const;
// Returns the estimated time, in minutes (integer), for each color
std::vector<std::string> get_color_times_minutes() const;
// If include_remaining==true the strings will be formatted as: "time for color (remaining time at color start)"
std::vector<std::string> get_color_times_minutes(bool include_remaining) const;
// Return an estimate of the memory consumed by the time estimator.
size_t memory_used() const;

View file

@ -21,8 +21,6 @@
namespace Slic3r {
unsigned int Model::s_auto_extruder_id = 1;
Model& Model::assign_copy(const Model &rhs)
{
this->copy_id(rhs);
@ -99,8 +97,7 @@ Model Model::read_from_file(const std::string &input_file, DynamicPrintConfig *c
result = load_stl(input_file.c_str(), &model);
else if (boost::algorithm::iends_with(input_file, ".obj"))
result = load_obj(input_file.c_str(), &model);
else if (!boost::algorithm::iends_with(input_file, ".zip.amf") && (boost::algorithm::iends_with(input_file, ".amf") ||
boost::algorithm::iends_with(input_file, ".amf.xml")))
else if (boost::algorithm::iends_with(input_file, ".amf") || boost::algorithm::iends_with(input_file, ".amf.xml"))
result = load_amf(input_file.c_str(), config, &model);
else if (boost::algorithm::iends_with(input_file, ".3mf"))
result = load_3mf(input_file.c_str(), config, &model);
@ -486,9 +483,20 @@ bool Model::looks_like_multipart_object() const
return false;
}
// Generate next extruder ID string, in the range of (1, max_extruders).
static inline std::string auto_extruder_id(unsigned int max_extruders, unsigned int &cntr)
{
char str_extruder[64];
sprintf(str_extruder, "%ud", cntr + 1);
if (++ cntr == max_extruders)
cntr = 0;
return str_extruder;
}
void Model::convert_multipart_object(unsigned int max_extruders)
{
if (this->objects.empty())
assert(this->objects.size() >= 2);
if (this->objects.size() < 2)
return;
ModelObject* object = new ModelObject(this);
@ -496,56 +504,30 @@ void Model::convert_multipart_object(unsigned int max_extruders)
object->name = this->objects.front()->name;
//FIXME copy the config etc?
reset_auto_extruder_id();
bool is_single_object = (this->objects.size() == 1);
unsigned int extruder_counter = 0;
for (const ModelObject* o : this->objects)
{
for (const ModelVolume* v : o->volumes)
{
if (is_single_object)
{
// If there is only one object, just copy the volumes
ModelVolume* new_v = object->add_volume(*v);
if (new_v != nullptr)
{
new_v->name = o->name;
new_v->config.set_deserialize("extruder", get_auto_extruder_id_as_string(max_extruders));
new_v->translate(-o->origin_translation);
}
}
else
{
for (const ModelVolume* v : o->volumes) {
// If there are more than one object, put all volumes together
// Each object may contain any number of volumes and instances
// The volumes transformations are relative to the object containing them...
Geometry::Transformation trafo_volume = v->get_transformation();
// Revert the centering operation.
trafo_volume.set_offset(trafo_volume.get_offset() - o->origin_translation);
int counter = 1;
for (const ModelInstance* i : o->instances)
{
ModelVolume* new_v = object->add_volume(*v);
if (new_v != nullptr)
{
auto copy_volume = [o, max_extruders, &counter, &extruder_counter](ModelVolume *new_v) {
assert(new_v != nullptr);
new_v->name = o->name + "_" + std::to_string(counter++);
new_v->config.set_deserialize("extruder", get_auto_extruder_id_as_string(max_extruders));
new_v->translate(-o->origin_translation);
new_v->config.set_deserialize("extruder", auto_extruder_id(max_extruders, extruder_counter));
return new_v;
};
if (o->instances.empty()) {
copy_volume(object->add_volume(*v))->set_transformation(trafo_volume);
} else {
for (const ModelInstance* i : o->instances)
// ...so, transform everything to a common reference system (world)
new_v->set_transformation(i->get_transformation() * v->get_transformation());
copy_volume(object->add_volume(*v))->set_transformation(i->get_transformation() * trafo_volume);
}
}
}
}
}
if (is_single_object)
{
// If there is only one object, keep its instances
for (const ModelInstance* i : this->objects.front()->instances)
{
object->add_instance(*i);
}
}
else
// If there are more than one object, create a single instance
object->add_instance();
@ -572,32 +554,6 @@ void Model::adjust_min_z()
}
}
unsigned int Model::get_auto_extruder_id(unsigned int max_extruders)
{
unsigned int id = s_auto_extruder_id;
if (id > max_extruders) {
// The current counter is invalid, likely due to switching the printer profiles
// to a profile with a lower number of extruders.
reset_auto_extruder_id();
id = s_auto_extruder_id;
} else if (++ s_auto_extruder_id > max_extruders) {
reset_auto_extruder_id();
}
return id;
}
std::string Model::get_auto_extruder_id_as_string(unsigned int max_extruders)
{
char str_extruder[64];
sprintf(str_extruder, "%ud", get_auto_extruder_id(max_extruders));
return str_extruder;
}
void Model::reset_auto_extruder_id()
{
s_auto_extruder_id = 1;
}
// Propose a filename including path derived from the ModelObject's input path.
// If object's name is filled in, use the object name, otherwise use the input name.
std::string Model::propose_export_file_name_and_path() const
@ -644,6 +600,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
this->sla_points_status = rhs.sla_points_status;
this->layer_config_ranges = rhs.layer_config_ranges; // #ys_FIXME_experiment
this->layer_height_profile = rhs.layer_height_profile;
this->printable = rhs.printable;
this->origin_translation = rhs.origin_translation;
m_bounding_box = rhs.m_bounding_box;
m_bounding_box_valid = rhs.m_bounding_box_valid;
@ -1662,7 +1619,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin();
std::string name = this->name;
Model::reset_auto_extruder_id();
unsigned int extruder_counter = 0;
Vec3d offset = this->get_offset();
for (TriangleMesh *mesh : meshptrs) {
@ -1681,7 +1638,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
this->object->volumes[ivolume]->center_geometry_after_creation();
this->object->volumes[ivolume]->translate(offset);
this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1);
this->object->volumes[ivolume]->config.set_deserialize("extruder", Model::get_auto_extruder_id_as_string(max_extruders));
this->object->volumes[ivolume]->config.set_deserialize("extruder", auto_extruder_id(max_extruders, extruder_counter));
delete mesh;
++ idx;
}

View file

@ -192,6 +192,8 @@ public:
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
// The pairs of <z, layer_height> are packed into a 1D array.
std::vector<coordf_t> layer_height_profile;
// Whether or not this object is printable
bool printable;
// This vector holds position of selected support points for SLA. The data are
// saved in mesh coordinates to allow using them for several instances.
@ -304,10 +306,10 @@ public:
private:
friend class Model;
// This constructor assigns new ID to this ModelObject and its config.
explicit ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()),
explicit ModelObject(Model* model) : m_model(model), printable(true), origin_translation(Vec3d::Zero()),
m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false)
{ assert(this->id().valid()); }
explicit ModelObject(int) : ObjectBase(-1), config(-1), m_model(nullptr), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false)
explicit ModelObject(int) : ObjectBase(-1), config(-1), m_model(nullptr), printable(true), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false)
{ assert(this->id().invalid()); assert(this->config.id().invalid()); }
~ModelObject();
void assign_new_unique_ids_recursive() override;
@ -370,7 +372,7 @@ private:
template<class Archive> void serialize(Archive &ar) {
ar(cereal::base_class<ObjectBase>(this));
Internal::StaticSerializationWrapper<ModelConfig> config_wrapper(config);
ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, origin_translation,
ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, printable, origin_translation,
m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid);
}
};
@ -595,6 +597,8 @@ private:
public:
// flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state())
EPrintVolumeState print_volume_state;
// Whether or not this instance is printable
bool printable;
ModelObject* get_object() const { return this->object; }
@ -639,7 +643,7 @@ public:
const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); }
bool is_printable() const { return print_volume_state == PVS_Inside; }
bool is_printable() const { return object->printable && printable && (print_volume_state == PVS_Inside); }
// Getting the input polygon for arrange
arrangement::ArrangePolygon get_arrange_polygon() const;
@ -667,10 +671,10 @@ private:
ModelObject* object;
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object) : print_volume_state(PVS_Inside), object(object) { assert(this->id().valid()); }
explicit ModelInstance(ModelObject* object) : print_volume_state(PVS_Inside), printable(true), object(object) { assert(this->id().valid()); }
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object, const ModelInstance &other) :
m_transformation(other.m_transformation), print_volume_state(PVS_Inside), object(object) { assert(this->id().valid() && this->id() != other.id()); }
m_transformation(other.m_transformation), print_volume_state(PVS_Inside), printable(true), object(object) {assert(this->id().valid() && this->id() != other.id());}
explicit ModelInstance(ModelInstance &&rhs) = delete;
ModelInstance& operator=(const ModelInstance &rhs) = delete;
@ -681,7 +685,7 @@ private:
// Used for deserialization, therefore no IDs are allocated.
ModelInstance() : ObjectBase(-1), object(nullptr) { assert(this->id().invalid()); }
template<class Archive> void serialize(Archive &ar) {
ar(m_transformation, print_volume_state);
ar(m_transformation, print_volume_state, printable);
}
};
@ -721,8 +725,6 @@ private:
// all objects may share mutliple materials.
class Model final : public ObjectBase
{
static unsigned int s_auto_extruder_id;
public:
// Materials are owned by a model and referenced by objects through t_model_material_id.
// Single material may be shared by multiple models.
@ -791,10 +793,6 @@ public:
void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); }
static unsigned int get_auto_extruder_id(unsigned int max_extruders);
static std::string get_auto_extruder_id_as_string(unsigned int max_extruders);
static void reset_auto_extruder_id();
// Propose an output file name & path based on the first printable object's name and source input file's path.
std::string propose_export_file_name_and_path() const;
// Propose an output path, replace extension. The new_extension shall contain the initial dot.

View file

@ -175,6 +175,11 @@ void PlaceholderParser::apply_env_variables()
}
namespace spirit = boost::spirit;
// Using an encoding, which accepts unsigned chars.
// Don't use boost::spirit::ascii, as it crashes internally due to indexing with negative char values for UTF8 characters into some 7bit character classification tables.
//namespace spirit_encoding = boost::spirit::ascii;
//FIXME iso8859_1 is just a workaround for the problem above. Replace it with UTF8 support!
namespace spirit_encoding = boost::spirit::iso8859_1;
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
@ -931,7 +936,7 @@ namespace client
///////////////////////////////////////////////////////////////////////////
// Inspired by the C grammar rules https://www.lysator.liu.se/c/ANSI-C-grammar-y.html
template <typename Iterator>
struct macro_processor : qi::grammar<Iterator, std::string(const MyContext*), qi::locals<bool>, spirit::ascii::space_type>
struct macro_processor : qi::grammar<Iterator, std::string(const MyContext*), qi::locals<bool>, spirit_encoding::space_type>
{
macro_processor() : macro_processor::base_type(start)
{
@ -944,12 +949,12 @@ namespace client
qi::lexeme_type lexeme;
qi::no_skip_type no_skip;
qi::real_parser<double, strict_real_policies_without_nan_inf> strict_double;
spirit::ascii::char_type char_;
spirit_encoding::char_type char_;
utf8_char_skipper_parser utf8char;
spirit::bool_type bool_;
spirit::int_type int_;
spirit::double_type double_;
spirit::ascii::string_type string;
spirit_encoding::string_type string;
spirit::eoi_type eoi;
spirit::repository::qi::iter_pos_type iter_pos;
auto kw = spirit::repository::qi::distinct(qi::copy(alnum | '_'));
@ -1178,20 +1183,20 @@ namespace client
}
// Generic expression over expr<Iterator>.
typedef qi::rule<Iterator, expr<Iterator>(const MyContext*), spirit::ascii::space_type> RuleExpression;
typedef qi::rule<Iterator, expr<Iterator>(const MyContext*), spirit_encoding::space_type> RuleExpression;
// The start of the grammar.
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool>, spirit::ascii::space_type> start;
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool>, spirit_encoding::space_type> start;
// A free-form text.
qi::rule<Iterator, std::string(), spirit::ascii::space_type> text;
qi::rule<Iterator, std::string(), spirit_encoding::space_type> text;
// A free-form text, possibly empty, possibly containing macro expansions.
qi::rule<Iterator, std::string(const MyContext*), spirit::ascii::space_type> text_block;
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> text_block;
// Statements enclosed in curely braces {}
qi::rule<Iterator, std::string(const MyContext*), spirit::ascii::space_type> macro;
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> macro;
// Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index].
qi::rule<Iterator, std::string(const MyContext*), spirit::ascii::space_type> legacy_variable_expansion;
qi::rule<Iterator, std::string(const MyContext*), spirit_encoding::space_type> legacy_variable_expansion;
// Parsed identifier name.
qi::rule<Iterator, boost::iterator_range<Iterator>(), spirit::ascii::space_type> identifier;
qi::rule<Iterator, boost::iterator_range<Iterator>(), spirit_encoding::space_type> identifier;
// Ternary operator (?:) over logical_or_expression.
RuleExpression conditional_expression;
// Logical or over logical_and_expressions.
@ -1209,16 +1214,16 @@ namespace client
// Number literals, functions, braced expressions, variable references, variable indexing references.
RuleExpression unary_expression;
// Rule to capture a regular expression enclosed in //.
qi::rule<Iterator, boost::iterator_range<Iterator>(), spirit::ascii::space_type> regular_expression;
qi::rule<Iterator, boost::iterator_range<Iterator>(), spirit_encoding::space_type> regular_expression;
// Evaluate boolean expression into bool.
qi::rule<Iterator, bool(const MyContext*), spirit::ascii::space_type> bool_expr_eval;
qi::rule<Iterator, bool(const MyContext*), spirit_encoding::space_type> bool_expr_eval;
// Reference of a scalar variable, or reference to a field of a vector variable.
qi::rule<Iterator, expr<Iterator>(const MyContext*), qi::locals<OptWithPos<Iterator>, int>, spirit::ascii::space_type> scalar_variable_reference;
qi::rule<Iterator, expr<Iterator>(const MyContext*), qi::locals<OptWithPos<Iterator>, int>, spirit_encoding::space_type> scalar_variable_reference;
// Rule to translate an identifier to a ConfigOption, or to fail.
qi::rule<Iterator, OptWithPos<Iterator>(const MyContext*), spirit::ascii::space_type> variable_reference;
qi::rule<Iterator, OptWithPos<Iterator>(const MyContext*), spirit_encoding::space_type> variable_reference;
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, bool>, spirit::ascii::space_type> if_else_output;
// qi::rule<Iterator, std::string(const MyContext*), qi::locals<expr<Iterator>, bool, std::string>, spirit::ascii::space_type> switch_output;
qi::rule<Iterator, std::string(const MyContext*), qi::locals<bool, bool>, spirit_encoding::space_type> if_else_output;
// qi::rule<Iterator, std::string(const MyContext*), qi::locals<expr<Iterator>, bool, std::string>, spirit_encoding::space_type> switch_output;
qi::symbols<char> keywords;
};
@ -1230,7 +1235,7 @@ static std::string process_macro(const std::string &templ, client::MyContext &co
typedef client::macro_processor<iterator_type> macro_processor;
// Our whitespace skipper.
spirit::ascii::space_type space;
spirit_encoding::space_type space;
// Our grammar, statically allocated inside the method, meaning it will be allocated the first time
// PlaceholderParser::process() runs.
//FIXME this kind of initialization is not thread safe!

View file

@ -18,6 +18,7 @@
#include <limits>
#include <unordered_set>
#include <boost/filesystem/path.hpp>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
//! macro used to mark string used at localization,
@ -1148,11 +1149,17 @@ std::string Print::validate() const
}
if (this->has_wipe_tower() && ! m_objects.empty()) {
// make sure all extruders use same diameter filament and have the same nozzle diameter
// Make sure all extruders use same diameter filament and have the same nozzle diameter
// EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments
double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders().front());
double first_filament_diam = m_config.filament_diameter.get_at(extruders().front());
for (const auto& extruder_idx : extruders()) {
if (m_config.nozzle_diameter.get_at(extruder_idx) != m_config.nozzle_diameter.get_at(extruders().front())
|| m_config.filament_diameter.get_at(extruder_idx) != m_config.filament_diameter.get_at(extruders().front()))
return L("The wipe tower is only supported if all extruders have the same nozzle diameter and use filaments of the same diameter.");
double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx);
double filament_diam = m_config.filament_diameter.get_at(extruder_idx);
if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam
|| std::abs((filament_diam-first_filament_diam)/first_filament_diam) > 0.1)
return L("The wipe tower is only supported if all extruders have the same nozzle diameter "
"and use filaments of the same diameter.");
}
if (m_config.gcode_flavor != gcfRepRap && m_config.gcode_flavor != gcfRepetier && m_config.gcode_flavor != gcfMarlin)
@ -1160,15 +1167,11 @@ std::string Print::validate() const
if (! m_config.use_relative_e_distances)
return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).");
for (size_t i=1; i<m_config.nozzle_diameter.values.size(); ++i)
if (m_config.nozzle_diameter.values[i] != m_config.nozzle_diameter.values[i-1])
return L("All extruders must have the same diameter for the Wipe Tower.");
if (m_objects.size() > 1) {
bool has_custom_layering = false;
std::vector<std::vector<coordf_t>> layer_height_profiles;
for (const PrintObject *object : m_objects) {
has_custom_layering = ! object->model_object()->layer_config_ranges.empty() || ! object->model_object()->layer_height_profile.empty(); // #ys_FIXME_experiment
has_custom_layering = ! object->model_object()->layer_config_ranges.empty() || ! object->model_object()->layer_height_profile.empty();
if (has_custom_layering) {
layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>());
break;
@ -1247,6 +1250,20 @@ std::string Print::validate() const
return L("One or more object were assigned an extruder that the printer does not have.");
#endif
auto validate_extrusion_width = [min_nozzle_diameter, max_nozzle_diameter](const ConfigBase &config, const char *opt_key, double layer_height, std::string &err_msg) -> bool {
double extrusion_width_min = config.get_abs_value(opt_key, min_nozzle_diameter);
double extrusion_width_max = config.get_abs_value(opt_key, max_nozzle_diameter);
if (extrusion_width_min == 0) {
// Default "auto-generated" extrusion width is always valid.
} else if (extrusion_width_min <= layer_height) {
err_msg = (boost::format(L("%1%=%2% mm is too low to be printable at a layer height %3% mm")) % opt_key % extrusion_width_min % layer_height).str();
return false;
} else if (extrusion_width_max >= max_nozzle_diameter * 2.) {
err_msg = (boost::format(L("Excessive %1%=%2% mm to be printable with a nozzle diameter %3% mm")) % opt_key % extrusion_width_max % max_nozzle_diameter).str();
return false;
}
return true;
};
for (PrintObject *object : m_objects) {
if (object->config().raft_layers > 0 || object->config().support_material.value) {
if ((object->config().support_material_extruder == 0 || object->config().support_material_interface_extruder == 0) && max_nozzle_diameter - min_nozzle_diameter > EPSILON) {
@ -1290,8 +1307,20 @@ std::string Print::validate() const
return L("First layer height can't be greater than nozzle diameter");
// validate layer_height
if (object->config().layer_height.value > min_nozzle_diameter)
double layer_height = object->config().layer_height.value;
if (layer_height > min_nozzle_diameter)
return L("Layer height can't be greater than nozzle diameter");
// Validate extrusion widths.
std::string err_msg;
if (! validate_extrusion_width(object->config(), "extrusion_width", layer_height, err_msg))
return err_msg;
if ((object->config().support_material || object->config().raft_layers > 0) && ! validate_extrusion_width(object->config(), "support_material_extrusion_width", layer_height, err_msg))
return err_msg;
for (const char *opt_key : { "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width" })
for (size_t i = 0; i < object->region_volumes.size(); ++ i)
if (! object->region_volumes[i].empty() && ! validate_extrusion_width(this->get_region(i)->config(), opt_key, layer_height, err_msg))
return err_msg;
}
}
@ -1723,7 +1752,7 @@ void Print::_make_wipe_tower()
break;
lt.has_support = true;
// Insert the new support layer.
double height = lt.print_z - m_wipe_tower_data.tool_ordering.layer_tools()[i-1].print_z;
double height = lt.print_z - (i == 0 ? 0. : m_wipe_tower_data.tool_ordering.layer_tools()[i-1].print_z);
//FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway.
it_layer = m_objects.front()->insert_support_layer(it_layer, -1, height, lt.print_z, lt.print_z - 0.5 * height);
++ it_layer;

View file

@ -422,6 +422,7 @@ void PrintConfigDef::init_fff_params()
def->cli = "bottom-fill-pattern|external-fill-pattern|solid-fill-pattern";
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
def->enum_values = def_top_fill_pattern->enum_values;
def->enum_labels = def_top_fill_pattern->enum_labels;
def->aliases = def_top_fill_pattern->aliases;
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipRectilinear));
@ -2675,14 +2676,14 @@ void PrintConfigDef::init_sla_params()
def->set_default_value(new ConfigOptionFloat(50.0));
// This is disabled on the UI. I hope it will never be enabled.
def = this->add("pad_edge_radius", coFloat);
def->label = L("Pad edge radius");
def->category = L("Pad");
// def->tooltip = L("");
def->sidetext = L("mm");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(1.0));
// def = this->add("pad_edge_radius", coFloat);
// def->label = L("Pad edge radius");
// def->category = L("Pad");
//// def->tooltip = L("");
// def->sidetext = L("mm");
// def->min = 0;
// def->mode = comAdvanced;
// def->set_default_value(new ConfigOptionFloat(1.0));
def = this->add("pad_wall_slope", coFloat);
def->label = L("Pad wall slope");
@ -2695,6 +2696,13 @@ void PrintConfigDef::init_sla_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(45.0));
def = this->add("pad_zero_elevation", coBool);
def->label = L("Pad around object");
def->category = L("Pad");
def->tooltip = L("Create pad around object and ignore the support elevation");
def->mode = comSimple;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("pad_object_gap", coFloat);
def->label = L("Pad object gap");
def->category = L("Pad");

View file

@ -1028,7 +1028,7 @@ public:
ConfigOptionFloat pad_max_merge_distance /*= 50*/;
// The smoothing radius of the pad edges
ConfigOptionFloat pad_edge_radius /*= 1*/;
// ConfigOptionFloat pad_edge_radius /*= 1*/;
// The slope of the pad wall...
ConfigOptionFloat pad_wall_slope;
@ -1041,6 +1041,9 @@ public:
// - The two pads will be connected with tiny connector sticks
// /////////////////////////////////////////////////////////////////////////
// Disable the elevation (ignore its value) and use the zero elevation mode
ConfigOptionBool pad_zero_elevation;
// This is the gap between the object bottom and the generated pad
ConfigOptionFloat pad_object_gap;
@ -1080,8 +1083,9 @@ protected:
OPT_PTR(pad_wall_thickness);
OPT_PTR(pad_wall_height);
OPT_PTR(pad_max_merge_distance);
OPT_PTR(pad_edge_radius);
// OPT_PTR(pad_edge_radius);
OPT_PTR(pad_wall_slope);
OPT_PTR(pad_zero_elevation);
OPT_PTR(pad_object_gap);
OPT_PTR(pad_object_connector_stride);
OPT_PTR(pad_object_connector_width);

View file

@ -713,7 +713,7 @@ struct Pad {
}
tmesh.translate(0, 0, float(zlevel));
tmesh.require_shared_vertices();
if (!tmesh.empty()) tmesh.require_shared_vertices();
}
bool empty() const { return tmesh.facets_count() == 0; }
@ -2597,39 +2597,51 @@ void SLASupportTree::merged_mesh_with_pad(TriangleMesh &outmesh) const {
}
std::vector<ExPolygons> SLASupportTree::slice(
const std::vector<float> &heights, float cr) const
const std::vector<float> &grid, float cr) const
{
const TriangleMesh &sup_mesh = m_impl->merged_mesh();
const TriangleMesh &pad_mesh = get_pad();
std::vector<ExPolygons> sup_slices;
using Slices = std::vector<ExPolygons>;
auto slices = reserve_vector<Slices>(2);
if (!sup_mesh.empty()) {
slices.emplace_back();
TriangleMeshSlicer sup_slicer(&sup_mesh);
sup_slicer.slice(heights, cr, &sup_slices, m_impl->ctl().cancelfn);
sup_slicer.slice(grid, cr, &slices.back(), m_impl->ctl().cancelfn);
}
if (!pad_mesh.empty()) {
slices.emplace_back();
auto bb = pad_mesh.bounding_box();
auto maxzit = std::upper_bound(heights.begin(), heights.end(), bb.max.z());
auto maxzit = std::upper_bound(grid.begin(), grid.end(), bb.max.z());
auto padgrid = reserve_vector<float>(heights.end() - maxzit);
std::copy(heights.begin(), maxzit, std::back_inserter(padgrid));
auto padgrid = reserve_vector<float>(grid.end() - maxzit);
std::copy(grid.begin(), maxzit, std::back_inserter(padgrid));
std::vector<ExPolygons> pad_slices;
if (!pad_mesh.empty()) {
TriangleMeshSlicer pad_slicer(&pad_mesh);
pad_slicer.slice(padgrid, cr, &pad_slices, m_impl->ctl().cancelfn);
pad_slicer.slice(padgrid, cr, &slices.back(), m_impl->ctl().cancelfn);
}
size_t len = std::min(heights.size(), pad_slices.size());
len = std::min(len, sup_slices.size());
size_t len = grid.size();
for (const Slices &slv : slices) { len = std::min(len, slv.size()); }
// Either the support or the pad or both has to be non empty
if (slices.empty()) return {};
Slices &mrg = slices.front();
for (auto it = std::next(slices.begin()); it != slices.end(); ++it) {
for (size_t i = 0; i < len; ++i) {
std::copy(pad_slices[i].begin(), pad_slices[i].end(),
std::back_inserter(sup_slices[i]));
pad_slices[i] = {};
Slices &slv = *it;
std::copy(slv[i].begin(), slv[i].end(), std::back_inserter(mrg[i]));
slv[i] = {}; // clear and delete
}
}
return sup_slices;
return mrg;
}
const TriangleMesh &SLASupportTree::add_pad(const ExPolygons& modelbase,

View file

@ -575,6 +575,16 @@ std::string SLAPrint::output_filename(const std::string &filename_base) const
}
namespace {
bool is_zero_elevation(const SLAPrintObjectConfig &c) {
bool en_implicit = c.support_object_elevation.getFloat() <= EPSILON &&
c.pad_enable.getBool() && c.supports_enable.getBool();
bool en_explicit = c.pad_zero_elevation.getBool() &&
c.supports_enable.getBool();
return en_implicit || en_explicit;
}
// Compile the argument for support creation from the static print config.
sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
sla::SupportConfig scfg;
@ -583,7 +593,8 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
scfg.head_back_radius_mm = 0.5*c.support_pillar_diameter.getFloat();
scfg.head_penetration_mm = c.support_head_penetration.getFloat();
scfg.head_width_mm = c.support_head_width.getFloat();
scfg.object_elevation_mm = c.support_object_elevation.getFloat();
scfg.object_elevation_mm = is_zero_elevation(c) ?
0. : c.support_object_elevation.getFloat();
scfg.bridge_slope = c.support_critical_angle.getFloat() * PI / 180.0 ;
scfg.max_bridge_length_mm = c.support_max_bridge_length.getFloat();
scfg.max_pillar_link_distance_mm = c.support_max_pillar_link_distance.getFloat();
@ -609,8 +620,7 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
sla::PoolConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c) {
sla::PoolConfig::EmbedObject ret;
ret.enabled = c.support_object_elevation.getFloat() <= EPSILON &&
c.pad_enable.getBool() && c.supports_enable.getBool();
ret.enabled = is_zero_elevation(c);
if(ret.enabled) {
ret.object_gap_mm = c.pad_object_gap.getFloat();
@ -667,7 +677,9 @@ std::string SLAPrint::validate() const
double elv = cfg.object_elevation_mm;
if(supports_en && elv > EPSILON && elv < pinhead_width )
return L("Elevation is too low for object.");
return L(
"Elevation is too low for object. Use the \"Pad around "
"obect\" feature to print the object without elevation.");
sla::PoolConfig::EmbedObject builtinpad = builtin_pad_cfg(po->config());
if(supports_en && builtinpad.enabled &&
@ -755,7 +767,7 @@ void SLAPrint::process()
for(coord_t h = minZs + ilhs + lhs; h <= maxZs; h += lhs)
po.m_slice_index.emplace_back(h, unscaled<float>(h) - lh / 2.f, lh);
// Just get the first record that is form the model:
// Just get the first record that is from the model:
auto slindex_it =
po.closest_slice_record(po.m_slice_index, float(bb3d.min(Z)));
@ -885,7 +897,7 @@ void SLAPrint::process()
// If the zero elevation mode is engaged, we have to filter out all the
// points that are on the bottom of the object
if (po.config().support_object_elevation.getFloat() <= EPSILON) {
if (is_zero_elevation(po.config())) {
double gnd = po.m_supportdata->emesh.ground_level();
auto & pts = po.m_supportdata->support_points;
double tolerance = po.config().pad_enable.getBool()
@ -1668,6 +1680,7 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf
|| opt_key == "pad_wall_thickness"
|| opt_key == "supports_enable"
|| opt_key == "support_object_elevation"
|| opt_key == "pad_zero_elevation"
|| opt_key == "slice_closing_radius") {
steps.emplace_back(slaposObjectSlice);
} else if (
@ -1740,7 +1753,10 @@ bool SLAPrintObject::invalidate_all_steps()
}
double SLAPrintObject::get_elevation() const {
if (is_zero_elevation(m_config)) return 0.;
bool en = m_config.supports_enable.getBool();
double ret = en ? m_config.support_object_elevation.getFloat() : 0.;
if(m_config.pad_enable.getBool()) {
@ -1757,6 +1773,8 @@ double SLAPrintObject::get_elevation() const {
double SLAPrintObject::get_current_elevation() const
{
if (is_zero_elevation(m_config)) return 0.;
bool has_supports = is_step_done(slaposSupportTree);
bool has_pad = is_step_done(slaposBasePool);

View file

@ -18,8 +18,9 @@ extern void trace(unsigned int level, const char *message);
// Format memory allocated, separate thousands by comma.
extern std::string format_memsize_MB(size_t n);
// Return string to be added to the boost::log output to inform about the current process memory allocation.
// The string is non-empty only if the loglevel >= info (3).
extern std::string log_memory_info();
// The string is non-empty if the loglevel >= info (3) or ignore_loglevel==true.
// Latter is used to get the memory info from SysInfoDialog.
extern std::string log_memory_info(bool ignore_loglevel = false);
extern void disable_multi_threading();
// Returns the size of physical memory (RAM) in bytes.
extern size_t total_physical_memory();

View file

@ -13,9 +13,13 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/resource.h>
#ifdef BSD
#include <sys/sysctl.h>
#endif
#ifdef __APPLE__
#include <mach/mach.h>
#endif
#endif
#include <boost/log/core.hpp>
@ -29,7 +33,6 @@
#include <boost/filesystem.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/nowide/fstream.hpp>
#include <boost/nowide/integration/filesystem.hpp>
#include <boost/nowide/convert.hpp>
#include <boost/nowide/cstdio.hpp>
@ -89,10 +92,10 @@ unsigned get_logging_level()
}
// Force set_logging_level(<=error) after loading of the DLL.
// Switch boost::filesystem to utf8.
// This is currently only needed if libslic3r is loaded as a shared library into Perl interpreter
// to perform unit and integration tests.
static struct RunOnInit {
RunOnInit() {
boost::nowide::nowide_filesystem();
set_logging_level(1);
}
} g_RunOnInit;
@ -431,8 +434,14 @@ std::string format_memsize_MB(size_t n)
return out + "MB";
}
// Returns platform-specific string to be used as log output or parsed in SysInfoDialog.
// The latter parses the string with (semi)colons as separators, it should look about as
// "desc1: value1; desc2: value2" or similar (spaces should not matter).
std::string log_memory_info(bool ignore_loglevel)
{
std::string out;
if (ignore_loglevel || logSeverity <= boost::log::trivial::info) {
#ifdef WIN32
#ifndef PROCESS_MEMORY_COUNTERS_EX
// MingW32 doesn't have this struct in psapi.h
typedef struct _PROCESS_MEMORY_COUNTERS_EX {
@ -450,28 +459,57 @@ std::string format_memsize_MB(size_t n)
} PROCESS_MEMORY_COUNTERS_EX, *PPROCESS_MEMORY_COUNTERS_EX;
#endif /* PROCESS_MEMORY_COUNTERS_EX */
std::string log_memory_info()
{
std::string out;
if (logSeverity <= boost::log::trivial::info) {
HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ::GetCurrentProcessId());
if (hProcess != nullptr) {
PROCESS_MEMORY_COUNTERS_EX pmc;
if (GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)))
out = " WorkingSet: " + format_memsize_MB(pmc.WorkingSetSize) + " PrivateBytes: " + format_memsize_MB(pmc.PrivateUsage) + " Pagefile(peak): " + format_memsize_MB(pmc.PagefileUsage) + "(" + format_memsize_MB(pmc.PeakPagefileUsage) + ")";
out = " WorkingSet: " + format_memsize_MB(pmc.WorkingSetSize) + "; PrivateBytes: " + format_memsize_MB(pmc.PrivateUsage) + "; Pagefile(peak): " + format_memsize_MB(pmc.PagefileUsage) + "(" + format_memsize_MB(pmc.PeakPagefileUsage) + ")";
else
out += " Used memory: N/A";
CloseHandle(hProcess);
}
#elif defined(__linux__) or defined(__APPLE__)
// Get current memory usage.
#ifdef __APPLE__
struct mach_task_basic_info info;
mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
out += " Resident memory: ";
if ( task_info( mach_task_self( ), MACH_TASK_BASIC_INFO, (task_info_t)&info, &infoCount ) == KERN_SUCCESS )
out += format_memsize_MB((size_t)info.resident_size);
else
out += "N/A";
#else // i.e. __linux__
size_t tSize = 0, resident = 0, share = 0;
std::ifstream buffer("/proc/self/statm");
if (buffer && (buffer >> tSize >> resident >> share)) {
size_t page_size = (size_t)sysconf(_SC_PAGE_SIZE); // in case x86-64 is configured to use 2MB pages
size_t rss = resident * page_size;
out += " Resident memory: " + format_memsize_MB(rss);
out += "; Shared memory: " + format_memsize_MB(share * page_size);
out += "; Private memory: " + format_memsize_MB(rss - share * page_size);
}
else
out += " Used memory: N/A";
#endif
// Now get peak memory usage.
out += "; Peak memory usage: ";
rusage memory_info;
if (getrusage(RUSAGE_SELF, &memory_info) == 0)
{
size_t peak_mem_usage = (size_t)memory_info.ru_maxrss;
#ifdef __linux__
peak_mem_usage *= 1024;// getrusage returns the value in kB on linux
#endif
out += format_memsize_MB(peak_mem_usage);
}
else
out += "N/A";
#endif
}
return out;
}
#else
std::string log_memory_info()
{
return std::string();
}
#endif
// Returns the size of physical memory (RAM) in bytes.
// http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system
size_t total_physical_memory()

View file

@ -12,6 +12,7 @@
#include "libslic3r/GCode/Analyzer.hpp"
#include "slic3r/GUI/PresetBundle.hpp"
#include "libslic3r/Format/STL.hpp"
#include "libslic3r/Utils.hpp"
#include <stdio.h>
#include <stdlib.h>
@ -74,15 +75,19 @@ void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh &mesh)
}
}
void GLIndexedVertexArray::finalize_geometry() const
void GLIndexedVertexArray::finalize_geometry(bool opengl_initialized)
{
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
assert(this->triangle_indices_VBO_id == 0);
assert(this->quad_indices_VBO_id == 0);
if (! opengl_initialized) {
// Shrink the data vectors to conserve memory in case the data cannot be transfered to the OpenGL driver yet.
this->shrink_to_fit();
return;
}
if (! empty()) {
if (! this->vertices_and_normals_interleaved.empty()) {
glsafe(::glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
glsafe(::glBufferData(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved.size() * 4, this->vertices_and_normals_interleaved.data(), GL_STATIC_DRAW));
@ -124,13 +129,8 @@ void GLIndexedVertexArray::release_geometry()
void GLIndexedVertexArray::render() const
{
if (this->vertices_and_normals_interleaved_VBO_id == 0)
{
// sends data to gpu, if not done yet
finalize_geometry();
if (this->vertices_and_normals_interleaved_VBO_id == 0)
return;
}
assert(this->vertices_and_normals_interleaved_VBO_id != 0);
assert(this->triangle_indices_VBO_id != 0 || this->quad_indices_VBO_id != 0);
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
@ -161,13 +161,8 @@ void GLIndexedVertexArray::render(
const std::pair<size_t, size_t>& tverts_range,
const std::pair<size_t, size_t>& qverts_range) const
{
if (this->vertices_and_normals_interleaved_VBO_id == 0)
{
// sends data to gpu, if not done yet
finalize_geometry();
if (this->vertices_and_normals_interleaved_VBO_id == 0)
return;
}
assert(this->vertices_and_normals_interleaved_VBO_id != 0);
assert(this->triangle_indices_VBO_id != 0 || this->quad_indices_VBO_id != 0);
// Render using the Vertex Buffer Objects.
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
@ -218,6 +213,7 @@ GLVolume::GLVolume(float r, float g, float b, float a)
, extruder_id(0)
, selected(false)
, disabled(false)
, printable(true)
, is_active(true)
, zoom_to_volumes(true)
, shader_outside_printer_detection_enabled(false)
@ -275,6 +271,13 @@ void GLVolume::set_render_color()
set_render_color(color, 4);
}
if (!printable)
{
render_color[0] /= 4;
render_color[1] /= 4;
render_color[2] /= 4;
}
if (force_transparent)
render_color[3] = color[3];
}
@ -418,12 +421,13 @@ std::vector<int> GLVolumeCollection::load_object(
const ModelObject *model_object,
int obj_idx,
const std::vector<int> &instance_idxs,
const std::string& color_by)
const std::string &color_by,
bool opengl_initialized)
{
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)
volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by));
volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by, opengl_initialized));
return volumes_idx;
}
@ -432,7 +436,8 @@ int GLVolumeCollection::load_object_volume(
int obj_idx,
int volume_idx,
int instance_idx,
const std::string& color_by)
const std::string &color_by,
bool opengl_initialized)
{
const ModelVolume *model_volume = model_object->volumes[volume_idx];
const int extruder_id = model_volume->extruder_id();
@ -455,6 +460,7 @@ int GLVolumeCollection::load_object_volume(
GLVolume& v = *this->volumes.back();
v.set_color_from_model_volume(model_volume);
v.indexed_vertex_array.load_mesh(mesh);
v.indexed_vertex_array.finalize_geometry(opengl_initialized);
v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx);
if (model_volume->is_model_part())
{
@ -481,7 +487,8 @@ void GLVolumeCollection::load_object_auxiliary(
const std::vector<std::pair<size_t, size_t>>& instances,
SLAPrintObjectStep milestone,
// Timestamp of the last change of the milestone
size_t timestamp)
size_t timestamp,
bool opengl_initialized)
{
assert(print_object->is_step_done(milestone));
Transform3d mesh_trafo_inv = print_object->trafo().inverse();
@ -495,6 +502,7 @@ void GLVolumeCollection::load_object_auxiliary(
this->volumes.emplace_back(new GLVolume((milestone == slaposBasePool) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR));
GLVolume& v = *this->volumes.back();
v.indexed_vertex_array.load_mesh(mesh);
v.indexed_vertex_array.finalize_geometry(opengl_initialized);
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.
@ -511,7 +519,7 @@ void GLVolumeCollection::load_object_auxiliary(
}
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)
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)
{
if (depth < 0.01f)
return int(this->volumes.size() - 1);
@ -564,6 +572,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
this->volumes.emplace_back(new GLVolume(color));
GLVolume& v = *this->volumes.back();
v.indexed_vertex_array.load_mesh(mesh);
v.indexed_vertex_array.finalize_geometry(opengl_initialized);
v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
v.composite_id = GLVolume::CompositeID(obj_idx, 0, 0);
@ -823,6 +832,27 @@ std::vector<double> GLVolumeCollection::get_current_print_zs(bool active_only) c
return print_zs;
}
size_t GLVolumeCollection::cpu_memory_used() const
{
size_t memsize = sizeof(*this) + this->volumes.capacity() * sizeof(GLVolume);
for (const GLVolume *volume : this->volumes)
memsize += volume->cpu_memory_used();
return memsize;
}
size_t GLVolumeCollection::gpu_memory_used() const
{
size_t memsize = 0;
for (const GLVolume *volume : this->volumes)
memsize += volume->gpu_memory_used();
return memsize;
}
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()) + ")";
}
// caller is responsible for supplying NO lines with zero length
static void thick_lines_to_indexed_vertex_array(
const Lines &lines,
@ -1598,6 +1628,7 @@ bool GLArrow::on_init()
triangles.emplace_back(7, 13, 6);
m_volume.indexed_vertex_array.load_mesh(TriangleMesh(vertices, triangles));
m_volume.indexed_vertex_array.finalize_geometry(true);
return true;
}
@ -1711,6 +1742,7 @@ bool GLCurvedArrow::on_init()
triangles.emplace_back(vertices_per_level, 2 * vertices_per_level + 1, vertices_per_level + 1);
m_volume.indexed_vertex_array.load_mesh(TriangleMesh(vertices, triangles));
m_volume.indexed_vertex_array.finalize_geometry(true);
return true;
}
@ -1737,6 +1769,7 @@ bool GLBed::on_init_from_file(const std::string& filename)
m_filename = filename;
m_volume.indexed_vertex_array.load_mesh(model.mesh());
m_volume.indexed_vertex_array.finalize_geometry(true);
float color[4] = { 0.235f, 0.235f, 0.235f, 1.0f };
set_color(color, 4);

View file

@ -64,7 +64,7 @@ public:
vertices_and_normals_interleaved_VBO_id(0),
triangle_indices_VBO_id(0),
quad_indices_VBO_id(0)
{}
{ assert(! rhs.has_VBOs()); }
GLIndexedVertexArray(GLIndexedVertexArray &&rhs) :
vertices_and_normals_interleaved(std::move(rhs.vertices_and_normals_interleaved)),
triangle_indices(std::move(rhs.triangle_indices)),
@ -72,7 +72,7 @@ public:
vertices_and_normals_interleaved_VBO_id(0),
triangle_indices_VBO_id(0),
quad_indices_VBO_id(0)
{}
{ assert(! rhs.has_VBOs()); }
~GLIndexedVertexArray() { release_geometry(); }
@ -80,14 +80,17 @@ public:
{
assert(vertices_and_normals_interleaved_VBO_id == 0);
assert(triangle_indices_VBO_id == 0);
assert(triangle_indices_VBO_id == 0);
assert(quad_indices_VBO_id == 0);
assert(rhs.vertices_and_normals_interleaved_VBO_id == 0);
assert(rhs.triangle_indices_VBO_id == 0);
assert(rhs.quad_indices_VBO_id == 0);
this->vertices_and_normals_interleaved = rhs.vertices_and_normals_interleaved;
this->triangle_indices = rhs.triangle_indices;
this->quad_indices = rhs.quad_indices;
this->m_bounding_box = rhs.m_bounding_box;
vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size;
triangle_indices_size = rhs.triangle_indices_size;
quad_indices_size = rhs.quad_indices_size;
this->vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size;
this->triangle_indices_size = rhs.triangle_indices_size;
this->quad_indices_size = rhs.quad_indices_size;
return *this;
}
@ -95,21 +98,24 @@ public:
{
assert(vertices_and_normals_interleaved_VBO_id == 0);
assert(triangle_indices_VBO_id == 0);
assert(triangle_indices_VBO_id == 0);
assert(quad_indices_VBO_id == 0);
assert(rhs.vertices_and_normals_interleaved_VBO_id == 0);
assert(rhs.triangle_indices_VBO_id == 0);
assert(rhs.quad_indices_VBO_id == 0);
this->vertices_and_normals_interleaved = std::move(rhs.vertices_and_normals_interleaved);
this->triangle_indices = std::move(rhs.triangle_indices);
this->quad_indices = std::move(rhs.quad_indices);
this->m_bounding_box = std::move(rhs.m_bounding_box);
vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size;
triangle_indices_size = rhs.triangle_indices_size;
quad_indices_size = rhs.quad_indices_size;
this->vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size;
this->triangle_indices_size = rhs.triangle_indices_size;
this->quad_indices_size = rhs.quad_indices_size;
return *this;
}
// Vertices and their normals, interleaved to be used by void glInterleavedArrays(GL_N3F_V3F, 0, x)
mutable std::vector<float> vertices_and_normals_interleaved;
mutable std::vector<int> triangle_indices;
mutable std::vector<int> quad_indices;
std::vector<float> vertices_and_normals_interleaved;
std::vector<int> triangle_indices;
std::vector<int> quad_indices;
// When the geometry data is loaded into the graphics card as Vertex Buffer Objects,
// the above mentioned std::vectors are cleared and the following variables keep their original length.
@ -119,9 +125,9 @@ public:
// IDs of the Vertex Array Objects, into which the geometry has been loaded.
// Zero if the VBOs are not sent to GPU yet.
mutable unsigned int vertices_and_normals_interleaved_VBO_id{ 0 };
mutable unsigned int triangle_indices_VBO_id{ 0 };
mutable unsigned int quad_indices_VBO_id{ 0 };
unsigned int vertices_and_normals_interleaved_VBO_id{ 0 };
unsigned int triangle_indices_VBO_id{ 0 };
unsigned int quad_indices_VBO_id{ 0 };
void load_mesh_full_shading(const TriangleMesh &mesh);
void load_mesh(const TriangleMesh& mesh) { this->load_mesh_full_shading(mesh); }
@ -141,12 +147,12 @@ public:
if (this->vertices_and_normals_interleaved.size() + 6 > this->vertices_and_normals_interleaved.capacity())
this->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(this->vertices_and_normals_interleaved.size() + 6));
this->vertices_and_normals_interleaved.push_back(nx);
this->vertices_and_normals_interleaved.push_back(ny);
this->vertices_and_normals_interleaved.push_back(nz);
this->vertices_and_normals_interleaved.push_back(x);
this->vertices_and_normals_interleaved.push_back(y);
this->vertices_and_normals_interleaved.push_back(z);
this->vertices_and_normals_interleaved.emplace_back(nx);
this->vertices_and_normals_interleaved.emplace_back(ny);
this->vertices_and_normals_interleaved.emplace_back(nz);
this->vertices_and_normals_interleaved.emplace_back(x);
this->vertices_and_normals_interleaved.emplace_back(y);
this->vertices_and_normals_interleaved.emplace_back(z);
this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size();
m_bounding_box.merge(Vec3f(x, y, z).cast<double>());
@ -167,9 +173,9 @@ public:
if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity())
this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3));
this->triangle_indices.push_back(idx1);
this->triangle_indices.push_back(idx2);
this->triangle_indices.push_back(idx3);
this->triangle_indices.emplace_back(idx1);
this->triangle_indices.emplace_back(idx2);
this->triangle_indices.emplace_back(idx3);
this->triangle_indices_size = this->triangle_indices.size();
};
@ -180,17 +186,17 @@ public:
if (this->quad_indices.size() + 4 > this->vertices_and_normals_interleaved.capacity())
this->quad_indices.reserve(next_highest_power_of_2(this->quad_indices.size() + 4));
this->quad_indices.push_back(idx1);
this->quad_indices.push_back(idx2);
this->quad_indices.push_back(idx3);
this->quad_indices.push_back(idx4);
this->quad_indices.emplace_back(idx1);
this->quad_indices.emplace_back(idx2);
this->quad_indices.emplace_back(idx3);
this->quad_indices.emplace_back(idx4);
this->quad_indices_size = this->quad_indices.size();
};
// 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.
void finalize_geometry() const;
void finalize_geometry(bool opengl_initialized);
// Release the geometry data, release OpenGL VBOs.
void release_geometry();
@ -211,7 +217,7 @@ public:
}
// Shrink the internal storage to tighly fit the data stored.
void shrink_to_fit() const {
void shrink_to_fit() {
this->vertices_and_normals_interleaved.shrink_to_fit();
this->triangle_indices.shrink_to_fit();
this->quad_indices.shrink_to_fit();
@ -219,6 +225,22 @@ public:
const BoundingBoxf3& bounding_box() const { return m_bounding_box; }
// Return an estimate of the memory consumed by this class.
size_t cpu_memory_used() const { return sizeof(*this) + vertices_and_normals_interleaved.capacity() * sizeof(float) + triangle_indices.capacity() * sizeof(int) + quad_indices.capacity() * sizeof(int); }
// Return an estimate of the memory held by GPU vertex buffers.
size_t gpu_memory_used() const
{
size_t memsize = 0;
if (this->vertices_and_normals_interleaved_VBO_id != 0)
memsize += this->vertices_and_normals_interleaved_size * 4;
if (this->triangle_indices_VBO_id != 0)
memsize += this->triangle_indices_size * 4;
if (this->quad_indices_VBO_id != 0)
memsize += this->quad_indices_size * 4;
return memsize;
}
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
private:
BoundingBoxf3 m_bounding_box;
};
@ -294,6 +316,8 @@ public:
bool selected;
// Is this object disabled from selection?
bool disabled;
// Is this object printable?
bool printable;
// Whether or not this volume is active for rendering
bool is_active;
// Whether or not to use this volume when applying zoom_to_volumes()
@ -420,13 +444,22 @@ public:
void render() const;
void render(int color_id, int detection_id, int worldmatrix_id) const;
void finalize_geometry() { this->indexed_vertex_array.finalize_geometry(); }
void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); }
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; }
bool is_sla_support() const;
bool is_sla_pad() const;
// Return an estimate of the memory consumed by this class.
size_t cpu_memory_used() const {
//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(); }
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
};
typedef std::vector<GLVolume*> GLVolumePtrs;
@ -464,14 +497,16 @@ public:
const ModelObject *model_object,
int obj_idx,
const std::vector<int> &instance_idxs,
const std::string& color_by);
const std::string &color_by,
bool opengl_initialized);
int load_object_volume(
const ModelObject *model_object,
int obj_idx,
int volume_idx,
int instance_idx,
const std::string& color_by);
const std::string &color_by,
bool opengl_initialized);
// Load SLA auxiliary GLVolumes (for support trees or pad).
void load_object_auxiliary(
@ -481,10 +516,11 @@ public:
const std::vector<std::pair<size_t, size_t>>& instances,
SLAPrintObjectStep milestone,
// Timestamp of the last change of the milestone
size_t timestamp);
size_t timestamp,
bool opengl_initialized);
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);
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);
// 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;
@ -492,7 +528,7 @@ public:
// 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.
void finalize_geometry() { for (auto* v : volumes) v->finalize_geometry(); }
void finalize_geometry(bool opengl_initialized) { for (auto* v : volumes) v->finalize_geometry(opengl_initialized); }
// 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(); }
@ -520,6 +556,14 @@ public:
// Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection
std::vector<double> get_current_print_zs(bool active_only) const;
// Return an estimate of the memory consumed by this class.
size_t cpu_memory_used() const;
// Return an estimate of the memory held by GPU vertex buffers.
size_t gpu_memory_used() const;
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
// Return CPU, GPU and total memory log line.
std::string log_memory_info() const;
private:
GLVolumeCollection(const GLVolumeCollection &other);
GLVolumeCollection& operator=(const GLVolumeCollection &);
@ -537,6 +581,7 @@ public:
GLModel();
virtual ~GLModel();
// init() / init_from_file() shall be called with the OpenGL context active!
bool init() { return on_init(); }
bool init_from_file(const std::string& filename) { return on_init_from_file(filename); }
@ -566,7 +611,7 @@ protected:
class GLArrow : public GLModel
{
protected:
virtual bool on_init();
bool on_init() override;
};
class GLCurvedArrow : public GLModel
@ -577,13 +622,13 @@ public:
explicit GLCurvedArrow(unsigned int resolution);
protected:
virtual bool on_init();
bool on_init() override;
};
class GLBed : public GLModel
{
protected:
virtual bool on_init_from_file(const std::string& filename);
bool on_init_from_file(const std::string& filename) override;
};
class _3DScene

View file

@ -212,7 +212,7 @@ wxPanel* BedShapePanel::init_texture_panel()
wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject());
if (lbl != nullptr)
{
wxString tooltip_text = (m_custom_texture == NONE) ? _(L("")) : _(m_custom_texture);
wxString tooltip_text = (m_custom_texture == NONE) ? "" : _(m_custom_texture);
wxToolTip* tooltip = lbl->GetToolTip();
if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text))
lbl->SetToolTip(tooltip_text);
@ -280,7 +280,7 @@ wxPanel* BedShapePanel::init_model_panel()
wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject());
if (lbl != nullptr)
{
wxString tooltip_text = (m_custom_model == NONE) ? _(L("")) : _(m_custom_model);
wxString tooltip_text = (m_custom_model == NONE) ? "" : _(m_custom_model);
wxToolTip* tooltip = lbl->GetToolTip();
if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text))
lbl->SetToolTip(tooltip_text);

View file

@ -22,10 +22,10 @@ namespace Slic3r {
namespace GUI {
const double Camera::DefaultDistance = 1000.0;
double Camera::FrustrumMinZSize = 50.0;
double Camera::FrustrumMinZRange = 50.0;
double Camera::FrustrumMinNearZ = 100.0;
double Camera::FrustrumZMargin = 10.0;
double Camera::FovMinDeg = 0.5;
double Camera::FovMaxDeg = 75.0;
double Camera::MaxFovDeg = 60.0;
Camera::Camera()
: phi(45.0f)
@ -186,7 +186,8 @@ void Camera::apply_view_matrix() const
void Camera::apply_projection(const BoundingBoxf3& box) const
{
m_distance = DefaultDistance;
set_distance(DefaultDistance);
double w = 0.0;
double h = 0.0;
@ -194,15 +195,14 @@ void Camera::apply_projection(const BoundingBoxf3& box) const
{
m_frustrum_zs = calc_tight_frustrum_zs_around(box);
w = (double)m_viewport[2];
h = (double)m_viewport[3];
w = 0.5 * (double)m_viewport[2];
h = 0.5 * (double)m_viewport[3];
double two_zoom = 2.0 * m_zoom;
if (two_zoom != 0.0)
if (m_zoom != 0.0)
{
double inv_two_zoom = 1.0 / two_zoom;
w *= inv_two_zoom;
h *= inv_two_zoom;
double inv_zoom = 1.0 / m_zoom;
w *= inv_zoom;
h *= inv_zoom;
}
switch (m_type)
@ -226,21 +226,16 @@ void Camera::apply_projection(const BoundingBoxf3& box) const
if (m_type == Perspective)
{
double fov_rad = 2.0 * std::atan(h / m_frustrum_zs.first);
double fov_deg = Geometry::rad2deg(fov_rad);
double fov_deg = Geometry::rad2deg(2.0 * std::atan(h / m_frustrum_zs.first));
// adjust camera distance to keep fov in a limited range
if (fov_deg > FovMaxDeg + 0.001)
if (fov_deg > MaxFovDeg)
{
double new_near_z = h / ::tan(0.5 * Geometry::deg2rad(FovMaxDeg));
m_distance += (new_near_z - m_frustrum_zs.first);
apply_view_matrix();
}
else if (fov_deg < FovMinDeg - 0.001)
{
double new_near_z = h / ::tan(0.5 * Geometry::deg2rad(FovMinDeg));
m_distance += (new_near_z - m_frustrum_zs.first);
apply_view_matrix();
double delta_z = h / ::tan(0.5 * Geometry::deg2rad(MaxFovDeg)) - m_frustrum_zs.first;
if (delta_z > 0.001)
set_distance(m_distance + delta_z);
else
break;
}
else
break;
@ -328,22 +323,23 @@ void Camera::debug_render() const
std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const
{
std::pair<double, double> ret = std::make_pair(DBL_MAX, -DBL_MAX);
std::pair<double, double> ret;
Vec3d bb_min = box.min;
Vec3d bb_max = box.max;
while (true)
{
ret = std::make_pair(DBL_MAX, -DBL_MAX);
// box vertices in world space
std::vector<Vec3d> vertices;
vertices.reserve(8);
vertices.push_back(bb_min);
vertices.emplace_back(bb_max(0), bb_min(1), bb_min(2));
vertices.emplace_back(bb_max(0), bb_max(1), bb_min(2));
vertices.emplace_back(bb_min(0), bb_max(1), bb_min(2));
vertices.emplace_back(bb_min(0), bb_min(1), bb_max(2));
vertices.emplace_back(bb_max(0), bb_min(1), bb_max(2));
vertices.push_back(bb_max);
vertices.emplace_back(bb_min(0), bb_max(1), bb_max(2));
vertices.push_back(box.min);
vertices.emplace_back(box.max(0), box.min(1), box.min(2));
vertices.emplace_back(box.max(0), box.max(1), box.min(2));
vertices.emplace_back(box.min(0), box.max(1), box.min(2));
vertices.emplace_back(box.min(0), box.min(1), box.max(2));
vertices.emplace_back(box.max(0), box.min(1), box.max(2));
vertices.push_back(box.max);
vertices.emplace_back(box.min(0), box.max(1), box.max(2));
// set the Z range in eye coordinates (negative Zs are in front of the camera)
for (const Vec3d& v : vertices)
@ -358,14 +354,21 @@ std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBo
ret.second += FrustrumZMargin;
// ensure min size
if (ret.second - ret.first < FrustrumMinZSize)
if (ret.second - ret.first < FrustrumMinZRange)
{
double mid_z = 0.5 * (ret.first + ret.second);
double half_size = 0.5 * FrustrumMinZSize;
double half_size = 0.5 * FrustrumMinZRange;
ret.first = mid_z - half_size;
ret.second = mid_z + half_size;
}
if (ret.first >= FrustrumMinNearZ)
break;
// ensure min Near Z
set_distance(m_distance + FrustrumMinNearZ - ret.first);
}
return ret;
}
@ -385,21 +388,19 @@ double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int ca
Vec3d up = get_dir_up();
Vec3d forward = get_dir_forward();
Vec3d bb_min = box.min;
Vec3d bb_max = box.max;
Vec3d bb_center = box.center();
// box vertices in world space
std::vector<Vec3d> vertices;
vertices.reserve(8);
vertices.push_back(bb_min);
vertices.emplace_back(bb_max(0), bb_min(1), bb_min(2));
vertices.emplace_back(bb_max(0), bb_max(1), bb_min(2));
vertices.emplace_back(bb_min(0), bb_max(1), bb_min(2));
vertices.emplace_back(bb_min(0), bb_min(1), bb_max(2));
vertices.emplace_back(bb_max(0), bb_min(1), bb_max(2));
vertices.push_back(bb_max);
vertices.emplace_back(bb_min(0), bb_max(1), bb_max(2));
vertices.push_back(box.min);
vertices.emplace_back(box.max(0), box.min(1), box.min(2));
vertices.emplace_back(box.max(0), box.max(1), box.min(2));
vertices.emplace_back(box.min(0), box.max(1), box.min(2));
vertices.emplace_back(box.min(0), box.min(1), box.max(2));
vertices.emplace_back(box.max(0), box.min(1), box.max(2));
vertices.push_back(box.max);
vertices.emplace_back(box.min(0), box.max(1), box.max(2));
double max_x = 0.0;
double max_y = 0.0;
@ -430,6 +431,12 @@ double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int ca
return std::min((double)canvas_w / (2.0 * max_x), (double)canvas_h / (2.0 * max_y));
}
void Camera::set_distance(double distance) const
{
m_distance = distance;
apply_view_matrix();
}
} // GUI
} // Slic3r

View file

@ -10,10 +10,10 @@ namespace GUI {
struct Camera
{
static const double DefaultDistance;
static double FrustrumMinZSize;
static double FrustrumMinZRange;
static double FrustrumMinNearZ;
static double FrustrumZMargin;
static double FovMinDeg;
static double FovMaxDeg;
static double MaxFovDeg;
enum EType : unsigned char
{
@ -101,6 +101,7 @@ private:
// the camera MUST be outside of the bounding box in eye coordinate of the given box
std::pair<double, double> calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const;
double calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h) const;
void set_distance(double distance) const;
};
} // GUI

View file

@ -393,6 +393,12 @@ void TextCtrl::set_value(const boost::any& value, bool change_event/* = false*/)
else
dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(value));
m_disable_change_event = false;
if (!change_event) {
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
// update m_value to correct work of next value_was_changed()
get_value_by_opt_type(ret_str);
}
}
void TextCtrl::set_last_meaningful_value()
@ -410,7 +416,7 @@ void TextCtrl::set_na_value()
boost::any& TextCtrl::get_value()
{
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
// modifies ret_string!
// update m_value
get_value_by_opt_type(ret_str);
return m_value;
@ -905,7 +911,7 @@ boost::any& Choice::get_value()
wxString ret_str = field->GetValue();
// options from right panel
std::vector <std::string> right_panel_options{ "support", "scale_unit" };
std::vector <std::string> right_panel_options{ "support", "pad", "scale_unit" };
for (auto rp_option: right_panel_options)
if (m_opt_id == rp_option)
return m_value = boost::any(ret_str);

View file

@ -1234,10 +1234,9 @@ bool GLCanvas3D::init()
return false;
}
// // 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()
// if (!m_volumes.empty())
// m_volumes.finalize_geometry();
// 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);
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;
@ -1320,6 +1319,26 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject
_set_warning_texture(WarningTexture::SomethingNotShown, false);
}
void GLCanvas3D::update_instance_printable_state_for_object(const size_t obj_idx)
{
ModelObject* model_object = m_model->objects[obj_idx];
for (int inst_idx = 0; inst_idx < model_object->instances.size(); inst_idx++)
{
ModelInstance* instance = model_object->instances[inst_idx];
for (GLVolume* volume : m_volumes.volumes)
{
if ((volume->object_idx() == obj_idx) && (volume->instance_idx() == inst_idx))
volume->printable = instance->printable;
}
}
}
void GLCanvas3D::update_instance_printable_state_for_objects(std::vector<size_t>& object_idxs)
{
for (size_t obj_idx : object_idxs)
update_instance_printable_state_for_object(obj_idx);
}
void GLCanvas3D::set_config(const DynamicPrintConfig* config)
{
@ -1529,7 +1548,7 @@ void GLCanvas3D::render()
}
m_camera.apply_view_matrix();
m_camera.apply_projection(_max_bounding_box(true));
m_camera.apply_projection(_max_bounding_box(true, true));
GLfloat position_cam[4] = { 1.0f, 0.0f, 1.0f, 0.0f };
glsafe(::glLightfv(GL_LIGHT1, GL_POSITION, position_cam));
@ -1691,7 +1710,7 @@ std::vector<int> GLCanvas3D::load_object(const ModelObject& model_object, int ob
instance_idxs.push_back(i);
}
}
return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by);
return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_initialized);
}
std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx)
@ -1879,7 +1898,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id);
if (it->new_geometry()) {
// New volume.
m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by);
m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_initialized);
m_volumes.volumes.back()->geometry_id = key.geometry_id;
update_object_list = true;
} else {
@ -1952,7 +1971,7 @@ 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())
m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp);
m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_initialized);
}
// Shift-up all volumes of the object so that it has the right elevation with respect to the print bed
@ -1992,7 +2011,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
depth = (900.f/w) * (float)(extruders_count - 1);
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_spacing * 4.5f);
brim_spacing * 4.5f, m_initialized);
if (volume_idx_wipe_tower_old != -1)
map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new;
}
@ -2110,8 +2129,7 @@ void GLCanvas3D::load_sla_preview()
if ((m_canvas != nullptr) && (print != nullptr))
{
_set_current();
// Reload the SLA support structures into GLVolumes.
this->reload_scene(true, true);
_load_sla_shells();
_update_sla_shells_outside_state();
_show_warning_texture_if_needed(WarningTexture::SlaSupportsOutside);
}
@ -2906,7 +2924,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
&& m_gizmos.get_current_type() != GLGizmosManager::SlaSupports) // disable context menu when the gizmo is open
{
// forces the selection of the volume
m_selection.add(volume_idx);
/* m_selection.add(volume_idx); // #et_FIXME_if_needed
* To avoid extra "Add-Selection" snapshots,
* call add() with check_for_already_contained=true
* */
m_selection.add(volume_idx, true, true);
m_gizmos.refresh_on_off_state();
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
m_gizmos.update_data();
@ -2929,7 +2951,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
else if (evt.Moving())
{
m_mouse.position = pos.cast<double>();
std::string tooltip = L("");
std::string tooltip = "";
if (tooltip.empty())
tooltip = m_gizmos.get_tooltip();
@ -3217,7 +3239,7 @@ void GLCanvas3D::do_flatten(const Vec3d& normal, const std::string& snapshot_typ
wxGetApp().plater()->take_snapshot(_(snapshot_type));
m_selection.flattening_rotate(normal);
do_rotate(L("")); // avoid taking another snapshot
do_rotate(""); // avoid taking another snapshot
}
void GLCanvas3D::do_mirror(const std::string& snapshot_type)
@ -3273,7 +3295,7 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
void GLCanvas3D::set_camera_zoom(double zoom)
{
const Size& cnv_size = get_canvas_size();
m_camera.set_zoom(zoom, _max_bounding_box(false), cnv_size.get_width(), cnv_size.get_height());
m_camera.set_zoom(zoom, _max_bounding_box(false, false), cnv_size.get_width(), cnv_size.get_height());
m_dirty = true;
}
@ -3620,14 +3642,14 @@ bool GLCanvas3D::_init_undoredo_toolbar()
std::string curr_additional_tooltip;
m_undoredo_toolbar.get_additional_tooltip(id, curr_additional_tooltip);
std::string new_additional_tooltip = L("");
std::string new_additional_tooltip = "";
if (can_undo)
wxGetApp().plater()->undo_redo_topmost_string_getter(true, new_additional_tooltip);
if (new_additional_tooltip != curr_additional_tooltip)
{
m_undoredo_toolbar.set_additional_tooltip(id, new_additional_tooltip);
set_tooltip(L(""));
set_tooltip("");
}
return can_undo;
};
@ -3649,14 +3671,14 @@ bool GLCanvas3D::_init_undoredo_toolbar()
std::string curr_additional_tooltip;
m_undoredo_toolbar.get_additional_tooltip(id, curr_additional_tooltip);
std::string new_additional_tooltip = L("");
std::string new_additional_tooltip = "";
if (can_redo)
wxGetApp().plater()->undo_redo_topmost_string_getter(false, new_additional_tooltip);
if (new_additional_tooltip != curr_additional_tooltip)
{
m_undoredo_toolbar.set_additional_tooltip(id, new_additional_tooltip);
set_tooltip(L(""));
set_tooltip("");
}
return can_redo;
};
@ -3699,9 +3721,20 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
m_dirty = false;
}
BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_bed_model) const
BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_bed_model) const
{
BoundingBoxf3 bb = volumes_bounding_box();
// The following is a workaround for gizmos not being taken in account when calculating the tight camera frustrum
// A better solution would ask the gizmo manager for the bounding box of the current active gizmo, if any
if (include_gizmos && m_gizmos.is_running())
{
BoundingBoxf3 sel_bb = m_selection.get_bounding_box();
Vec3d sel_bb_center = sel_bb.center();
Vec3d extend_by = sel_bb.max_size() * Vec3d::Ones();
bb.merge(BoundingBoxf3(sel_bb_center - extend_by, sel_bb_center + extend_by));
}
bb.merge(m_bed.get_bounding_box(include_bed_model));
return bb;
}
@ -3737,6 +3770,7 @@ void GLCanvas3D::_picking_pass() const
// Better to use software ray - casting on a bounding - box hierarchy.
if (m_multisample_allowed)
// This flag is often ignored by NVIDIA drivers if rendering into a screen buffer.
glsafe(::glDisable(GL_MULTISAMPLE));
glsafe(::glDisable(GL_BLEND));
@ -3766,6 +3800,8 @@ void GLCanvas3D::_picking_pass() const
if (inside)
{
glsafe(::glReadPixels(m_mouse.position(0), cnv_size.get_height() - m_mouse.position(1) - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color));
if (picking_checksum_alpha_channel(color[0], color[1], color[2]) == color[3])
// Only non-interpolated colors are valid, those have their lowest three bits zeroed.
volume_id = color[0] + (color[1] << 8) + (color[2] << 16);
}
if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size()))
@ -3789,6 +3825,7 @@ void GLCanvas3D::_rectangular_selection_picking_pass() const
if (m_picking_enabled)
{
if (m_multisample_allowed)
// This flag is often ignored by NVIDIA drivers if rendering into a screen buffer.
glsafe(::glDisable(GL_MULTISAMPLE));
glsafe(::glDisable(GL_BLEND));
@ -3814,6 +3851,8 @@ void GLCanvas3D::_rectangular_selection_picking_pass() const
struct Pixel
{
std::array<GLubyte, 4> data;
// Only non-interpolated colors are valid, those have their lowest three bits zeroed.
bool valid() const { return picking_checksum_alpha_channel(data[0], data[1], data[2]) == data[3]; }
int id() const { return data[0] + (data[1] << 8) + (data[2] << 16); }
};
@ -3824,17 +3863,15 @@ void GLCanvas3D::_rectangular_selection_picking_pass() const
tbb::parallel_for(tbb::blocked_range<size_t>(0, frame.size(), (size_t)width),
[this, &frame, &idxs, &mutex](const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i < range.end(); ++i)
{
if (frame[i].valid()) {
int volume_id = frame[i].id();
if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size()))
{
if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size())) {
mutex.lock();
idxs.insert(volume_id);
mutex.unlock();
}
}
}
);
});
#else
std::vector<GLubyte> frame(4 * px_count);
glsafe(::glReadPixels(left, top, width, height, GL_RGBA, GL_UNSIGNED_BYTE, (void*)frame.data()));
@ -4013,42 +4050,27 @@ void GLCanvas3D::_render_volumes_for_picking() const
// do not cull backfaces to show broken geometry, if any
glsafe(::glDisable(GL_CULL_FACE));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
const Transform3d& view_matrix = m_camera.get_view_matrix();
GLVolumeWithIdAndZList to_render = volumes_to_render(m_volumes.volumes, GLVolumeCollection::Opaque, view_matrix);
for (size_t type = 0; type < 2; ++ type) {
GLVolumeWithIdAndZList to_render = volumes_to_render(m_volumes.volumes, (type == 0) ? GLVolumeCollection::Opaque : GLVolumeCollection::Transparent, view_matrix);
for (const GLVolumeWithIdAndZ& volume : to_render)
{
if (!volume.first->disabled && ((volume.first->composite_id.volume_id >= 0) || m_render_sla_auxiliaries)) {
// Object picking mode. Render the object with a color encoding the object index.
unsigned int r = (volume.second.first & 0x000000FF) >> 0;
unsigned int g = (volume.second.first & 0x0000FF00) >> 8;
unsigned int b = (volume.second.first & 0x00FF0000) >> 16;
glsafe(::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255));
if (!volume.first->disabled && ((volume.first->composite_id.volume_id >= 0) || m_render_sla_auxiliaries))
unsigned int id = volume.second.first;
unsigned int r = (id & (0x000000FF << 0)) << 0;
unsigned int g = (id & (0x000000FF << 8)) >> 8;
unsigned int b = (id & (0x000000FF << 16)) >> 16;
unsigned int a = picking_checksum_alpha_channel(r, g, b);
glsafe(::glColor4f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255, (GLfloat)a * INV_255));
volume.first->render();
}
to_render = volumes_to_render(m_volumes.volumes, GLVolumeCollection::Transparent, view_matrix);
for (const GLVolumeWithIdAndZ& volume : to_render)
{
// Object picking mode. Render the object with a color encoding the object index.
unsigned int r = (volume.second.first & 0x000000FF) >> 0;
unsigned int g = (volume.second.first & 0x0000FF00) >> 8;
unsigned int b = (volume.second.first & 0x00FF0000) >> 16;
glsafe(::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255));
if (!volume.first->disabled && ((volume.first->composite_id.volume_id >= 0) || m_render_sla_auxiliaries))
volume.first->render();
}
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisable(GL_BLEND));
glsafe(::glEnable(GL_CULL_FACE));
}
@ -4511,6 +4533,7 @@ void GLCanvas3D::_load_print_toolpaths()
_3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), volume);
}
volume.indexed_vertex_array.finalize_geometry(m_initialized);
}
void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, const std::vector<double>& color_print_values)
@ -4576,7 +4599,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; });
// Maximum size of an allocation block: 32MB / sizeof(float)
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start";
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start" << m_volumes.log_memory_info() << log_memory_info();
//FIXME Improve the heuristics for a grain size.
size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1));
@ -4682,16 +4705,22 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
}
}
}
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();
});
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results";
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results" << m_volumes.log_memory_info() << log_memory_info();
// Remove empty volumes from the newly added volumes.
m_volumes.volumes.erase(
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
[](const GLVolume *volume) { return volume->empty(); }),
m_volumes.volumes.end());
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized);
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end";
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info();
}
void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors)
@ -4748,7 +4777,7 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
ctxt.wipe_tower_angle = ctxt.print->config().wipe_tower_rotation_angle.value/180.f * PI;
ctxt.wipe_tower_pos = Vec2f(ctxt.print->config().wipe_tower_x.value, ctxt.print->config().wipe_tower_y.value);
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start" << m_volumes.log_memory_info() << log_memory_info();
//FIXME Improve the heuristics for a grain size.
size_t n_items = print->wipe_tower_data().tool_changes.size() + (ctxt.priming.empty() ? 0 : 1);
@ -4846,16 +4875,20 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
}
}
for (GLVolume *vol : vols)
vol->indexed_vertex_array.shrink_to_fit();
});
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results";
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.
m_volumes.volumes.erase(
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
[](const GLVolume *volume) { return volume->empty(); }),
m_volumes.volumes.end());
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized);
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end";
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info();
}
static inline int hex_digit_to_int(const char c)
@ -4868,6 +4901,8 @@ static inline int hex_digit_to_int(const char c)
void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
{
BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - start" << m_volumes.log_memory_info() << log_memory_info();
// helper functions to select data in dependence of the extrusion view type
struct Helper
{
@ -4983,6 +5018,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
if (filters.empty())
return;
BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - create volumes" << m_volumes.log_memory_info() << log_memory_info();
// creates a new volume for each filter
for (Filter& filter : filters)
{
@ -5013,6 +5050,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
}
}
BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - populate volumes" << m_volumes.log_memory_info() << log_memory_info();
// populates volumes
for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
{
@ -5030,6 +5069,12 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
}
}
}
// finalize volumes and sends geometry to gpu
for (size_t i = initial_volumes_count; i < m_volumes.volumes.size(); ++i)
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized);
BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - end" << m_volumes.log_memory_info() << log_memory_info();
}
void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
@ -5074,6 +5119,10 @@ void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data,
return;
}
// finalize volumes and sends geometry to gpu
for (size_t i = initial_volumes_count; i < m_volumes.volumes.size(); ++i)
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized);
}
bool GLCanvas3D::_travel_paths_by_type(const GCodePreviewData& preview_data)
@ -5302,6 +5351,7 @@ void GLCanvas3D::_load_gcode_retractions(const GCodePreviewData& preview_data)
_3DScene::point3_to_verts(position.position, position.width, position.height, *volume);
}
volume->indexed_vertex_array.finalize_geometry(m_initialized);
}
}
@ -5329,6 +5379,7 @@ void GLCanvas3D::_load_gcode_unretractions(const GCodePreviewData& preview_data)
_3DScene::point3_to_verts(position.position, position.width, position.height, *volume);
}
volume->indexed_vertex_array.finalize_geometry(m_initialized);
}
}
@ -5354,7 +5405,7 @@ void GLCanvas3D::_load_fff_shells()
instance_ids[i] = i;
}
m_volumes.load_object(model_obj, object_id, instance_ids, "object");
m_volumes.load_object(model_obj, object_id, instance_ids, "object", m_initialized);
++object_id;
}
@ -5376,11 +5427,63 @@ void GLCanvas3D::_load_fff_shells()
if (!print->is_step_done(psWipeTower))
depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1);
m_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_spacing * 4.5f);
!print->is_step_done(psWipeTower), brim_spacing * 4.5f, m_initialized);
}
}
}
// While it looks like we can call
// this->reload_scene(true, true)
// the two functions are quite different:
// 1) This function only loads objects, for which the step slaposSliceSupports already finished. Therefore objects outside of the print bed never load.
// 2) This function loads object mesh with the relative scaling correction (the "relative_correction" parameter) was applied,
// therefore the mesh may be slightly larger or smaller than the mesh shown in the 3D scene.
void GLCanvas3D::_load_sla_shells()
{
const SLAPrint* print = this->sla_print();
if (print->objects().empty())
// nothing to render, return
return;
auto add_volume = [this](const SLAPrintObject &object, int volume_id, const SLAPrintObject::Instance& instance,
const TriangleMesh &mesh, const float color[4], bool outside_printer_detection_enabled) {
m_volumes.volumes.emplace_back(new GLVolume(color));
GLVolume& v = *m_volumes.volumes.back();
v.indexed_vertex_array.load_mesh(mesh);
v.indexed_vertex_array.finalize_geometry(this->m_initialized);
v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled;
v.composite_id.volume_id = volume_id;
v.set_instance_offset(unscale(instance.shift(0), instance.shift(1), 0));
v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation));
v.set_instance_mirror(X, object.is_left_handed() ? -1. : 1.);
v.set_convex_hull(mesh.convex_hull_3d());
};
// adds objects' volumes
for (const SLAPrintObject* obj : print->objects())
if (obj->is_step_done(slaposSliceSupports)) {
unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size();
for (const SLAPrintObject::Instance& instance : obj->instances()) {
add_volume(*obj, 0, instance, obj->transformed_mesh(), GLVolume::MODEL_COLOR[0], true);
// Set the extruder_id and volume_id to achieve the same color as in the 3D scene when
// through the update_volumes_colors_by_extruder() call.
m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id();
if (obj->is_step_done(slaposSupportTree) && obj->has_mesh(slaposSupportTree))
add_volume(*obj, -int(slaposSupportTree), instance, obj->support_mesh(), GLVolume::SLA_SUPPORT_COLOR, true);
if (obj->is_step_done(slaposBasePool) && obj->has_mesh(slaposBasePool))
add_volume(*obj, -int(slaposBasePool), instance, obj->pad_mesh(), GLVolume::SLA_PAD_COLOR, false);
}
double shift_z = obj->get_current_elevation();
for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) {
GLVolume& v = *m_volumes.volumes[i];
// apply shift z
v.set_sla_shift_z(shift_z);
}
}
update_volumes_colors_by_extruder();
}
void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data)
{
unsigned int size = (unsigned int)m_gcode_preview_volume_index.first_volumes.size();

View file

@ -482,6 +482,8 @@ public:
void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1);
void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1);
void update_instance_printable_state_for_object(size_t obj_idx);
void update_instance_printable_state_for_objects(std::vector<size_t>& object_idxs);
void set_config(const DynamicPrintConfig* config);
void set_process(BackgroundSlicingProcess* process);
@ -652,7 +654,7 @@ private:
bool _set_current();
void _resize(unsigned int w, unsigned int h);
BoundingBoxf3 _max_bounding_box(bool include_bed_model) const;
BoundingBoxf3 _max_bounding_box(bool include_gizmos, bool include_bed_model) const;
void _zoom_to_box(const BoundingBoxf3& box);
@ -721,6 +723,8 @@ private:
void _load_gcode_unretractions(const GCodePreviewData& preview_data);
// generates objects and wipe tower geometry
void _load_fff_shells();
// Load SLA objects and support structures for objects, for which the slaposSliceSupports step has been finished.
void _load_sla_shells();
// sets gcode geometry visibility according to user selection
void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data);
void _update_toolpath_volumes_outside_state();

View file

@ -290,7 +290,21 @@ GLCanvas3D* GLCanvas3DManager::get_canvas(wxGLCanvas* canvas)
wxGLCanvas* GLCanvas3DManager::create_wxglcanvas(wxWindow *parent)
{
int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24, WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0 };
int attribList[] = {
WX_GL_RGBA,
WX_GL_DOUBLEBUFFER,
// RGB channels each should be allocated with 8 bit depth. One should almost certainly get these bit depths by default.
WX_GL_MIN_RED, 8,
WX_GL_MIN_GREEN, 8,
WX_GL_MIN_BLUE, 8,
// Requesting an 8 bit alpha channel. Interestingly, the NVIDIA drivers would most likely work with some alpha plane, but glReadPixels would not return
// the alpha channel on NVIDIA if not requested when the GL context is created.
WX_GL_MIN_ALPHA, 8,
WX_GL_DEPTH_SIZE, 24,
WX_GL_SAMPLE_BUFFERS, GL_TRUE,
WX_GL_SAMPLES, 4,
0
};
if (s_multisample == MS_Unknown)
{
@ -300,7 +314,7 @@ wxGLCanvas* GLCanvas3DManager::create_wxglcanvas(wxWindow *parent)
}
if (! can_multisample())
attribList[4] = 0;
attribList[12] = 0;
return new wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS);
}

View file

@ -27,49 +27,57 @@ namespace GUI {
void GLTexture::Compressor::reset()
{
if (m_is_compressing)
{
// force compression completion, if any
if (m_thread.joinable()) {
m_abort_compressing = true;
// wait for compression completion, if any
while (m_is_compressing) {}
}
m_thread.join();
m_levels.clear();
m_num_levels_compressed = 0;
m_abort_compressing = false;
}
void GLTexture::Compressor::add_level(unsigned int w, unsigned int h, const std::vector<unsigned char>& data)
{
m_levels.emplace_back(w, h, data);
assert(m_levels.empty());
assert(m_abort_compressing == false);
assert(m_num_levels_compressed == 0);
}
void GLTexture::Compressor::start_compressing()
{
std::thread t(&GLTexture::Compressor::compress, this);
t.detach();
// The worker thread should be stopped already.
assert(! m_thread.joinable());
assert(! m_levels.empty());
assert(m_abort_compressing == false);
assert(m_num_levels_compressed == 0);
if (! m_levels.empty()) {
std::thread thrd(&GLTexture::Compressor::compress, this);
m_thread = std::move(thrd);
}
}
bool GLTexture::Compressor::unsent_compressed_data_available() const
{
for (const Level& level : m_levels)
{
if (!level.sent_to_gpu && level.compressed)
if (m_levels.empty())
return false;
// Querying the atomic m_num_levels_compressed value synchronizes processor caches, so that the dat of m_levels modified by the worker thread are accessible to the calling thread.
unsigned int num_compressed = m_num_levels_compressed;
for (unsigned int i = 0; i < num_compressed; ++ i)
if (! m_levels[i].sent_to_gpu && ! m_levels[i].compressed_data.empty())
return true;
}
return false;
}
void GLTexture::Compressor::send_compressed_data_to_gpu()
{
// this method should be called inside the main thread of Slicer or a new OpenGL context (sharing resources) would be needed
if (m_levels.empty())
return;
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_texture.m_id));
for (int i = 0; i < (int)m_levels.size(); ++i)
// Querying the atomic m_num_levels_compressed value synchronizes processor caches, so that the dat of m_levels modified by the worker thread are accessible to the calling thread.
int num_compressed = (int)m_num_levels_compressed;
for (int i = 0; i < num_compressed; ++ i)
{
Level& level = m_levels[i];
if (!level.sent_to_gpu && level.compressed)
if (! level.sent_to_gpu && ! level.compressed_data.empty())
{
glsafe(::glCompressedTexSubImage2D(GL_TEXTURE_2D, (GLint)i, 0, 0, (GLsizei)level.w, (GLsizei)level.h, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)level.compressed_data.size(), (const GLvoid*)level.compressed_data.data()));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i));
@ -77,29 +85,21 @@ void GLTexture::Compressor::send_compressed_data_to_gpu()
level.sent_to_gpu = true;
// we are done with the compressed data, we can discard it
level.compressed_data.clear();
level.compressed = false;
}
}
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
}
bool GLTexture::Compressor::all_compressed_data_sent_to_gpu() const
{
for (const Level& level : m_levels)
{
if (!level.sent_to_gpu)
return false;
}
return true;
if (num_compressed == (unsigned int)m_levels.size())
// Finalize the worker thread, close it.
this->reset();
}
void GLTexture::Compressor::compress()
{
// reference: https://github.com/Cyan4973/RygsDXTc
m_is_compressing = true;
m_abort_compressing = false;
assert(m_num_levels_compressed == 0);
assert(m_abort_compressing == false);
for (Level& level : m_levels)
{
@ -115,11 +115,8 @@ void GLTexture::Compressor::compress()
// we are done with the source data, we can discard it
level.src_data.clear();
level.compressed = true;
++ m_num_levels_compressed;
}
m_is_compressing = false;
m_abort_compressing = false;
}
GLTexture::Quad_UVs GLTexture::FullTextureUVs = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f } };

View file

@ -1,8 +1,10 @@
#ifndef slic3r_GLTexture_hpp_
#define slic3r_GLTexture_hpp_
#include <atomic>
#include <string>
#include <vector>
#include <thread>
class wxImage;
@ -19,30 +21,34 @@ namespace GUI {
unsigned int h;
std::vector<unsigned char> src_data;
std::vector<unsigned char> compressed_data;
bool compressed;
bool sent_to_gpu;
Level(unsigned int w, unsigned int h, const std::vector<unsigned char>& data) : w(w), h(h), src_data(data), compressed(false), sent_to_gpu(false) {}
Level(unsigned int w, unsigned int h, const std::vector<unsigned char>& data) : w(w), h(h), src_data(data), sent_to_gpu(false) {}
};
GLTexture& m_texture;
std::vector<Level> m_levels;
bool m_is_compressing;
bool m_abort_compressing;
std::thread m_thread;
// Does the caller want the background thread to stop?
// This atomic also works as a memory barrier for synchronizing the cancel event with the worker thread.
std::atomic<bool> m_abort_compressing;
// How many levels were compressed since the start of the background processing thread?
// This atomic also works as a memory barrier for synchronizing results of the worker thread with the calling thread.
std::atomic<unsigned int> m_num_levels_compressed;
public:
explicit Compressor(GLTexture& texture) : m_texture(texture), m_is_compressing(false), m_abort_compressing(false) {}
explicit Compressor(GLTexture& texture) : m_texture(texture), m_abort_compressing(false), m_num_levels_compressed(0) {}
~Compressor() { reset(); }
void reset();
void add_level(unsigned int w, unsigned int h, const std::vector<unsigned char>& data);
void add_level(unsigned int w, unsigned int h, const std::vector<unsigned char>& data) { m_levels.emplace_back(w, h, data); }
void start_compressing();
bool unsent_compressed_data_available() const;
void send_compressed_data_to_gpu();
bool all_compressed_data_sent_to_gpu() const;
bool all_compressed_data_sent_to_gpu() const { return m_levels.empty(); }
private:
void compress();

View file

@ -173,7 +173,8 @@ bool GUI_App::on_init_inner()
wxCHECK_MSG(wxDirExists(resources_dir), false,
wxString::Format("Resources path does not exist or is not a directory: %s", resources_dir));
SetAppName(SLIC3R_APP_KEY);
//SetAppName(SLIC3R_APP_KEY);
SetAppName(SLIC3R_APP_KEY "-alpha");
SetAppDisplayName(SLIC3R_APP_NAME);
// Enable this to get the default Win32 COMCTRL32 behavior of static boxes.
@ -986,6 +987,7 @@ wxString GUI_App::current_language_code_safe() const
language_code = language_code.substr(0, idx_underscore);
const std::map<wxString, wxString> mapping {
{ "cs", "cs_CZ", },
{ "sk", "cs_CZ", },
{ "de", "de_DE", },
{ "es", "es_ES", },
{ "fr", "fr_FR", },

View file

@ -186,7 +186,18 @@ ObjectList::ObjectList(wxWindow* parent) :
Bind(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, [this](wxCommandEvent& e) { last_volume_is_deleted(e.GetInt()); });
Bind(wxEVT_SIZE, ([this](wxSizeEvent &e) { this->EnsureVisible(this->GetCurrentItem()); e.Skip(); }));
Bind(wxEVT_SIZE, ([this](wxSizeEvent &e) {
#ifdef __WXGTK__
// On GTK, the EnsureVisible call is postponed to Idle processing (see wxDataViewCtrl::m_ensureVisibleDefered).
// So the postponed EnsureVisible() call is planned for an item, which may not exist at the Idle processing time, if this wxEVT_SIZE
// event is succeeded by a delete of the currently active item. We are trying our luck by postponing the wxEVT_SIZE triggered EnsureVisible(),
// which seems to be working as of now.
this->CallAfter([this](){ this->EnsureVisible(this->GetCurrentItem()); });
#else
this->EnsureVisible(this->GetCurrentItem());
#endif
e.Skip();
}));
}
ObjectList::~ObjectList()
@ -212,16 +223,20 @@ void ObjectList::create_objects_ctrl()
EnableDropTarget(wxDF_UNICODETEXT);
#endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE
// column 0(Icon+Text) of the view control:
// column ItemName(Icon+Text) of the view control:
// And Icon can be consisting of several bitmaps
AppendColumn(new wxDataViewColumn(_(L("Name")), new BitmapTextRenderer(),
0, 20*wxGetApp().em_unit()/*200*/, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE));
colName, 20*wxGetApp().em_unit()/*200*/, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE));
// column 1 of the view control:
// column PrintableProperty (Icon) of the view control:
AppendBitmapColumn(" ", colPrint, wxDATAVIEW_CELL_INERT, int(2 * wxGetApp().em_unit()),
wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
// column Extruder of the view control:
AppendColumn(create_objects_list_extruder_column(4));
// column 2 of the view control:
AppendBitmapColumn(" ", 2, wxDATAVIEW_CELL_INERT, int(2.5 * wxGetApp().em_unit())/*25*/,
// column ItemEditing of the view control:
AppendBitmapColumn("Editing", colEditing, wxDATAVIEW_CELL_INERT, int(2.5 * wxGetApp().em_unit())/*25*/,
wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
}
@ -321,7 +336,7 @@ void ObjectList::set_tooltip_for_item(const wxPoint& pt)
return;
}
if (col->GetTitle() == " " && GetSelectedItemsCount()<2)
if (col->GetTitle() == _(L("Editing")) && GetSelectedItemsCount()<2)
GetMainWindow()->SetToolTip(_(L("Right button click the icon to change the object settings")));
else if (col->GetTitle() == _("Name"))
{
@ -377,7 +392,7 @@ wxDataViewColumn* ObjectList::create_objects_list_extruder_column(int extruders_
choices.Add(wxString::Format("%d", i));
wxDataViewChoiceRenderer *c =
new wxDataViewChoiceRenderer(choices, wxDATAVIEW_CELL_EDITABLE, wxALIGN_CENTER_HORIZONTAL);
wxDataViewColumn* column = new wxDataViewColumn(_(L("Extruder")), c, 1,
wxDataViewColumn* column = new wxDataViewColumn(_(L("Extruder")), c, colExtruder,
8*wxGetApp().em_unit()/*80*/, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
return column;
}
@ -397,7 +412,7 @@ void ObjectList::update_extruder_values_for_items(const int max_extruder)
else
extruder = wxString::Format("%d", object->config.option<ConfigOptionInt>("extruder")->value);
m_objects_model->SetValue(extruder, item, 1);
m_objects_model->SetValue(extruder, item, colExtruder);
if (object->volumes.size() > 1) {
for (auto id = 0; id < object->volumes.size(); id++) {
@ -409,7 +424,7 @@ void ObjectList::update_extruder_values_for_items(const int max_extruder)
else
extruder = wxString::Format("%d", object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value);
m_objects_model->SetValue(extruder, item, 1);
m_objects_model->SetValue(extruder, item, colExtruder);
}
}
}
@ -421,7 +436,7 @@ void ObjectList::update_objects_list_extruder_column(int extruders_count)
if (printer_technology() == ptSLA)
extruders_count = 1;
wxDataViewChoiceRenderer* ch_render = dynamic_cast<wxDataViewChoiceRenderer*>(GetColumn(1)->GetRenderer());
wxDataViewChoiceRenderer* ch_render = dynamic_cast<wxDataViewChoiceRenderer*>(GetColumn(colExtruder)->GetRenderer());
if (ch_render->GetChoices().GetCount() - 1 == extruders_count)
return;
@ -430,21 +445,21 @@ void ObjectList::update_objects_list_extruder_column(int extruders_count)
if (m_objects && extruders_count > 1)
update_extruder_values_for_items(extruders_count);
// delete old 2nd column
DeleteColumn(GetColumn(1));
// insert new created 3rd column
InsertColumn(1, create_objects_list_extruder_column(extruders_count));
// delete old extruder column
DeleteColumn(GetColumn(colExtruder));
// insert new created extruder column
InsertColumn(colExtruder, create_objects_list_extruder_column(extruders_count));
// set show/hide for this column
set_extruder_column_hidden(extruders_count <= 1);
//a workaround for a wrong last column width updating under OSX
GetColumn(2)->SetWidth(25);
GetColumn(colEditing)->SetWidth(25);
m_prevent_update_extruder_in_config = false;
}
void ObjectList::set_extruder_column_hidden(const bool hide) const
{
GetColumn(1)->SetHidden(hide);
GetColumn(colExtruder)->SetHidden(hide);
}
void ObjectList::update_extruder_in_config(const wxDataViewItem& item)
@ -471,7 +486,7 @@ void ObjectList::update_extruder_in_config(const wxDataViewItem& item)
}
wxVariant variant;
m_objects_model->GetValue(variant, item, 1);
m_objects_model->GetValue(variant, item, colExtruder);
const wxString selection = variant.GetString();
if (!m_config || selection.empty())
@ -734,21 +749,21 @@ void ObjectList::OnContextMenu(wxDataViewEvent&)
wxDataViewColumn* col;
const wxPoint pt = get_mouse_position_in_control();
HitTest(pt, item, col);
if (!item)
#ifdef __WXOSX__ // temporary workaround for OSX
// after Yosemite OS X version, HitTest return undefined item
item = GetSelection();
if (item)
show_context_menu();
else
if (!item) item = GetSelection();
#endif // __WXOSX__
if (!item) {
printf("undefined item\n");
return;
#else
return;
#endif // __WXOSX__
}
const wxString title = col->GetTitle();
if (title == " ")
toggle_printable_state(item);
else if (title == _("Editing"))
show_context_menu();
else if (title == _("Name"))
{
@ -1013,6 +1028,11 @@ const std::vector<std::string>& ObjectList::get_options_for_bundle(const wxStrin
return empty;
}
static bool improper_category(const std::string& category, const int extruders_cnt)
{
return category.empty() || (extruders_cnt == 1 && (category == "Extruders" || category == "Wipe options" ));
}
void ObjectList::get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part)
{
auto options = get_options(is_part);
@ -1024,8 +1044,8 @@ void ObjectList::get_options_menu(settings_menu_hierarchy& settings_menu, const
{
auto const opt = config.def()->get(option);
auto category = opt->category;
if (category.empty() ||
(category == "Extruders" && extruders_cnt == 1)) continue;
if (improper_category(category, extruders_cnt))
continue;
const std::string& label = !opt->full_label.empty() ? opt->full_label : opt->label;
std::pair<std::string, std::string> option_label(option, label);
@ -1370,6 +1390,13 @@ wxMenuItem* ObjectList::append_menu_item_instance_to_object(wxMenu* menu, wxWind
[this](wxCommandEvent&) { split_instances(); }, "", menu, [](){return wxGetApp().plater()->can_set_instance_to_object(); }, parent);
}
wxMenuItem* ObjectList::append_menu_item_printable(wxMenu* menu, wxWindow* parent)
{
return append_menu_check_item(menu, wxID_ANY, _(L("Printable")), "", [this](wxCommandEvent&) {
wxGetApp().plater()->canvas3D()->get_selection().toggle_instance_printable_state();
}, menu);
}
void ObjectList::append_menu_items_osx(wxMenu* menu)
{
append_menu_item(menu, wxID_ANY, _(L("Rename")), "",
@ -1537,7 +1564,7 @@ void ObjectList::create_freq_settings_popupmenu(wxMenu *menu)
const int extruders_cnt = extruders_count();
for (auto& it : bundle) {
if (it.first.empty() || it.first == "Extruders" && extruders_cnt == 1)
if (improper_category(it.first, extruders_cnt))
continue;
append_menu_item(menu, wxID_ANY, _(it.first), "",
@ -1550,7 +1577,7 @@ void ObjectList::create_freq_settings_popupmenu(wxMenu *menu)
m_freq_settings_fff : m_freq_settings_sla;
for (auto& it : bundle_quick) {
if (it.first.empty() || it.first == "Extruders" && extruders_cnt == 1)
if (improper_category(it.first, extruders_cnt))
continue;
append_menu_item(menu, wxID_ANY, wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)), "",
@ -1589,6 +1616,9 @@ void ObjectList::load_subobject(ModelVolumeType type)
changed_object(obj_idx);
if (type == ModelVolumeType::MODEL_PART)
// update printable state on canvas
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx);
wxDataViewItem sel_item;
for (const auto& volume : volumes_info )
@ -1712,6 +1742,9 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
changed_object(obj_idx);
if (type == ModelVolumeType::MODEL_PART)
// update printable state on canvas
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx);
const auto object_item = m_objects_model->GetTopParent(GetSelection());
select_item(m_objects_model->AddVolumeChild(object_item, name, type,
@ -1755,7 +1788,19 @@ void ObjectList::del_subobject_item(wxDataViewItem& item)
if (type & itVolume && (*m_objects)[obj_idx]->get_mesh_errors_count() == 0)
m_objects_model->DeleteWarningIcon(m_objects_model->GetParent(item));
// If last two Instances of object is selected, show the message about impossible action
bool show_msg = false;
if (type & itInstance) {
wxDataViewItemArray instances;
m_objects_model->GetChildren(m_objects_model->GetParent(item), instances);
if (instances.Count() == 2 && IsSelected(instances[0]) && IsSelected(instances[1]))
show_msg = true;
}
m_objects_model->Delete(item);
if (show_msg)
Slic3r::GUI::show_error(nullptr, _(L("Last instance of an object cannot be deleted.")));
}
void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item)
@ -1838,7 +1883,7 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
if (vol->is_model_part())
++solid_cnt;
if (volume->is_model_part() && solid_cnt == 1) {
Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last solid part from object.")));
Slic3r::GUI::show_error(nullptr, _(L("From Object List You can't delete the last solid part from object.")));
return false;
}
@ -1857,7 +1902,7 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
}
else if (type == itInstance) {
if (object->instances.size() == 1) {
Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last intance from object.")));
Slic3r::GUI::show_error(nullptr, _(L("Last instance of an object cannot be deleted.")));
return false;
}
@ -2172,7 +2217,7 @@ SettingsBundle ObjectList::get_item_settings_bundle(const DynamicPrintConfig* co
for (auto& opt_key : opt_keys)
{
auto category = config->def()->get(opt_key)->category;
if (category.empty() || (category == "Extruders" && extruders_cnt == 1))
if (improper_category(category, extruders_cnt))
continue;
std::vector< std::string > new_category;
@ -2243,7 +2288,17 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
// add instances to the object, if it has those
if (model_object->instances.size()>1)
increase_object_instances(obj_idx, model_object->instances.size());
{
std::vector<bool> print_idicator(model_object->instances.size());
for (int i = 0; i < model_object->instances.size(); ++i)
print_idicator[i] = model_object->instances[i]->is_printable();
const wxDataViewItem object_item = m_objects_model->GetItemById(obj_idx);
m_objects_model->AddInstanceChild(object_item, print_idicator);
Expand(m_objects_model->GetInstanceRootItem(object_item));
}
else
m_objects_model->SetPrintableState(model_object->instances[0]->is_printable() ? piPrintable : piUnprintable, obj_idx);
// add settings to the object, if it has those
add_settings_item(item, &model_object->config);
@ -2325,7 +2380,7 @@ void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
(*m_objects)[item->obj_idx]->config.has("extruder"))
{
const wxString extruder = wxString::Format("%d", (*m_objects)[item->obj_idx]->config.option<ConfigOptionInt>("extruder")->value);
m_objects_model->SetValue(extruder, m_objects_model->GetItemById(item->obj_idx), 1);
m_objects_model->SetValue(extruder, m_objects_model->GetItemById(item->obj_idx), colExtruder);
}
wxGetApp().plater()->canvas3D()->ensure_on_bed(item->obj_idx);
}
@ -2404,6 +2459,8 @@ void ObjectList::remove()
for (auto& item : sels)
{
if (m_objects_model->InvalidItem(item)) // item can be deleted for this moment (like last 2 Instances or Volumes)
continue;
if (m_objects_model->GetParent(item) == wxDataViewItem(0))
delete_from_model_and_list(itObject, m_objects_model->GetIdByItem(item), -1);
else {
@ -2442,13 +2499,13 @@ void ObjectList::del_layer_range(const t_layer_height_range& range)
select_item(selectable_item);
}
double get_min_layer_height(const int extruder_idx)
static double get_min_layer_height(const int extruder_idx)
{
const DynamicPrintConfig& config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
return config.opt_float("min_layer_height", extruder_idx <= 0 ? 0 : extruder_idx-1);
}
double get_max_layer_height(const int extruder_idx)
static double get_max_layer_height(const int extruder_idx)
{
const DynamicPrintConfig& config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
return config.opt_float("max_layer_height", extruder_idx <= 0 ? 0 : extruder_idx-1);
@ -2585,6 +2642,9 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_lay
ranges[new_range] = config;
wxDataViewItem root_item = m_objects_model->GetLayerRootItem(m_objects_model->GetItemById(obj_idx));
// To avoid update selection after deleting of a selected item (under GTK)
// set m_prevent_list_events to true
m_prevent_list_events = true;
m_objects_model->DeleteChildren(root_item);
if (root_item.IsOk())
@ -2835,6 +2895,9 @@ void ObjectList::update_selections_on_canvas()
wxDataViewItemArray sels;
GetSelections(sels);
// clear selection before adding new elements
selection.clear(); //OR remove_all()?
for (auto item : sels)
{
add_to_selection(item, selection, instance_idx, mode);
@ -3150,33 +3213,6 @@ void ObjectList::last_volume_is_deleted(const int obj_idx)
volume->config.set_key_value("extruder", new ConfigOptionInt(0));
}
/* #lm_FIXME_delete_after_testing
void ObjectList::update_settings_items()
{
m_prevent_canvas_selection_update = true;
wxDataViewItemArray sel;
GetSelections(sel); // stash selection
wxDataViewItemArray items;
m_objects_model->GetChildren(wxDataViewItem(0), items);
for (auto& item : items) {
const wxDataViewItem& settings_item = m_objects_model->GetSettingsItem(item);
select_item(settings_item ? settings_item : m_objects_model->AddSettingsChild(item));
// If settings item was deleted from the list,
// it's need to be deleted from selection array, if it was there
if (settings_item != m_objects_model->GetSettingsItem(item) &&
sel.Index(settings_item) != wxNOT_FOUND) {
sel.Remove(settings_item);
}
}
// restore selection:
SetSelections(sel);
m_prevent_canvas_selection_update = false;
}
*/
void ObjectList::update_and_show_object_settings_item()
{
const wxDataViewItem item = GetSelection();
@ -3311,7 +3347,8 @@ void ObjectList::instances_to_separated_object(const int obj_idx, const std::set
}
// Add new object to the object_list
add_object_to_list(m_objects->size() - 1);
const size_t new_obj_indx = static_cast<size_t>(m_objects->size() - 1);
add_object_to_list(new_obj_indx);
for (std::set<int>::const_reverse_iterator it = inst_idxs.rbegin(); it != inst_idxs.rend(); ++it)
{
@ -3319,12 +3356,17 @@ void ObjectList::instances_to_separated_object(const int obj_idx, const std::set
del_subobject_from_object(obj_idx, *it, itInstance);
delete_instance_from_list(obj_idx, *it);
}
// update printable state for new volumes on canvas3D
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object(new_obj_indx);
}
void ObjectList::instances_to_separated_objects(const int obj_idx)
{
const int inst_cnt = (*m_objects)[obj_idx]->instances.size();
std::vector<size_t> object_idxs;
for (int i = inst_cnt-1; i > 0 ; i--)
{
// create new object from initial
@ -3338,12 +3380,17 @@ void ObjectList::instances_to_separated_objects(const int obj_idx)
}
// Add new object to the object_list
add_object_to_list(m_objects->size() - 1);
const size_t new_obj_indx = static_cast<size_t>(m_objects->size() - 1);
add_object_to_list(new_obj_indx);
object_idxs.push_back(new_obj_indx);
// delete current instance from the initial object
del_subobject_from_object(obj_idx, i, itInstance);
delete_instance_from_list(obj_idx, i);
}
// update printable state for new volumes on canvas3D
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_objects(object_idxs);
}
void ObjectList::split_instances()
@ -3398,7 +3445,7 @@ void ObjectList::rename_item()
// The icon can't be edited so get its old value and reuse it.
wxVariant valueOld;
m_objects_model->GetValue(valueOld, item, 0);
m_objects_model->GetValue(valueOld, item, colName);
DataViewBitmapText bmpText;
bmpText << valueOld;
@ -3408,7 +3455,7 @@ void ObjectList::rename_item()
wxVariant value;
value << bmpText;
m_objects_model->SetValue(value, item, 0);
m_objects_model->SetValue(value, item, colName);
m_objects_model->ItemChanged(item);
update_name_in_model(item);
@ -3449,9 +3496,10 @@ void ObjectList::msw_rescale()
// update min size !!! A width of control shouldn't be a wxDefaultCoord
SetMinSize(wxSize(1, 15 * em));
GetColumn(0)->SetWidth(19 * em);
GetColumn(1)->SetWidth( 8 * em);
GetColumn(2)->SetWidth( 2 * em);
GetColumn(colName)->SetWidth(19 * em);
GetColumn(colPrint)->SetWidth( 2 * em);
GetColumn(colExtruder)->SetWidth( 8 * em);
GetColumn(colEditing)->SetWidth( 2 * em);
// rescale all icons, used by ObjectList
msw_rescale_icons();
@ -3472,18 +3520,18 @@ void ObjectList::msw_rescale()
void ObjectList::ItemValueChanged(wxDataViewEvent &event)
{
if (event.GetColumn() == 0)
if (event.GetColumn() == colName)
update_name_in_model(event.GetItem());
else if (event.GetColumn() == 1)
else if (event.GetColumn() == colExtruder)
update_extruder_in_config(event.GetItem());
}
void ObjectList::OnEditingDone(wxDataViewEvent &event)
{
if (event.GetColumn() != 0)
if (event.GetColumn() != colName)
return;
const auto renderer = dynamic_cast<BitmapTextRenderer*>(GetColumn(0)->GetRenderer());
const auto renderer = dynamic_cast<BitmapTextRenderer*>(GetColumn(colName)->GetRenderer());
if (renderer->WasCanceled())
wxTheApp->CallAfter([this]{
@ -3565,7 +3613,7 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const
/* We can change extruder for Object/Volume only.
* So, if Instance is selected, get its Object item and change it
*/
m_objects_model->SetValue(extruder_str, type & itInstance ? m_objects_model->GetTopParent(item) : item, 1);
m_objects_model->SetValue(extruder_str, type & itInstance ? m_objects_model->GetTopParent(item) : item, colExtruder);
const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) :
m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
@ -3588,18 +3636,69 @@ void ObjectList::update_after_undo_redo()
m_objects_model->DeleteAll();
size_t obj_idx = 0;
std::vector<size_t> obj_idxs;
obj_idxs.reserve(m_objects->size());
while (obj_idx < m_objects->size()) {
add_object_to_list(obj_idx, false);
obj_idxs.push_back(obj_idx);
++obj_idx;
}
#ifndef __WXOSX__
// selection_changed();
#endif /* __WXOSX__ */
update_selections();
m_prevent_canvas_selection_update = false;
// update printable states on canvas
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_objects(obj_idxs);
// update scene
wxGetApp().plater()->update();
}
void ObjectList::update_printable_state(int obj_idx, int instance_idx)
{
ModelObject* object = (*m_objects)[obj_idx];
const PrintIndicator printable = object->instances[instance_idx]->printable ? piPrintable : piUnprintable;
if (object->instances.size() == 1)
instance_idx = -1;
m_objects_model->SetPrintableState(printable, obj_idx, instance_idx);
}
void ObjectList::toggle_printable_state(wxDataViewItem item)
{
const ItemType type = m_objects_model->GetItemType(item);
if (!(type&(itObject|itInstance/*|itVolume*/)))
return;
if (type & itObject)
{
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
ModelObject* object = (*m_objects)[obj_idx];
// get object's printable and change it
const bool printable = !m_objects_model->IsPrintable(item);
const wxString snapshot_text = wxString::Format("%s %s",
printable ? _(L("Set Printable")) : _(L("Set Unprintable")),
object->name);
take_snapshot(snapshot_text);
// set printable value for all instances in object
for (auto inst : object->instances)
inst->printable = printable;
// update printable state on canvas
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx);
// update printable state in ObjectList
m_objects_model->SetObjectPrintableState(printable ? piPrintable : piUnprintable , item);
}
else
wxGetApp().plater()->canvas3D()->get_selection().toggle_instance_printable_state();
// update scene
wxGetApp().plater()->update();
}
ModelObject* ObjectList::object(const int obj_idx) const

View file

@ -225,6 +225,7 @@ public:
wxMenuItem* append_menu_item_settings(wxMenu* menu);
wxMenuItem* append_menu_item_change_type(wxMenu* menu);
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu, wxWindow* parent);
wxMenuItem* append_menu_item_printable(wxMenu* menu, wxWindow* parent);
void append_menu_items_osx(wxMenu* menu);
wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu);
void append_menu_item_export_stl(wxMenu* menu) const ;
@ -348,6 +349,9 @@ public:
void msw_rescale();
void update_after_undo_redo();
//update printable state for item from objects model
void update_printable_state(int obj_idx, int instance_idx);
void toggle_printable_state(wxDataViewItem item);
private:
#ifdef __WXOSX__

View file

@ -176,6 +176,7 @@ Preview::Preview(
, m_checkbox_retractions(nullptr)
, m_checkbox_unretractions(nullptr)
, m_checkbox_shells(nullptr)
, m_checkbox_legend(nullptr)
, m_config(config)
, m_process(process)
, m_gcode_preview_data(gcode_preview_data)
@ -252,6 +253,9 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view
m_checkbox_unretractions = new wxCheckBox(this, wxID_ANY, _(L("Unretractions")));
m_checkbox_shells = new wxCheckBox(this, wxID_ANY, _(L("Shells")));
m_checkbox_legend = new wxCheckBox(this, wxID_ANY, _(L("Legend")));
m_checkbox_legend->SetValue(true);
wxBoxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL);
top_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0);
top_sizer->Add(m_double_slider_sizer, 0, wxEXPAND, 0);
@ -270,6 +274,8 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view
bottom_sizer->Add(m_checkbox_unretractions, 0, wxEXPAND | wxALL, 5);
bottom_sizer->AddSpacer(10);
bottom_sizer->Add(m_checkbox_shells, 0, wxEXPAND | wxALL, 5);
bottom_sizer->AddSpacer(20);
bottom_sizer->Add(m_checkbox_legend, 0, wxEXPAND | wxALL, 5);
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->Add(top_sizer, 1, wxALL | wxEXPAND, 0);
@ -442,6 +448,7 @@ void Preview::bind_event_handlers()
m_checkbox_retractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this);
m_checkbox_unretractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this);
m_checkbox_shells->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this);
m_checkbox_legend->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_legend, this);
}
void Preview::unbind_event_handlers()
@ -453,6 +460,7 @@ void Preview::unbind_event_handlers()
m_checkbox_retractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this);
m_checkbox_unretractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this);
m_checkbox_shells->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this);
m_checkbox_legend->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_legend, this);
}
void Preview::show_hide_ui_elements(const std::string& what)
@ -464,6 +472,7 @@ void Preview::show_hide_ui_elements(const std::string& what)
m_checkbox_retractions->Enable(enable);
m_checkbox_unretractions->Enable(enable);
m_checkbox_shells->Enable(enable);
m_checkbox_legend->Enable(enable);
enable = (what != "none");
m_label_view_type->Enable(enable);
@ -476,6 +485,7 @@ void Preview::show_hide_ui_elements(const std::string& what)
m_checkbox_retractions->Show(visible);
m_checkbox_unretractions->Show(visible);
m_checkbox_shells->Show(visible);
m_checkbox_legend->Show(visible);
m_label_view_type->Show(visible);
m_choice_view_type->Show(visible);
}
@ -542,6 +552,12 @@ void Preview::on_checkbox_shells(wxCommandEvent& evt)
refresh_print();
}
void Preview::on_checkbox_legend(wxCommandEvent& evt)
{
m_canvas->enable_legend_texture(m_checkbox_legend->IsChecked());
m_canvas_widget->Refresh();
}
void Preview::update_view_type()
{
const DynamicPrintConfig& config = wxGetApp().preset_bundle->project_config;
@ -702,6 +718,11 @@ void Preview::update_double_slider_from_canvas(wxKeyEvent& event)
m_slider->SetHigherValue(new_pos);
if (event.ShiftDown() || m_slider->is_one_layer()) m_slider->SetLowerValue(m_slider->GetHigherValue());
}
else if (key == 'L') {
m_checkbox_legend->SetValue(!m_checkbox_legend->GetValue());
auto evt = wxCommandEvent();
on_checkbox_legend(evt);
}
else if (key == 'S')
m_slider->ChangeOneLayerLock();
else

View file

@ -80,6 +80,7 @@ class Preview : public wxPanel
wxCheckBox* m_checkbox_retractions;
wxCheckBox* m_checkbox_unretractions;
wxCheckBox* m_checkbox_shells;
wxCheckBox* m_checkbox_legend;
DynamicPrintConfig* m_config;
BackgroundSlicingProcess* m_process;
@ -147,6 +148,7 @@ private:
void on_checkbox_retractions(wxCommandEvent& evt);
void on_checkbox_unretractions(wxCommandEvent& evt);
void on_checkbox_shells(wxCommandEvent& evt);
void on_checkbox_legend(wxCommandEvent& evt);
// Create/Update/Reset double slider on 3dPreview
void create_double_slider();

View file

@ -29,19 +29,21 @@ GLGizmoBase::Grabber::Grabber()
color[0] = 1.0f;
color[1] = 1.0f;
color[2] = 1.0f;
color[3] = 1.0f;
}
void GLGizmoBase::Grabber::render(bool hover, float size) const
{
float render_color[3];
float render_color[4];
if (hover)
{
render_color[0] = 1.0f - color[0];
render_color[1] = 1.0f - color[1];
render_color[2] = 1.0f - color[2];
render_color[3] = color[3];
}
else
::memcpy((void*)render_color, (const void*)color, 3 * sizeof(float));
::memcpy((void*)render_color, (const void*)color, 4 * sizeof(float));
render(size, render_color, true);
}
@ -63,7 +65,7 @@ void GLGizmoBase::Grabber::render(float size, const float* render_color, bool us
if (use_lighting)
glsafe(::glEnable(GL_LIGHTING));
glsafe(::glColor3fv(render_color));
glsafe(::glColor4fv(render_color));
glsafe(::glPushMatrix());
glsafe(::glTranslated(center(0), center(1), center(2)));
@ -144,9 +146,9 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, u
, m_dragging(false)
, m_imgui(wxGetApp().imgui())
{
::memcpy((void*)m_base_color, (const void*)DEFAULT_BASE_COLOR, 3 * sizeof(float));
::memcpy((void*)m_drag_color, (const void*)DEFAULT_DRAG_COLOR, 3 * sizeof(float));
::memcpy((void*)m_highlight_color, (const void*)DEFAULT_HIGHLIGHT_COLOR, 3 * sizeof(float));
::memcpy((void*)m_base_color, (const void*)DEFAULT_BASE_COLOR, 4 * sizeof(float));
::memcpy((void*)m_drag_color, (const void*)DEFAULT_DRAG_COLOR, 4 * sizeof(float));
::memcpy((void*)m_highlight_color, (const void*)DEFAULT_HIGHLIGHT_COLOR, 4 * sizeof(float));
}
void GLGizmoBase::set_hover_id(int id)
@ -161,7 +163,7 @@ void GLGizmoBase::set_hover_id(int id)
void GLGizmoBase::set_highlight_color(const float* color)
{
if (color != nullptr)
::memcpy((void*)m_highlight_color, (const void*)color, 3 * sizeof(float));
::memcpy((void*)m_highlight_color, (const void*)color, 4 * sizeof(float));
}
void GLGizmoBase::enable_grabber(unsigned int id)
@ -210,7 +212,7 @@ void GLGizmoBase::update(const UpdateData& data)
on_update(data);
}
std::array<float, 3> GLGizmoBase::picking_color_component(unsigned int id) const
std::array<float, 4> GLGizmoBase::picking_color_component(unsigned int id) const
{
static const float INV_255 = 1.0f / 255.0f;
@ -220,9 +222,12 @@ std::array<float, 3> GLGizmoBase::picking_color_component(unsigned int id) const
id -= m_group_id;
// color components are encoded to match the calculation of volume_id made into GLCanvas3D::_picking_pass()
return std::array<float, 3> { (float)((id >> 0) & 0xff) * INV_255, // red
(float)((id >> 8) & 0xff) * INV_255, // green
(float)((id >> 16) & 0xff) * INV_255 }; // blue
return std::array<float, 4> {
float((id >> 0) & 0xff) * INV_255, // red
float((id >> 8) & 0xff) * INV_255, // green
float((id >> 16) & 0xff) * INV_255, // blue
float(picking_checksum_alpha_channel(id & 0xff, (id >> 8) & 0xff, (id >> 16) & 0xff))* INV_255 // checksum for validating against unwanted alpha blending and multi sampling
};
}
void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
@ -247,10 +252,11 @@ void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const
{
if (m_grabbers[i].enabled)
{
std::array<float, 3> color = picking_color_component(i);
std::array<float, 4> color = picking_color_component(i);
m_grabbers[i].color[0] = color[0];
m_grabbers[i].color[1] = color[1];
m_grabbers[i].color[2] = color[2];
m_grabbers[i].color[3] = color[3];
m_grabbers[i].render_for_picking(mean_size);
}
}
@ -267,5 +273,20 @@ std::string GLGizmoBase::format(float value, unsigned int decimals) const
return Slic3r::string_printf("%.*f", decimals, value);
}
// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components
// were not interpolated by alpha blending or multi sampling.
unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue)
{
// 8 bit hash for the color
unsigned char b = ((((37 * red) + green) & 0x0ff) * 37 + blue) & 0x0ff;
// Increase enthropy by a bit reversal
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
// Flip every second bit to increase the enthropy even more.
b ^= 0x55;
return b;
}
} // namespace GUI
} // namespace Slic3r

View file

@ -21,11 +21,11 @@ class ModelObject;
namespace GUI {
static const float DEFAULT_BASE_COLOR[3] = { 0.625f, 0.625f, 0.625f };
static const float DEFAULT_DRAG_COLOR[3] = { 1.0f, 1.0f, 1.0f };
static const float DEFAULT_HIGHLIGHT_COLOR[3] = { 1.0f, 0.38f, 0.0f };
static const float AXES_COLOR[3][3] = { { 0.75f, 0.0f, 0.0f }, { 0.0f, 0.75f, 0.0f }, { 0.0f, 0.0f, 0.75f } };
static const float CONSTRAINED_COLOR[3] = { 0.5f, 0.5f, 0.5f };
static const float DEFAULT_BASE_COLOR[4] = { 0.625f, 0.625f, 0.625f, 1.0f };
static const float DEFAULT_DRAG_COLOR[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
static const float DEFAULT_HIGHLIGHT_COLOR[4] = { 1.0f, 0.38f, 0.0f, 1.0f };
static const float AXES_COLOR[][4] = { { 0.75f, 0.0f, 0.0f, 1.0f }, { 0.0f, 0.75f, 0.0f, 1.0f }, { 0.0f, 0.0f, 0.75f, 1.0f } };
static const float CONSTRAINED_COLOR[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
@ -48,7 +48,7 @@ protected:
Vec3d center;
Vec3d angles;
float color[3];
float color[4];
bool enabled;
bool dragging;
@ -94,9 +94,9 @@ protected:
unsigned int m_sprite_id;
int m_hover_id;
bool m_dragging;
float m_base_color[3];
float m_drag_color[3];
float m_highlight_color[3];
float m_base_color[4];
float m_drag_color[4];
float m_highlight_color[4];
mutable std::vector<Grabber> m_grabbers;
ImGuiWrapper* m_imgui;
@ -166,7 +166,7 @@ protected:
// Returns the picking color for the given id, based on the BASE_ID constant
// No check is made for clashing with other picking color (i.e. GLVolumes)
std::array<float, 3> picking_color_component(unsigned int id) const;
std::array<float, 4> picking_color_component(unsigned int id) const;
void render_grabbers(const BoundingBoxf3& box) const;
void render_grabbers(float size) const;
void render_grabbers_for_picking(const BoundingBoxf3& box) const;
@ -175,6 +175,10 @@ protected:
std::string format(float value, unsigned int decimals) const;
};
// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components
// were not interpolated by alpha blending or multi sampling.
extern unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue);
} // namespace GUI
} // namespace Slic3r

View file

@ -17,7 +17,7 @@ namespace GUI {
const double GLGizmoCut::Offset = 10.0;
const double GLGizmoCut::Margin = 20.0;
const std::array<float, 3> GLGizmoCut::GrabberColor = { 1.0, 0.5, 0.0 };
const std::array<float, 4> GLGizmoCut::GrabberColor = { 1.0, 0.5, 0.0, 1.0 };
GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)

View file

@ -11,7 +11,7 @@ class GLGizmoCut : public GLGizmoBase
{
static const double Offset;
static const double Margin;
static const std::array<float, 3> GrabberColor;
static const std::array<float, 4> GrabberColor;
mutable double m_cut_z;
double m_start_z;

View file

@ -115,7 +115,7 @@ void GLGizmoFlatten::on_render_for_picking() const
const_cast<GLGizmoFlatten*>(this)->update_planes();
for (int i = 0; i < (int)m_planes.size(); ++i)
{
glsafe(::glColor3fv(picking_color_component(i).data()));
glsafe(::glColor4fv(picking_color_component(i).data()));
::glBegin(GL_POLYGON);
for (const Vec3d& vertex : m_planes[i].vertices)
{

View file

@ -104,15 +104,15 @@ void GLGizmoMove3D::on_render() const
// x axis
m_grabbers[0].center = Vec3d(box.max(0) + Offset, center(1), center(2));
::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float));
::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 4 * sizeof(float));
// y axis
m_grabbers[1].center = Vec3d(center(0), box.max(1) + Offset, center(2));
::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float));
::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[1], 4 * sizeof(float));
// z axis
m_grabbers[2].center = Vec3d(center(0), center(1), box.max(2) + Offset);
::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float));
::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[2], 4 * sizeof(float));
glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f));
@ -123,7 +123,7 @@ void GLGizmoMove3D::on_render() const
{
if (m_grabbers[i].enabled)
{
glsafe(::glColor3fv(AXES_COLOR[i]));
glsafe(::glColor4fv(AXES_COLOR[i]));
::glBegin(GL_LINES);
::glVertex3dv(center.data());
::glVertex3dv(m_grabbers[i].center.data());
@ -142,7 +142,7 @@ void GLGizmoMove3D::on_render() const
else
{
// draw axis
glsafe(::glColor3fv(AXES_COLOR[m_hover_id]));
glsafe(::glColor4fv(AXES_COLOR[m_hover_id]));
::glBegin(GL_LINES);
::glVertex3dv(center.data());
::glVertex3dv(m_grabbers[m_hover_id].center.data());
@ -220,19 +220,20 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box
float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0);
double size = m_dragging ? (double)m_grabbers[axis].get_dragging_half_size(mean_size) : (double)m_grabbers[axis].get_half_size(mean_size);
float color[3];
::memcpy((void*)color, (const void*)m_grabbers[axis].color, 3 * sizeof(float));
float color[4];
::memcpy((void*)color, (const void*)m_grabbers[axis].color, 4 * sizeof(float));
if (!picking && (m_hover_id != -1))
{
color[0] = 1.0f - color[0];
color[1] = 1.0f - color[1];
color[2] = 1.0f - color[2];
color[3] = color[3];
}
if (!picking)
glsafe(::glEnable(GL_LIGHTING));
glsafe(::glColor3fv(color));
glsafe(::glColor4fv(color));
glsafe(::glPushMatrix());
glsafe(::glTranslated(m_grabbers[axis].center(0), m_grabbers[axis].center(1), m_grabbers[axis].center(2)));
if (axis == X)

View file

@ -155,7 +155,7 @@ void GLGizmoRotate::on_render() const
transform_to_local(selection);
glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f));
glsafe(::glColor3fv((m_hover_id != -1) ? m_drag_color : m_highlight_color));
glsafe(::glColor4fv((m_hover_id != -1) ? m_drag_color : m_highlight_color));
render_circle();
@ -166,7 +166,7 @@ void GLGizmoRotate::on_render() const
render_reference_radius();
}
glsafe(::glColor3fv(m_highlight_color));
glsafe(::glColor4fv(m_highlight_color));
if (m_hover_id != -1)
render_angle();
@ -287,14 +287,14 @@ void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const
m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0);
m_grabbers[0].angles(2) = m_angle;
glsafe(::glColor3fv((m_hover_id != -1) ? m_drag_color : m_highlight_color));
glsafe(::glColor4fv((m_hover_id != -1) ? m_drag_color : m_highlight_color));
::glBegin(GL_LINES);
::glVertex3f(0.0f, 0.0f, 0.0f);
::glVertex3dv(m_grabbers[0].center.data());
glsafe(::glEnd());
::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 3 * sizeof(float));
::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 4 * sizeof(float));
render_grabbers(box);
}
@ -306,8 +306,8 @@ void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool pick
float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0);
double size = m_dragging ? (double)m_grabbers[0].get_dragging_half_size(mean_size) : (double)m_grabbers[0].get_half_size(mean_size);
float color[3];
::memcpy((void*)color, (const void*)m_grabbers[0].color, 3 * sizeof(float));
float color[4];
::memcpy((void*)color, (const void*)m_grabbers[0].color, 4 * sizeof(float));
if (!picking && (m_hover_id != -1))
{
color[0] = 1.0f - color[0];
@ -318,7 +318,7 @@ void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool pick
if (!picking)
glsafe(::glEnable(GL_LIGHTING));
glsafe(::glColor3fv(color));
glsafe(::glColor4fv(color));
glsafe(::glPushMatrix());
glsafe(::glTranslated(m_grabbers[0].center(0), m_grabbers[0].center(1), m_grabbers[0].center(2)));
glsafe(::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0));

View file

@ -172,20 +172,20 @@ void GLGizmoScale3D::on_render() const
// x axis
m_grabbers[0].center = m_transform * Vec3d(m_box.min(0), center(1), center(2)) - offset_x;
m_grabbers[1].center = m_transform * Vec3d(m_box.max(0), center(1), center(2)) + offset_x;
::memcpy((void*)m_grabbers[0].color, (ctrl_down && (m_hover_id == 1)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[0], 3 * sizeof(float));
::memcpy((void*)m_grabbers[1].color, (ctrl_down && (m_hover_id == 0)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[0], 3 * sizeof(float));
::memcpy((void*)m_grabbers[0].color, (ctrl_down && (m_hover_id == 1)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[0], 4 * sizeof(float));
::memcpy((void*)m_grabbers[1].color, (ctrl_down && (m_hover_id == 0)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[0], 4 * sizeof(float));
// y axis
m_grabbers[2].center = m_transform * Vec3d(center(0), m_box.min(1), center(2)) - offset_y;
m_grabbers[3].center = m_transform * Vec3d(center(0), m_box.max(1), center(2)) + offset_y;
::memcpy((void*)m_grabbers[2].color, (ctrl_down && (m_hover_id == 3)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[1], 3 * sizeof(float));
::memcpy((void*)m_grabbers[3].color, (ctrl_down && (m_hover_id == 2)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[1], 3 * sizeof(float));
::memcpy((void*)m_grabbers[2].color, (ctrl_down && (m_hover_id == 3)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[1], 4 * sizeof(float));
::memcpy((void*)m_grabbers[3].color, (ctrl_down && (m_hover_id == 2)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[1], 4 * sizeof(float));
// z axis
m_grabbers[4].center = m_transform * Vec3d(center(0), center(1), m_box.min(2)) - offset_z;
m_grabbers[5].center = m_transform * Vec3d(center(0), center(1), m_box.max(2)) + offset_z;
::memcpy((void*)m_grabbers[4].color, (ctrl_down && (m_hover_id == 5)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[2], 3 * sizeof(float));
::memcpy((void*)m_grabbers[5].color, (ctrl_down && (m_hover_id == 4)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[2], 3 * sizeof(float));
::memcpy((void*)m_grabbers[4].color, (ctrl_down && (m_hover_id == 5)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[2], 4 * sizeof(float));
::memcpy((void*)m_grabbers[5].color, (ctrl_down && (m_hover_id == 4)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[2], 4 * sizeof(float));
// uniform
m_grabbers[6].center = m_transform * Vec3d(m_box.min(0), m_box.min(1), center(2)) - offset_x - offset_y;
@ -194,7 +194,7 @@ void GLGizmoScale3D::on_render() const
m_grabbers[9].center = m_transform * Vec3d(m_box.min(0), m_box.max(1), center(2)) - offset_x + offset_y;
for (int i = 6; i < 10; ++i)
{
::memcpy((void*)m_grabbers[i].color, (const void*)m_highlight_color, 3 * sizeof(float));
::memcpy((void*)m_grabbers[i].color, (const void*)m_highlight_color, 4 * sizeof(float));
}
// sets grabbers orientation
@ -214,20 +214,20 @@ void GLGizmoScale3D::on_render() const
// draw connections
if (m_grabbers[0].enabled && m_grabbers[1].enabled)
{
glsafe(::glColor3fv(m_grabbers[0].color));
glsafe(::glColor4fv(m_grabbers[0].color));
render_grabbers_connection(0, 1);
}
if (m_grabbers[2].enabled && m_grabbers[3].enabled)
{
glsafe(::glColor3fv(m_grabbers[2].color));
glsafe(::glColor4fv(m_grabbers[2].color));
render_grabbers_connection(2, 3);
}
if (m_grabbers[4].enabled && m_grabbers[5].enabled)
{
glsafe(::glColor3fv(m_grabbers[4].color));
glsafe(::glColor4fv(m_grabbers[4].color));
render_grabbers_connection(4, 5);
}
glsafe(::glColor3fv(m_base_color));
glsafe(::glColor4fv(m_base_color));
render_grabbers_connection(6, 7);
render_grabbers_connection(7, 8);
render_grabbers_connection(8, 9);
@ -238,7 +238,7 @@ void GLGizmoScale3D::on_render() const
else if ((m_hover_id == 0) || (m_hover_id == 1))
{
// draw connection
glsafe(::glColor3fv(m_grabbers[0].color));
glsafe(::glColor4fv(m_grabbers[0].color));
render_grabbers_connection(0, 1);
// draw grabbers
m_grabbers[0].render(true, grabber_mean_size);
@ -247,7 +247,7 @@ void GLGizmoScale3D::on_render() const
else if ((m_hover_id == 2) || (m_hover_id == 3))
{
// draw connection
glsafe(::glColor3fv(m_grabbers[2].color));
glsafe(::glColor4fv(m_grabbers[2].color));
render_grabbers_connection(2, 3);
// draw grabbers
m_grabbers[2].render(true, grabber_mean_size);
@ -256,7 +256,7 @@ void GLGizmoScale3D::on_render() const
else if ((m_hover_id == 4) || (m_hover_id == 5))
{
// draw connection
glsafe(::glColor3fv(m_grabbers[4].color));
glsafe(::glColor4fv(m_grabbers[4].color));
render_grabbers_connection(4, 5);
// draw grabbers
m_grabbers[4].render(true, grabber_mean_size);
@ -265,7 +265,7 @@ void GLGizmoScale3D::on_render() const
else if (m_hover_id >= 6)
{
// draw connection
glsafe(::glColor3fv(m_drag_color));
glsafe(::glColor4fv(m_drag_color));
render_grabbers_connection(6, 7);
render_grabbers_connection(7, 8);
render_grabbers_connection(8, 9);

View file

@ -286,7 +286,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
glsafe(::glTranslated(0.0, 0.0, m_z_shift));
glsafe(::glMultMatrixd(instance_matrix.data()));
float render_color[3];
float render_color[4];
size_t cache_size = m_editing_mode ? m_editing_cache.size() : m_normal_cache.size();
for (size_t i = 0; i < cache_size; ++i)
{
@ -298,12 +298,14 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
// First decide about the color of the point.
if (picking) {
std::array<float, 3> color = picking_color_component(i);
std::array<float, 4> color = picking_color_component(i);
render_color[0] = color[0];
render_color[1] = color[1];
render_color[2] = color[2];
render_color[3] = color[3];
}
else {
render_color[3] = 1.f;
if ((m_hover_id == i && m_editing_mode)) { // ignore hover state unless editing mode is active
render_color[0] = 0.f;
render_color[1] = 1.0f;
@ -320,7 +322,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
for (unsigned char i=0; i<3; ++i) render_color[i] = 0.5f;
}
}
glsafe(::glColor3fv(render_color));
glsafe(::glColor4fv(render_color));
float render_color_emissive[4] = { 0.5f * render_color[0], 0.5f * render_color[1], 0.5f * render_color[2], 1.f};
glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive));
@ -422,9 +424,9 @@ void GLGizmoSlaSupports::update_mesh()
// Unprojects the mouse position on the mesh and return the hit point and normal of the facet.
// The function throws if no intersection if found.
std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal
// Return false if no intersection was found, true otherwise.
bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal)
{
// if the gizmo doesn't have the V, F structures for igl, calculate them first:
if (m_its == nullptr)
@ -457,7 +459,7 @@ std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3),
point1.cast<float>(), (point2-point1).cast<float>(), hits))
throw std::invalid_argument("unproject_on_mesh(): No intersection found.");
return false; // no intersection found
std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; });
@ -481,14 +483,12 @@ std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
if (i==hits.size() || (hits.size()-i) % 2 != 0) {
// All hits are either clipped, or there is an odd number of unclipped
// hits - meaning the nearest must be from inside the mesh.
throw std::invalid_argument("unproject_on_mesh(): No intersection found.");
return false;
}
// Calculate and return both the point and the facet normal.
return std::make_pair(
result,
a.cross(b)
);
pos_and_normal = std::make_pair(result, a.cross(b));
return true;
}
// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event.
@ -526,17 +526,16 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
// If there is some selection, don't add new point and deselect everything instead.
if (m_selection_empty) {
try {
std::pair<Vec3f, Vec3f> pos_and_normal = unproject_on_mesh(mouse_position); // don't create anything if this throws
std::pair<Vec3f, Vec3f> pos_and_normal;
if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection
wxGetApp().plater()->take_snapshot(_(L("Add support point")));
m_editing_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second);
m_parent.set_as_dirty();
m_wait_for_up_event = true;
}
catch (...) { // not clicked on object
else
return false;
}
}
else
select_point(NoPoints);
@ -739,10 +738,8 @@ void GLGizmoSlaSupports::on_update(const UpdateData& data)
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;
try {
pos_and_normal = unproject_on_mesh(data.mouse_pos.cast<double>());
}
catch (...) { return; }
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;

View file

@ -32,7 +32,7 @@ private:
int m_active_instance = -1;
float m_active_instance_bb_radius; // to cache the bb
mutable float m_z_shift = 0.f;
std::pair<Vec3f, Vec3f> unproject_on_mesh(const Vec2d& mouse_pos);
bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
const float RenderPointScale = 1.f;

View file

@ -354,7 +354,7 @@ bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (
ImGui::Selectable(item_text, i < hovered);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(item_text);
ImGui::SetTooltip("%s", item_text);
hovered = i;
is_hovered = true;
}

View file

@ -132,6 +132,7 @@ void KBShortcutsDialog::fill_shortcuts()
plater_shortcuts.reserve(20);
plater_shortcuts.push_back(Shortcut("A", L("Arrange")));
plater_shortcuts.push_back(Shortcut("Shift+A", L("Arrange selection")));
plater_shortcuts.push_back(Shortcut(ctrl+"A", L("Select All objects")));
plater_shortcuts.push_back(Shortcut("Del", L("Delete selected")));
plater_shortcuts.push_back(Shortcut(ctrl+"Del", L("Delete All")));
@ -177,6 +178,7 @@ void KBShortcutsDialog::fill_shortcuts()
preview_shortcuts.push_back(Shortcut(L("Arrow Down"), L("Lower Layer")));
preview_shortcuts.push_back(Shortcut("U", L("Upper Layer")));
preview_shortcuts.push_back(Shortcut("D", L("Lower Layer")));
preview_shortcuts.push_back(Shortcut("L", L("Show/Hide (L)egend")));
m_full_shortcuts.push_back(std::make_pair(_(L("Preview Shortcuts")), std::make_pair(preview_shortcuts, szLeft)));

View file

@ -279,6 +279,18 @@ bool MainFrame::can_export_gcode() const
return true;
}
bool MainFrame::can_send_gcode() const
{
if (m_plater == nullptr)
return false;
if (m_plater->model().objects.empty())
return false;
const auto print_host_opt = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionString>("print_host");
return print_host_opt != nullptr && !print_host_opt->value.empty();
}
bool MainFrame::can_slice() const
{
bool bg_proc = wxGetApp().app_config->get("background_processing") == "1";
@ -451,6 +463,10 @@ void MainFrame::init_menubar()
[this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(); }, menu_icon("export_gcode"), nullptr,
[this](){return can_export_gcode(); }, this);
m_changeable_menu_items.push_back(item_export_gcode);
wxMenuItem* item_send_gcode = append_menu_item(export_menu, wxID_ANY, _(L("S&end G-code")) + dots +"\tCtrl+Shift+G", _(L("Send to print current plate as G-code")),
[this](wxCommandEvent&) { if (m_plater) m_plater->send_gcode(); }, menu_icon("export_gcode"), nullptr,
[this](){return can_send_gcode(); }, this);
m_changeable_menu_items.push_back(item_send_gcode);
export_menu->AppendSeparator();
append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &STL")) + dots, _(L("Export current plate as STL")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, menu_icon("export_plater"), nullptr,
@ -689,7 +705,8 @@ void MainFrame::update_menubar()
{
const bool is_fff = plater()->printer_technology() == ptFFF;
m_changeable_menu_items[miExport] ->SetItemLabel((is_fff ? _(L("Export &G-code")) : _(L("Export")) ) + dots + "\tCtrl+G");
m_changeable_menu_items[miExport] ->SetItemLabel((is_fff ? _(L("Export &G-code")) : _(L("E&xport")) ) + dots + "\tCtrl+G");
m_changeable_menu_items[miSend] ->SetItemLabel((is_fff ? _(L("S&end G-code")) : _(L("S&end to print"))) + dots + "\tCtrl+Shift+G");
m_changeable_menu_items[miMaterialTab] ->SetItemLabel((is_fff ? _(L("&Filament Settings Tab")) : _(L("Mate&rial Settings Tab"))) + "\tCtrl+3");
m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_scaled_bitmap(this, menu_icon(is_fff ? "spool": "resin")));

View file

@ -67,6 +67,7 @@ class MainFrame : public DPIFrame
bool can_export_model() const;
bool can_export_supports() const;
bool can_export_gcode() const;
bool can_send_gcode() const;
bool can_slice() const;
bool can_change_view() const;
bool can_select() const;
@ -79,6 +80,7 @@ class MainFrame : public DPIFrame
enum MenuItems
{ // FFF SLA
miExport = 0, // Export G-code Export
miSend, // Send G-code Send to print
miMaterialTab, // Filament Settings Material Settings
};

View file

@ -556,14 +556,21 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
Tab* tab = wxGetApp().get_tab(Preset::TYPE_SLA_PRINT);
if (!tab) return;
if (opt_key == "pad_enable") {
tab->set_value(opt_key, value);
tab->update();
DynamicPrintConfig new_conf = *config_sla;
if (opt_key == "pad") {
const wxString& selection = boost::any_cast<wxString>(value);
const bool pad_enable = selection == _("None") ? false : true;
new_conf.set_key_value("pad_enable", new ConfigOptionBool(pad_enable));
if (selection == _("Below object"))
new_conf.set_key_value("pad_zero_elevation", new ConfigOptionBool(false));
else if (selection == _("Around object"))
new_conf.set_key_value("pad_zero_elevation", new ConfigOptionBool(true));
}
else
{
assert(opt_key == "support");
DynamicPrintConfig new_conf = *config_sla;
const wxString& selection = boost::any_cast<wxString>(value);
const bool supports_enable = selection == _("None") ? false : true;
@ -573,10 +580,9 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
new_conf.set_key_value("support_buildplate_only", new ConfigOptionBool(false));
else if (selection == _("Support on build plate only"))
new_conf.set_key_value("support_buildplate_only", new ConfigOptionBool(true));
tab->load_config(new_conf);
}
tab->load_config(new_conf);
tab->update_dirty();
};
@ -594,9 +600,19 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
line = Line{ "", "" };
option = m_og_sla->get_option("pad_enable");
option.opt.sidetext = " ";
ConfigOptionDef pad_def;
pad_def.label = L("Pad");
pad_def.type = coStrings;
pad_def.gui_type = "select_open";
pad_def.tooltip = L("Select what kind of pad do you need");
pad_def.enum_labels.push_back(L("None"));
pad_def.enum_labels.push_back(L("Below object"));
pad_def.enum_labels.push_back(L("Around object"));
pad_def.set_default_value(new ConfigOptionStrings{ "Below object" });
option = Option(pad_def, "pad");
option.opt.full_width = true;
line.append_option(option);
line.append_widget(empty_widget);
m_og_sla->append_line(line);
@ -1175,10 +1191,10 @@ void Sidebar::show_sliced_info_sizer(const bool show)
if (ps.estimated_silent_print_time != "N/A") {
new_label += wxString::Format("\n - %s", _(L("stealth mode")));
info_text += wxString::Format("\n%s", ps.estimated_silent_print_time);
for (int i = (int)ps.estimated_normal_color_print_times.size() - 1; i >= 0; --i)
for (int i = (int)ps.estimated_silent_color_print_times.size() - 1; i >= 0; --i)
{
new_label += wxString::Format("\n - %s%d", _(L("Color ")), i + 1);
info_text += wxString::Format("\n%s", ps.estimated_normal_color_print_times[i]);
info_text += wxString::Format("\n%s", ps.estimated_silent_color_print_times[i]);
}
}
p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label);
@ -2302,7 +2318,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
}
if (new_model != nullptr) {
if (new_model != nullptr && new_model->objects.size() > 1) {
wxMessageDialog dlg(q, _(L(
"Multiple objects were loaded for a multi-material printer.\n"
"Instead of considering them as multiple objects, should I consider\n"
@ -2326,6 +2342,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
// automatic selection of added objects
if (!obj_idxs.empty() && (view3D != nullptr))
{
// update printable state for new volumes on canvas3D
wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_objects(obj_idxs);
Selection& selection = view3D->get_canvas3d()->get_selection();
selection.clear();
for (size_t idx : obj_idxs)
@ -2627,7 +2646,6 @@ void Plater::priv::reset()
void Plater::priv::mirror(Axis axis)
{
this->take_snapshot(_(L("Mirror")));
view3D->mirror_selection(axis);
}
@ -3490,6 +3508,9 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
sidebar->obj_list()->append_menu_item_instance_to_object(menu, q);
menu->AppendSeparator();
wxMenuItem* menu_item_printable = sidebar->obj_list()->append_menu_item_printable(menu, q);
menu->AppendSeparator();
append_menu_item(menu, wxID_ANY, _(L("Reload from Disk")), _(L("Reload the selected file from Disk")),
[this](wxCommandEvent&) { reload_from_disk(); });
@ -3497,6 +3518,17 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
[this](wxCommandEvent&) { q->export_stl(false, true); });
menu->AppendSeparator();
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) {
const Selection& selection = get_selection();
int instance_idx = selection.get_instance_idx();
evt.Enable(instance_idx != -1);
if (instance_idx != -1)
{
evt.Check(model.objects[selection.get_object_idx()]->instances[instance_idx]->printable);
view3D->set_as_dirty();
}
}, menu_item_printable->GetId());
}
sidebar->obj_list()->append_menu_item_fix_through_netfabb(menu);
@ -4536,7 +4568,7 @@ void Plater::undo_redo_topmost_string_getter(const bool is_undo, std::string& ou
return;
}
out_text = L("");
out_text = "";
}
void Plater::on_extruders_change(int num_extruders)

View file

@ -477,9 +477,10 @@ const std::vector<std::string>& Preset::sla_print_options()
"pad_wall_thickness",
"pad_wall_height",
"pad_max_merge_distance",
"pad_edge_radius",
// "pad_edge_radius",
"pad_wall_slope",
"pad_object_gap",
"pad_zero_elevation",
"pad_object_connector_stride",
"pad_object_connector_width",
"pad_object_connector_penetration",

View file

@ -100,6 +100,7 @@ void Selection::set_volumes(GLVolumePtrs* volumes)
update_valid();
}
// Init shall be called from the OpenGL render function, so that the OpenGL context is initialized!
bool Selection::init()
{
if (!m_arrow.init())
@ -1461,6 +1462,39 @@ std::vector<unsigned int> Selection::get_unselected_volume_idxs_from(const std::
return idxs;
}
void Selection::toggle_instance_printable_state()
{
int instance_idx = get_instance_idx();
if (instance_idx == -1)
return;
int obj_idx = get_object_idx();
if ((0 <= obj_idx) && (obj_idx < (int)m_model->objects.size()))
{
ModelObject* model_object = m_model->objects[obj_idx];
if ((0 <= instance_idx) && (instance_idx < (int)model_object->instances.size()))
{
ModelInstance* instance = model_object->instances[instance_idx];
const bool printable = !instance->printable;
wxString snapshot_text = model_object->instances.size() == 1 ? wxString::Format("%s %s",
printable ? _(L("Set Printable")) : _(L("Set Unprintable")), model_object->name) :
printable ? _(L("Set Printable Instance")) : _(L("Set Unprintable Instance"));
wxGetApp().plater()->take_snapshot(snapshot_text);
instance->printable = printable;
for (GLVolume* volume : *m_volumes)
{
if ((volume->object_idx() == obj_idx) && (volume->instance_idx() == instance_idx))
volume->printable = instance->printable;
}
wxGetApp().obj_list()->update_printable_state(obj_idx, instance_idx);
}
}
}
void Selection::update_valid()
{
m_valid = (m_volumes != nullptr) && (m_model != nullptr);

View file

@ -336,6 +336,8 @@ public:
// returns the list of idxs of the volumes contained in the given list but not in the selection
std::vector<unsigned int> get_unselected_volume_idxs_from(const std::vector<unsigned int>& volume_idxs) const;
void toggle_instance_printable_state();
private:
void update_valid();
void update_type();

View file

@ -58,21 +58,19 @@ std::string get_mem_info(bool format_as_html)
std::string b_end = format_as_html ? "</b>" : "";
std::string line_end = format_as_html ? "<br>" : "\n";
const Slic3r::UndoRedo::Stack &stack = wxGetApp().plater()->undo_redo_stack_main();
out << b_start << "RAM size reserved for the Undo / Redo stack [MB]: " << b_end << Slic3r::format_memsize_MB(stack.get_memory_limit()) << line_end;
out << b_start << "RAM size occupied by the Undo / Redo stack [MB]: " << b_end << Slic3r::format_memsize_MB(stack.memsize()) << line_end << line_end;
#ifdef _WIN32
HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ::GetCurrentProcessId());
if (hProcess != nullptr) {
PROCESS_MEMORY_COUNTERS_EX pmc;
if (GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)))
out << b_start << "WorkingSet [MB]: " << b_end << format_memsize_MB(pmc.WorkingSetSize) << line_end
<< b_start << "PrivateBytes [MB]: " << b_end << format_memsize_MB(pmc.PrivateUsage) << line_end
<< b_start << "Pagefile(peak) [MB]: " << b_end << format_memsize_MB(pmc.PagefileUsage) << "(" << format_memsize_MB(pmc.PeakPagefileUsage) << ")" << line_end;
CloseHandle(hProcess);
std::string mem_info_str = log_memory_info(true);
std::istringstream mem_info(mem_info_str);
std::string value;
while (std::getline(mem_info, value, ':')) {
out << b_start << (value+": ") << b_end;
std::getline(mem_info, value, ';');
out << value << line_end;
}
#endif
const Slic3r::UndoRedo::Stack &stack = wxGetApp().plater()->undo_redo_stack_main();
out << b_start << "RAM size reserved for the Undo / Redo stack: " << b_end << Slic3r::format_memsize_MB(stack.get_memory_limit()) << line_end;
out << b_start << "RAM size occupied by the Undo / Redo stack: " << b_end << Slic3r::format_memsize_MB(stack.memsize()) << line_end << line_end;
return out.str();
}

View file

@ -373,6 +373,8 @@ void Tab::update_labels_colour()
// Thaw();
auto cur_item = m_treectrl->GetFirstVisibleItem();
if (!cur_item || !m_treectrl->IsVisible(cur_item))
return;
while (cur_item) {
auto title = m_treectrl->GetItemText(cur_item);
for (auto page : m_pages)
@ -729,6 +731,8 @@ void Tab::update_mode()
m_mode_sizer->SetMode(m_mode);
update_visibility();
update_changed_tree_ui();
}
void Tab::update_visibility()
@ -741,8 +745,6 @@ void Tab::update_visibility()
Layout();
Thaw();
update_changed_tree_ui();
}
void Tab::msw_rescale()
@ -834,6 +836,11 @@ static wxString support_combo_value_for_config(const DynamicPrintConfig &config,
_("Everywhere"));
}
static wxString pad_combo_value_for_config(const DynamicPrintConfig &config)
{
return config.opt_bool("pad_enable") ? (config.opt_bool("pad_zero_elevation") ? _("Around object") : _("Below object")) : _("None");
}
void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
{
if (wxGetApp().plater() == nullptr) {
@ -853,6 +860,9 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
(opt_key == "supports_enable" || opt_key == "support_buildplate_only"))
og_freq_chng_params->set_value("support", support_combo_value_for_config(*m_config, is_fff));
if (! is_fff && (opt_key == "pad_enable" || opt_key == "pad_zero_elevation"))
og_freq_chng_params->set_value("pad", pad_combo_value_for_config(*m_config));
if (opt_key == "brim_width")
{
bool val = m_config->opt_float("brim_width") > 0.0 ? true : false;
@ -987,6 +997,8 @@ void Tab::update_frequently_changed_parameters()
if (!og_freq_chng_params) return;
og_freq_chng_params->set_value("support", support_combo_value_for_config(*m_config, is_fff));
if (! is_fff)
og_freq_chng_params->set_value("pad", pad_combo_value_for_config(*m_config));
const std::string updated_value_key = is_fff ? "fill_density" : "pad_enable";
@ -1758,6 +1770,17 @@ void TabFilament::reload_config()
Tab::reload_config();
}
void TabFilament::update_volumetric_flow_preset_hints()
{
wxString text;
try {
text = from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle));
} catch (std::exception &ex) {
text = _(L("Volumetric flow hints not available\n\n")) + from_u8(ex.what());
}
m_volumetric_speed_description_line->SetText(text);
}
void TabFilament::update()
{
if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA)
@ -1767,8 +1790,7 @@ void TabFilament::update()
wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset()));
m_cooling_description_line->SetText(text);
text = from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle));
m_volumetric_speed_description_line->SetText(text);
this->update_volumetric_flow_preset_hints();
Layout();
bool cooling = m_config->opt_bool("cooling", 0);
@ -1790,7 +1812,7 @@ void TabFilament::update()
void TabFilament::OnActivate()
{
m_volumetric_speed_description_line->SetText(from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle)));
this->update_volumetric_flow_preset_hints();
Tab::OnActivate();
}
@ -2025,14 +2047,17 @@ void TabPrinter::build_fff()
"Do you want to change the diameter for all extruders to first extruder nozzle diameter value?"));
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Nozzle diameter")), wxICON_WARNING | wxYES_NO);
if (dialog->ShowModal() == wxID_YES) {
DynamicPrintConfig new_conf = *m_config;
if (dialog->ShowModal() == wxID_YES) {
for (size_t i = 1; i < nozzle_diameters.size(); i++)
nozzle_diameters[i] = frst_diam;
new_conf.set_key_value("nozzle_diameter", new ConfigOptionFloats(nozzle_diameters));
load_config(new_conf);
}
else
new_conf.set_key_value("single_extruder_multi_material", new ConfigOptionBool(false));
load_config(new_conf);
break;
}
}
@ -2480,7 +2505,8 @@ void TabPrinter::build_unregular_pages()
// if value was changed
if (fabs(nozzle_diameters[extruder_idx == 0 ? 1 : 0] - new_nd) > EPSILON)
{
const wxString msg_text = _(L("Do you want to change the diameter for all extruders?"));
const wxString msg_text = _(L("This is a single extruder multimaterial printer, diameters of all extruders "
"will be set to the new value. Do you want to proceed?"));
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Nozzle diameter")), wxICON_WARNING | wxYES_NO);
DynamicPrintConfig new_conf = *m_config;
@ -3691,6 +3717,7 @@ void TabSLAPrint::build()
// optgroup->append_single_option_line("pad_edge_radius");
optgroup->append_single_option_line("pad_wall_slope");
optgroup->append_single_option_line("pad_zero_elevation");
optgroup->append_single_option_line("pad_object_gap");
optgroup->append_single_option_line("pad_object_connector_stride");
optgroup->append_single_option_line("pad_object_connector_width");
@ -3740,6 +3767,32 @@ void TabSLAPrint::update()
m_update_cnt++;
bool supports_en = m_config->opt_bool("supports_enable");
get_field("support_head_front_diameter")->toggle(supports_en);
get_field("support_head_penetration")->toggle(supports_en);
get_field("support_head_width")->toggle(supports_en);
get_field("support_pillar_diameter")->toggle(supports_en);
get_field("support_pillar_connection_mode")->toggle(supports_en);
get_field("support_buildplate_only")->toggle(supports_en);
get_field("support_base_diameter")->toggle(supports_en);
get_field("support_base_height")->toggle(supports_en);
get_field("support_base_safety_distance")->toggle(supports_en);
get_field("support_critical_angle")->toggle(supports_en);
get_field("support_max_bridge_length")->toggle(supports_en);
get_field("support_max_pillar_link_distance")->toggle(supports_en);
get_field("support_points_density_relative")->toggle(supports_en);
get_field("support_points_minimal_distance")->toggle(supports_en);
bool pad_en = m_config->opt_bool("pad_enable");
get_field("pad_wall_thickness")->toggle(pad_en);
get_field("pad_wall_height")->toggle(pad_en);
get_field("pad_max_merge_distance")->toggle(pad_en);
// get_field("pad_edge_radius")->toggle(supports_en);
get_field("pad_wall_slope")->toggle(pad_en);
get_field("pad_zero_elevation")->toggle(pad_en);
double head_penetration = m_config->opt_float("support_head_penetration");
double head_width = m_config->opt_float("support_head_width");
if (head_penetration > head_width) {
@ -3780,13 +3833,14 @@ void TabSLAPrint::update()
load_config(new_conf);
}
// if(m_config->opt_float("support_object_elevation") < EPSILON &&
// m_config->opt_bool("pad_enable")) {
// // TODO: disable editding of:
// // pad_object_connector_stride
// // pad_object_connector_width
// // pad_object_connector_penetration
// }
bool has_suppad = pad_en && supports_en;
bool zero_elev = m_config->opt_bool("pad_zero_elevation") && has_suppad;
get_field("support_object_elevation")->toggle(supports_en && !zero_elev);
get_field("pad_object_gap")->toggle(zero_elev);
get_field("pad_object_connector_stride")->toggle(zero_elev);
get_field("pad_object_connector_width")->toggle(zero_elev);
get_field("pad_object_connector_penetration")->toggle(zero_elev);
m_update_cnt--;

View file

@ -218,7 +218,7 @@ protected:
int m_em_unit;
// To avoid actions with no-completed Tab
bool m_complited { false };
ConfigOptionMode m_mode = comSimple;
ConfigOptionMode m_mode = comExpert; // to correct first Tab update_visibility() set mode to Expert
public:
PresetBundle* m_preset_bundle;
@ -340,6 +340,7 @@ class TabFilament : public Tab
void add_filament_overrides_page();
void update_filament_overrides_page();
void update_volumetric_flow_preset_hints();
std::map<std::string, wxCheckBox*> m_overrides_options;
public:

View file

@ -157,6 +157,24 @@ wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string,
return item;
}
wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
std::function<void(wxCommandEvent& event)> cb, wxEvtHandler* event_handler)
{
if (id == wxID_ANY)
id = wxNewId();
wxMenuItem* item = menu->AppendCheckItem(id, string, description);
#ifdef __WXMSW__
if (event_handler != nullptr && event_handler != menu)
event_handler->Bind(wxEVT_MENU, cb, id);
else
#endif // __WXMSW__
menu->Bind(wxEVT_MENU, cb, id);
return item;
}
const unsigned int wxCheckListBoxComboPopup::DefaultWidth = 200;
const unsigned int wxCheckListBoxComboPopup::DefaultHeight = 200;
const unsigned int wxCheckListBoxComboPopup::DefaultItemHeight = 18;
@ -432,6 +450,16 @@ wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in,
// ObjectDataViewModelNode
// ----------------------------------------------------------------------------
void ObjectDataViewModelNode::init_container()
{
#ifdef __WXGTK__
// it's necessary on GTK because of control have to know if this item will be container
// in another case you couldn't to add subitem for this item
// it will be produce "segmentation fault"
m_container = true;
#endif //__WXGTK__
}
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type) :
m_parent(parent),
m_type(type),
@ -454,13 +482,8 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent
m_name = _(L("Layers"));
}
#ifdef __WXGTK__
// it's necessary on GTK because of control have to know if this item will be container
// in another case you couldn't to add subitem for this item
// it will be produce "segmentation fault"
if (type & (itInstanceRoot | itLayerRoot))
m_container = true;
#endif //__WXGTK__
init_container();
}
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
@ -486,14 +509,8 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent
m_name = _(L("Range")) + label_range + "(" + _(L("mm")) + ")";
m_bmp = create_scaled_bitmap(nullptr, "edit_layers_some"); // FIXME: pass window ptr
#ifdef __WXGTK__
// it's necessary on GTK because of control have to know if this item will be container
// in another case you couldn't to add subitem for this item
// it will be produce "segmentation fault"
m_container = true;
#endif //__WXGTK__
set_action_icon();
init_container();
}
#ifndef NDEBUG
@ -512,6 +529,13 @@ void ObjectDataViewModelNode::set_action_icon()
m_action_icon = create_scaled_bitmap(nullptr, m_action_icon_name); // FIXME: pass window ptr
}
void ObjectDataViewModelNode::set_printable_icon(PrintIndicator printable)
{
m_printable = printable;
m_printable_icon = m_printable == piUndef ? m_empty_bmp :
create_scaled_bitmap(nullptr, m_printable == piPrintable ? "eye_open.png" : "eye_closed.png");
}
Slic3r::GUI::BitmapCache *m_bitmap_cache = nullptr;
void ObjectDataViewModelNode::update_settings_digest_bitmaps()
{
@ -565,17 +589,20 @@ bool ObjectDataViewModelNode::SetValue(const wxVariant& variant, unsigned col)
{
switch (col)
{
case 0: {
case colPrint:
m_printable_icon << variant;
return true;
case colName: {
DataViewBitmapText data;
data << variant;
m_bmp = data.GetBitmap();
m_name = data.GetText();
return true; }
case 1: {
case colExtruder: {
const wxString & val = variant.GetString();
m_extruder = val == "0" ? _(L("default")) : val;
return true; }
case 2:
case colEditing:
m_action_icon << variant;
return true;
default:
@ -735,26 +762,60 @@ static bool append_root_node(ObjectDataViewModelNode *parent_node,
return false;
}
wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem &parent_item, size_t num)
wxDataViewItem ObjectDataViewModel::AddRoot(const wxDataViewItem &parent_item, ItemType root_type)
{
ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID();
if (!parent_node) return wxDataViewItem(0);
// get InstanceRoot node
ObjectDataViewModelNode *inst_root_node { nullptr };
ObjectDataViewModelNode *root_node { nullptr };
const bool appended = append_root_node(parent_node, &root_node, root_type);
if (!root_node) return wxDataViewItem(0);
const bool appended = append_root_node(parent_node, &inst_root_node, itInstanceRoot);
const wxDataViewItem inst_root_item((void*)inst_root_node);
if (!inst_root_node) return wxDataViewItem(0);
const wxDataViewItem root_item((void*)root_node);
if (appended)
ItemAdded(parent_item, inst_root_item);// notify control
ItemAdded(parent_item, root_item);// notify control
return root_item;
}
wxDataViewItem ObjectDataViewModel::AddInstanceRoot(const wxDataViewItem &parent_item)
{
return AddRoot(parent_item, itInstanceRoot);
}
wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem &parent_item, size_t num)
{
const std::vector<bool> print_indicator(num, true);
return wxDataViewItem((void*)AddInstanceChild(parent_item, print_indicator));
}
wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem& parent_item,
const std::vector<bool>& print_indicator)
{
const wxDataViewItem inst_root_item = AddInstanceRoot(parent_item);
if (!inst_root_item) return wxDataViewItem(0);
ObjectDataViewModelNode* inst_root_node = (ObjectDataViewModelNode*)inst_root_item.GetID();
const bool just_created = inst_root_node->GetChildren().Count() == 0;
// Add instance nodes
ObjectDataViewModelNode *instance_node = nullptr;
size_t counter = 0;
while (counter < num) {
while (counter < print_indicator.size()) {
instance_node = new ObjectDataViewModelNode(inst_root_node, itInstance);
// if InstanceRoot item is just created and start to adding Instances
if (just_created && counter == 0) {
ObjectDataViewModelNode* obj_node = (ObjectDataViewModelNode*)parent_item.GetID();
// use object's printable state to first instance
instance_node->set_printable_icon(obj_node->IsPrintable());
}
else
instance_node->set_printable_icon(print_indicator[counter] ? piPrintable : piUnprintable);
inst_root_node->Append(instance_node);
// notify control
const wxDataViewItem instance_item((void*)instance_node);
@ -762,25 +823,67 @@ wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem &paren
++counter;
}
// update object_node printable property
UpdateObjectPrintable(parent_item);
return wxDataViewItem((void*)instance_node);
}
void ObjectDataViewModel::UpdateObjectPrintable(wxDataViewItem parent_item)
{
const wxDataViewItem inst_root_item = GetInstanceRootItem(parent_item);
if (!inst_root_item)
return;
ObjectDataViewModelNode* inst_root_node = (ObjectDataViewModelNode*)inst_root_item.GetID();
const size_t child_cnt = inst_root_node->GetChildren().Count();
PrintIndicator obj_pi = piUnprintable;
for (size_t i=0; i < child_cnt; i++)
if (inst_root_node->GetNthChild(i)->IsPrintable() & piPrintable) {
obj_pi = piPrintable;
break;
}
// and set printable state for object_node to piUndef
ObjectDataViewModelNode* obj_node = (ObjectDataViewModelNode*)parent_item.GetID();
obj_node->set_printable_icon(obj_pi);
ItemChanged(parent_item);
}
// update printable property for all instances from object
void ObjectDataViewModel::UpdateInstancesPrintable(wxDataViewItem parent_item)
{
const wxDataViewItem inst_root_item = GetInstanceRootItem(parent_item);
if (!inst_root_item)
return;
ObjectDataViewModelNode* obj_node = (ObjectDataViewModelNode*)parent_item.GetID();
const PrintIndicator obj_pi = obj_node->IsPrintable();
ObjectDataViewModelNode* inst_root_node = (ObjectDataViewModelNode*)inst_root_item.GetID();
const size_t child_cnt = inst_root_node->GetChildren().Count();
for (size_t i=0; i < child_cnt; i++)
{
ObjectDataViewModelNode* inst_node = inst_root_node->GetNthChild(i);
// and set printable state for object_node to piUndef
inst_node->set_printable_icon(obj_pi);
ItemChanged(wxDataViewItem((void*)inst_node));
}
}
bool ObjectDataViewModel::IsPrintable(const wxDataViewItem& item) const
{
ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID();
if (!node)
return false;
return node->IsPrintable() == piPrintable;
}
wxDataViewItem ObjectDataViewModel::AddLayersRoot(const wxDataViewItem &parent_item)
{
ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID();
if (!parent_node) return wxDataViewItem(0);
// get LayerRoot node
ObjectDataViewModelNode *layer_root_node{ nullptr };
const bool appended = append_root_node(parent_node, &layer_root_node, itLayerRoot);
if (!layer_root_node) return wxDataViewItem(0);
const wxDataViewItem layer_root_item((void*)layer_root_node);
if (appended)
ItemAdded(parent_item, layer_root_item);// notify control
return layer_root_item;
return AddRoot(parent_item, itLayerRoot);
}
wxDataViewItem ObjectDataViewModel::AddLayersChild(const wxDataViewItem &parent_item,
@ -878,11 +981,13 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
ItemDeleted(parent, item);
ObjectDataViewModelNode *last_instance_node = node_parent->GetNthChild(0);
PrintIndicator last_instance_printable = last_instance_node->IsPrintable();
node_parent->GetChildren().Remove(last_instance_node);
delete last_instance_node;
ItemDeleted(parent, wxDataViewItem(last_instance_node));
ObjectDataViewModelNode *obj_node = node_parent->GetParent();
obj_node->set_printable_icon(last_instance_printable);
obj_node->GetChildren().Remove(node_parent);
delete node_parent;
ret_item = wxDataViewItem(obj_node);
@ -895,6 +1000,9 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
return ret_item;
}
if (node->m_type & itInstance)
UpdateObjectPrintable(wxDataViewItem(node_parent->GetParent()));
// if there was last layer item, delete this one and layers root item
if (node_parent->GetChildCount() == 0 && node_parent->m_type == itLayerRoot)
{
@ -1004,9 +1112,12 @@ wxDataViewItem ObjectDataViewModel::DeleteLastInstance(const wxDataViewItem &par
const int inst_cnt = inst_root_node->GetChildCount();
const bool delete_inst_root_item = inst_cnt - num < 2 ? true : false;
PrintIndicator last_inst_printable = piUndef;
int stop = delete_inst_root_item ? 0 : inst_cnt - num;
for (int i = inst_cnt - 1; i >= stop;--i) {
ObjectDataViewModelNode *last_instance_node = inst_root_node->GetNthChild(i);
if (i==0) last_inst_printable = last_instance_node->IsPrintable();
inst_root_node->GetChildren().Remove(last_instance_node);
delete last_instance_node;
ItemDeleted(inst_root_item, wxDataViewItem(last_instance_node));
@ -1015,13 +1126,18 @@ wxDataViewItem ObjectDataViewModel::DeleteLastInstance(const wxDataViewItem &par
if (delete_inst_root_item) {
ret_item = parent_item;
parent_node->GetChildren().Remove(inst_root_node);
parent_node->set_printable_icon(last_inst_printable);
ItemDeleted(parent_item, inst_root_item);
ItemChanged(parent_item);
#ifndef __WXGTK__
if (parent_node->GetChildCount() == 0)
parent_node->m_container = false;
#endif //__WXGTK__
}
// update object_node printable property
UpdateObjectPrintable(parent_item);
return ret_item;
}
@ -1325,6 +1441,18 @@ int ObjectDataViewModel::GetRowByItem(const wxDataViewItem& item) const
return -1;
}
bool ObjectDataViewModel::InvalidItem(const wxDataViewItem& item)
{
if (!item)
return true;
ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID();
if (!node || node->invalid())
return true;
return false;
}
wxString ObjectDataViewModel::GetName(const wxDataViewItem &item) const
{
ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID();
@ -1347,13 +1475,16 @@ void ObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &ite
ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID();
switch (col)
{
case 0:
case colPrint:
variant << node->m_printable_icon;
break;
case colName:
variant << DataViewBitmapText(node->m_name, node->m_bmp);
break;
case 1:
case colExtruder:
variant = node->m_extruder;
break;
case 2:
case colEditing:
variant << node->m_action_icon;
break;
default:
@ -1416,7 +1547,7 @@ bool ObjectDataViewModel::IsEnabled(const wxDataViewItem &item, unsigned int col
ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID();
// disable extruder selection for the non "itObject|itVolume" item
return !(col == 1 && node->m_extruder.IsEmpty());
return !(col == colExtruder && node->m_extruder.IsEmpty());
}
wxDataViewItem ObjectDataViewModel::GetParent(const wxDataViewItem &item) const
@ -1581,6 +1712,46 @@ void ObjectDataViewModel::SetVolumeType(const wxDataViewItem &item, const Slic3r
ItemChanged(item);
}
wxDataViewItem ObjectDataViewModel::SetPrintableState(
PrintIndicator printable,
int obj_idx,
int subobj_idx /* = -1*/,
ItemType subobj_type/* = itInstance*/)
{
wxDataViewItem item = wxDataViewItem(0);
if (subobj_idx < 0)
item = GetItemById(obj_idx);
else
item = subobj_type&itInstance ? GetItemByInstanceId(obj_idx, subobj_idx) :
GetItemByVolumeId(obj_idx, subobj_idx);
ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID();
if (!node)
return wxDataViewItem(0);
node->set_printable_icon(printable);
ItemChanged(item);
if (subobj_idx >= 0)
UpdateObjectPrintable(GetItemById(obj_idx));
return item;
}
wxDataViewItem ObjectDataViewModel::SetObjectPrintableState(
PrintIndicator printable,
wxDataViewItem obj_item)
{
ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)obj_item.GetID();
if (!node)
return wxDataViewItem(0);
node->set_printable_icon(printable);
ItemChanged(obj_item);
UpdateInstancesPrintable(obj_item);
return obj_item;
}
void ObjectDataViewModel::Rescale()
{
wxDataViewItemArray all_items;
@ -1765,7 +1936,7 @@ bool BitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value
// The icon can't be edited so get its old value and reuse it.
wxVariant valueOld;
GetView()->GetModel()->GetValue(valueOld, m_item, 0);
GetView()->GetModel()->GetValue(valueOld, m_item, colName);
DataViewBitmapText bmpText;
bmpText << valueOld;

View file

@ -44,6 +44,9 @@ wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxStrin
wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
std::function<void(wxCommandEvent& event)> cb, wxEvtHandler* event_handler);
wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
std::function<void(wxCommandEvent& event)> cb, wxEvtHandler* event_handler);
class wxDialog;
void edit_tooltip(wxString& tooltip);
void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector<int>& btn_ids);
@ -173,6 +176,21 @@ enum ItemType {
itLayer = 64,
};
enum ColumnNumber
{
colName = 0, // item name
colPrint , // printable property
colExtruder , // extruder selection
colEditing , // item editing
};
enum PrintIndicator
{
piUndef = 0, // no print indicator
piPrintable , // printable
piUnprintable , // unprintable
};
class ObjectDataViewModelNode;
WX_DEFINE_ARRAY_PTR(ObjectDataViewModelNode*, MyObjectTreeModelNodePtrArray);
@ -192,6 +210,8 @@ class ObjectDataViewModelNode
bool m_container = false;
wxString m_extruder = "default";
wxBitmap m_action_icon;
PrintIndicator m_printable {piUndef};
wxBitmap m_printable_icon;
std::string m_action_icon_name = "";
Slic3r::ModelVolumeType m_volume_type;
@ -204,14 +224,8 @@ public:
m_type(itObject),
m_extruder(extruder)
{
#ifdef __WXGTK__
// it's necessary on GTK because of control have to know if this item will be container
// in another case you couldn't to add subitem for this item
// it will be produce "segmentation fault"
m_container = true;
#endif //__WXGTK__
set_action_icon();
init_container();
}
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
@ -226,14 +240,8 @@ public:
m_extruder (extruder)
{
m_bmp = bmp;
#ifdef __WXGTK__
// it's necessary on GTK because of control have to know if this item will be container
// in another case you couldn't to add subitem for this item
// it will be produce "segmentation fault"
m_container = true;
#endif //__WXGTK__
set_action_icon();
init_container();
}
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
@ -258,6 +266,7 @@ public:
#endif /* NDEBUG */
}
void init_container();
bool IsContainer() const
{
return m_container;
@ -316,6 +325,7 @@ public:
void SetIdx(const int& idx);
int GetIdx() const { return m_idx; }
t_layer_height_range GetLayerRange() const { return m_layer_range; }
PrintIndicator IsPrintable() const { return m_printable; }
// use this function only for childrens
void AssignAllVal(ObjectDataViewModelNode& from_node)
@ -347,6 +357,8 @@ public:
// Set action icons for node
void set_action_icon();
// Set printable icon for node
void set_printable_icon(PrintIndicator printable);
void update_settings_digest_bitmaps();
bool update_settings_digest(const std::vector<std::string>& categories);
@ -356,6 +368,7 @@ public:
#ifndef NDEBUG
bool valid();
#endif /* NDEBUG */
bool invalid() const { return m_idx < -1; }
private:
friend class ObjectDataViewModel;
@ -391,6 +404,7 @@ public:
const bool create_frst_child = true);
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num);
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, const std::vector<bool>& print_indicator);
wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item);
wxDataViewItem AddLayersChild( const wxDataViewItem &parent_item,
const t_layer_height_range& layer_range,
@ -418,6 +432,7 @@ public:
void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx);
int GetRowByItem(const wxDataViewItem& item) const;
bool IsEmpty() { return m_objects.empty(); }
bool InvalidItem(const wxDataViewItem& item);
// helper method for wxLog
@ -468,9 +483,17 @@ public:
void UpdateSettingsDigest( const wxDataViewItem &item,
const std::vector<std::string>& categories);
bool IsPrintable(const wxDataViewItem &item) const;
void UpdateObjectPrintable(wxDataViewItem parent_item);
void UpdateInstancesPrintable(wxDataViewItem parent_item);
void SetVolumeBitmaps(const std::vector<wxBitmap*>& volume_bmps) { m_volume_bmps = volume_bmps; }
void SetWarningBitmap(wxBitmap* bitmap) { m_warning_bmp = bitmap; }
void SetVolumeType(const wxDataViewItem &item, const Slic3r::ModelVolumeType type);
wxDataViewItem SetPrintableState( PrintIndicator printable, int obj_idx,
int subobj_idx = -1,
ItemType subobj_type = itInstance);
wxDataViewItem SetObjectPrintableState(PrintIndicator printable, wxDataViewItem obj_item);
void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; }
// Rescale bitmaps for existing Items
@ -480,6 +503,10 @@ public:
const bool is_marked = false);
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const;
private:
wxDataViewItem AddRoot(const wxDataViewItem& parent_item, const ItemType root_type);
wxDataViewItem AddInstanceRoot(const wxDataViewItem& parent_item);
};
// ----------------------------------------------------------------------------

View file

@ -353,6 +353,8 @@ void Serial::set_baud_rate(unsigned baud_rate)
}
}
/*
void Serial::set_DTR(bool on)
{
auto handle = native_handle();
@ -495,6 +497,7 @@ std::string Serial::printer_format_line(const std::string &line, unsigned line_n
return (boost::format("N%1% %2%*%3%\n") % line_num_str % line % checksum).str();
}
*/
} // namespace Utils

View file

@ -46,6 +46,17 @@ public:
~Serial();
void set_baud_rate(unsigned baud_rate);
// The Serial implementation is currently in disarray and therefore commented out.
// The boost implementation seems to have several problems, such as lack of support
// for custom baud rates, few weird implementation bugs and a history of API breakages.
// It's questionable whether it solves more problems than causes. Probably not.
// TODO: Custom implementation not based on asio.
//
// As of now, this class is only kept for the purpose of rebooting AVR109,
// see FirmwareDialog::priv::avr109_reboot()
/*
void set_DTR(bool on);
// Resets the line number both internally as well as with the firmware using M110
@ -76,6 +87,7 @@ public:
static std::string printer_format_line(const std::string &line, unsigned line_num);
private:
unsigned m_line_num = 0;
*/
};

View file

@ -3,7 +3,7 @@
set(SLIC3R_APP_NAME "PrusaSlicer")
set(SLIC3R_APP_KEY "PrusaSlicer")
set(SLIC3R_VERSION "2.1.0-alpha0")
set(SLIC3R_VERSION "2.1.0-alpha1")
set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN")
set(SLIC3R_RC_VERSION "2,1,0,0")
set(SLIC3R_RC_VERSION_DOTS "2.1.0.0")

View file

@ -44,7 +44,7 @@ ok !$point->coincides_with($point2), 'coincides_with';
{
my $line = Slic3r::Line->new([50,50], [125,-25]);
is +Slic3r::Point->new(100,0)->distance_to_line($line), 0, 'distance_to_line()';
cmp_ok(abs(Slic3r::Point->new(100,0)->distance_to_line($line)), '<=', 4e-15, 'distance_to_line()');
}
{