Merge branch 'master' into lm_tm_hollowing

This commit is contained in:
tamasmeszaros 2020-01-23 17:43:18 +01:00
commit b45e95877e
59 changed files with 3532 additions and 2470 deletions

View File

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg8"
version="1.1"
viewBox="0 0 3.7041666 3.7041667"
height="3.7041667mm"
width="3.7041667mm">
<defs
id="defs2">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath3733">
<path
d="M 0,800 H 800 V 0 H 0 Z"
id="path3731" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath4697">
<path
d="M 0,800 H 800 V 0 H 0 Z"
id="path4695" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath4697-9">
<path
d="M 0,800 H 800 V 0 H 0 Z"
id="path4695-2" />
</clipPath>
</defs>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(294.04601,-97.736694)"
id="layer1">
<g
transform="translate(-265.33929,12.851203)"
id="layer1-0">
<g
transform="matrix(1.31769,0,0,1.31769,-167.28747,-111.35623)"
id="layer1-3">
<g
id="g4685"
transform="matrix(0.00352778,0,0,-0.00352778,105.26858,151.76571)">
<path
d="M 381.663,302.607 H 558.791 V 65.846 H 381.663 Z"
style="fill:#d8d8db;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4687" />
<path
d="m 470.227,302.607 h 95.411 V 65.846 h -95.411 z"
style="fill:#f7f7f8;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4689" />
<g
id="g4691">
<g
id="g4693"
clip-path="url(#clipPath4697-9)">
<g
id="g4699"
transform="translate(380.7793,225.1963)">
<path
d="m 0,0 h 174.037 c 39.032,0 70.675,31.643 70.675,70.675 v 372.92 c 0,39.033 -31.643,70.675 -70.675,70.675 H 0 c -39.033,0 -70.675,-31.642 -70.675,-70.675 V 70.675 C -70.675,31.643 -39.033,0 0,0"
style="fill:#e96700;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4701" />
</g>
<g
id="g4703"
transform="translate(377.0244,168.7666)">
<path
d="m 0,0 h -237.865 c -8.782,0 -15.902,7.12 -15.902,15.902 v 365.743 c 0,8.782 7.12,15.901 15.902,15.901 H 28.933 c 8.782,0 15.902,-7.119 15.902,-15.901 V 56.43 C 38.907,48.507 9.055,8.503 0,0"
style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4705" />
</g>
<g
id="g4707"
transform="translate(421.8594,225.1963)">
<path
d="M 0,0 C -7.399,-10.049 -35.78,-47.927 -44.835,-56.43 H -282.7"
style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4709" />
</g>
<g
id="g4711"
transform="translate(421.8594,225.1963)">
<path
d="M 0,0 C -5.876,-7.899 -35.78,-47.927 -44.835,-56.43"
style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4713" />
</g>
<g
id="g4715"
transform="translate(333.957,198.1406)">
<path
d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 81.276 c 0,2.195 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.781 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0"
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4717" />
</g>
<g
id="g4719"
transform="translate(297.957,198.1406)">
<path
d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 81.276 c 0,2.195 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.781 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0"
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4721" />
</g>
<g
id="g4723"
transform="translate(261.957,198.1406)">
<path
d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 81.276 c 0,2.195 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.781 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0"
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4725" />
</g>
<g
id="g4727"
transform="translate(225.957,198.1406)">
<path
d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 81.276 c 0,2.195 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.781 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0"
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4729" />
</g>
<g
id="g4731"
transform="translate(189.957,198.1406)">
<path
d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 81.276 c 0,2.195 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.781 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0"
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4733" />
</g>
<g
id="g4735"
transform="translate(369.2207,225.1963)">
<path
d="m 0,0 h -22.969 c -2.196,0 -3.976,1.78 -3.976,3.976 v 54.22 c 0,2.196 1.78,3.976 3.976,3.976 H 0 c 2.196,0 3.976,-1.78 3.976,-3.976 V 3.976 C 3.976,1.78 2.196,0 0,0"
style="fill:#efee86;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4737" />
</g>
<path
d="m 450.019,104.056 h -50.577 v 33.128 h 50.577 z"
style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4739" />
<path
d="m 543.221,104.056 h -50.576 v 33.128 h 50.576 z"
style="fill:#666666;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4741" />
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -4914,7 +4914,7 @@ msgid "Cannot overwrite an external profile."
msgstr ""
#: src/slic3r/GUI/Tab.cpp:3038
msgid "Preset with name \"%1%\" already exist."
msgid "Preset with name \"%1%\" already exists."
msgstr ""
#: src/slic3r/GUI/Tab.cpp:3039

View File

@ -46,6 +46,7 @@ src/slic3r/GUI/Tab.hpp
src/slic3r/GUI/UpdateDialogs.cpp
src/slic3r/GUI/WipeTowerDialog.cpp
src/slic3r/GUI/wxExtensions.cpp
src/slic3r/GUI/DoubleSlider.cpp
src/slic3r/GUI/ExtruderSequenceDialog.cpp
src/slic3r/Utils/Duet.cpp
src/slic3r/Utils/OctoPrint.cpp

View File

@ -1,3 +1,8 @@
min_slic3r_version = 2.2.0-alpha3
1.1.1-alpha3 Print bed textures are now configurable from the Preset Bundle. Requires PrusaSlicer 2.2.0-alpha3 and newer.
# The following line (max_slic3r_version) forces the users of PrusaSlicer 2.2.0-alpha3 and newer to update the profiles to 1.1.1-alpha3 and newer,
# so they will see the print bed.
max_slic3r_version = 2.2.0-alpha2
min_slic3r_version = 2.2.0-alpha0
1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles.
1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0

View File

@ -5,7 +5,7 @@
name = Prusa Research
# Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded.
config_version = 1.1.1-alpha2
config_version = 1.1.1-alpha3
# Where to get the updates from?
config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
changelog_url = http://files.prusa3d.com/?latest=slicer-profiles&lng=%1%

View File

@ -84,7 +84,7 @@ endif (MINGW)
if (NOT WIN32)
# Binary name on unix like systems (OSX, Linux)
set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
endif ()
target_link_libraries(PrusaSlicer libslic3r cereal)

View File

@ -119,6 +119,8 @@ add_library(libslic3r STATIC
Line.hpp
Model.cpp
Model.hpp
CustomGCode.cpp
CustomGCode.hpp
Arrange.hpp
Arrange.cpp
MotionPlanner.cpp

View File

@ -0,0 +1,72 @@
#include "CustomGCode.hpp"
#include "Config.hpp"
#include "GCode/PreviewData.hpp"
#include "GCodeWriter.hpp"
namespace Slic3r {
namespace CustomGCode {
// If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer),
// and if CustomGCode::Info.gcodes is empty (there is no color print data available in a new format
// then CustomGCode::Info.gcodes should be updated considering this option.
extern void update_custom_gcode_per_print_z_from_config(Info& info, DynamicPrintConfig* config)
{
auto *colorprint_heights = config->option<ConfigOptionFloats>("colorprint_heights");
if (colorprint_heights == nullptr)
return;
if (info.gcodes.empty() && ! colorprint_heights->values.empty()) {
// Convert the old colorprint_heighs only if there is no equivalent data in a new format.
const std::vector<std::string>& colors = GCodePreviewData::ColorPrintColors();
const auto& colorprint_values = colorprint_heights->values;
info.gcodes.clear();
info.gcodes.reserve(colorprint_values.size());
int i = 0;
for (auto val : colorprint_values)
info.gcodes.emplace_back(Item{ val, ColorChangeCode, 1, colors[(++i)%7] });
info.mode = SingleExtruder;
}
// The "colorprint_heights" config value has been deprecated. At this point of time it has been converted
// to a new format and therefore it shall be erased.
config->erase("colorprint_heights");
}
// If information for custom Gcode per print Z was imported from older Slicer, mode will be undefined.
// So, we should set CustomGCode::Info.mode should be updated considering code values from items.
extern void check_mode_for_custom_gcode_per_print_z(Info& info)
{
if (info.mode != Undef)
return;
bool is_single_extruder = true;
for (auto item : info.gcodes)
{
if (item.gcode == ToolChangeCode) {
info.mode = MultiAsSingle;
return;
}
if (item.gcode == ColorChangeCode && item.extruder > 1)
is_single_extruder = false;
}
info.mode = is_single_extruder ? SingleExtruder : MultiExtruder;
}
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
// print_z corresponds to the first layer printed with the new extruder.
std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders)
{
std::vector<std::pair<double, unsigned int>> custom_tool_changes;
for (const Item& custom_gcode : custom_gcode_per_print_z.gcodes)
if (custom_gcode.gcode == ToolChangeCode) {
// If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast<unsigned int>(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder));
}
return custom_tool_changes;
}
} // namespace CustomGCode
} // namespace Slic3r

View File

@ -0,0 +1,86 @@
#ifndef slic3r_CustomGCode_hpp_
#define slic3r_CustomGCode_hpp_
#include <string>
#include <vector>
namespace Slic3r {
class DynamicPrintConfig;
// Additional Codes which can be set by user using DoubleSlider
static constexpr char ColorChangeCode[] = "M600";
static constexpr char PausePrintCode[] = "M601";
static constexpr char ToolChangeCode[] = "tool_change";
namespace CustomGCode {
struct Item
{
bool operator<(const Item& rhs) const { return this->print_z < rhs.print_z; }
bool operator==(const Item& rhs) const
{
return (rhs.print_z == this->print_z ) &&
(rhs.gcode == this->gcode ) &&
(rhs.extruder == this->extruder ) &&
(rhs.color == this->color );
}
bool operator!=(const Item& rhs) const { return ! (*this == rhs); }
double print_z;
std::string gcode;
int extruder; // Informative value for ColorChangeCode and ToolChangeCode
// "gcode" == ColorChangeCode => M600 will be applied for "extruder" extruder
// "gcode" == ToolChangeCode => for whole print tool will be switched to "extruder" extruder
std::string color; // if gcode is equal to PausePrintCode,
// this field is used for save a short message shown on Printer display
};
enum Mode
{
Undef,
SingleExtruder, // Single extruder printer preset is selected
MultiAsSingle, // Multiple extruder printer preset is selected, but
// this mode works just for Single extruder print
// (For all print from objects settings is used just one extruder)
MultiExtruder // Multiple extruder printer preset is selected
};
// string anlogue of custom_code_per_height mode
static constexpr char SingleExtruderMode[] = "SingleExtruder";
static constexpr char MultiAsSingleMode [] = "MultiAsSingle";
static constexpr char MultiExtruderMode [] = "MultiExtruder";
struct Info
{
Mode mode = Undef;
std::vector<Item> gcodes;
bool operator==(const Info& rhs) const
{
return (rhs.mode == this->mode ) &&
(rhs.gcodes == this->gcodes );
}
bool operator!=(const Info& rhs) const { return !(*this == rhs); }
};
// If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer),
// and if CustomGCode::Info.gcodes is empty (there is no color print data available in a new format
// then CustomGCode::Info.gcodes should be updated considering this option.
extern void update_custom_gcode_per_print_z_from_config(Info& info, DynamicPrintConfig* config);
// If information for custom Gcode per print Z was imported from older Slicer, mode will be undefined.
// So, we should set CustomGCode::Info.mode should be updated considering code values from items.
extern void check_mode_for_custom_gcode_per_print_z(Info& info);
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
// print_z corresponds to the first layer printed with the new extruder.
std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders);
} // namespace CustomGCode
} // namespace Slic3r
#endif /* slic3r_CustomGCode_hpp_ */

View File

@ -1191,6 +1191,14 @@ namespace Slic3r {
for (const auto& code : code_tree)
{
if (code.first == "mode")
{
pt::ptree tree = code.second;
std::string mode = tree.get<std::string>("<xmlattr>.value");
m_model->custom_gcode_per_print_z.mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder :
mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle :
CustomGCode::Mode::MultiExtruder;
}
if (code.first != "code")
continue;
pt::ptree tree = code.second;
@ -1199,7 +1207,7 @@ namespace Slic3r {
int extruder = tree.get<int> ("<xmlattr>.extruder" );
std::string color = tree.get<std::string> ("<xmlattr>.color" );
m_model->custom_gcode_per_print_z.gcodes.push_back(Model::CustomGCode{print_z, gcode, extruder, color}) ;
m_model->custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, gcode, extruder, color}) ;
}
}
}
@ -2767,7 +2775,7 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
pt::ptree tree;
pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", "");
for (const Model::CustomGCode& code : model.custom_gcode_per_print_z.gcodes)
for (const CustomGCode::Item& code : model.custom_gcode_per_print_z.gcodes)
{
pt::ptree& code_tree = main_tree.add("code", "");
// store minX and maxZ
@ -2775,7 +2783,13 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
code_tree.put("<xmlattr>.gcode" , code.gcode );
code_tree.put("<xmlattr>.extruder" , code.extruder );
code_tree.put("<xmlattr>.color" , code.color );
}
}
pt::ptree& mode_tree = main_tree.add("mode", "");
// store mode of a custom_gcode_per_print_z
mode_tree.put("<xmlattr>.value", model.custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
model.custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
CustomGCode::MultiExtruderMode);
if (!tree.empty())
{

View File

@ -13,6 +13,7 @@
#include "../Utils.hpp"
#include "../I18N.hpp"
#include "../Geometry.hpp"
#include "../CustomGCode.hpp"
#include "AMF.hpp"
@ -156,6 +157,7 @@ struct AMFParserContext
NODE_TYPE_PRINTABLE, // amf/constellation/instance/mirrorz
NODE_TYPE_CUSTOM_GCODE, // amf/custom_code_per_height
NODE_TYPE_GCODE_PER_HEIGHT, // amf/custom_code_per_height/code
NODE_TYPE_CUSTOM_GCODE_MODE, // amf/custom_code_per_height/mode
NODE_TYPE_METADATA, // anywhere under amf/*/metadata
};
@ -308,12 +310,18 @@ void AMFParserContext::startElement(const char *name, const char **atts)
else
this->stop();
}
else if (strcmp(name, "code") == 0 && m_path[1] == NODE_TYPE_CUSTOM_GCODE) {
node_type_new = NODE_TYPE_GCODE_PER_HEIGHT;
m_value[0] = get_attribute(atts, "height");
m_value[1] = get_attribute(atts, "gcode");
m_value[2] = get_attribute(atts, "extruder");
m_value[3] = get_attribute(atts, "color");
else if (m_path[1] == NODE_TYPE_CUSTOM_GCODE) {
if (strcmp(name, "code") == 0) {
node_type_new = NODE_TYPE_GCODE_PER_HEIGHT;
m_value[0] = get_attribute(atts, "print_z");
m_value[1] = get_attribute(atts, "gcode");
m_value[2] = get_attribute(atts, "extruder");
m_value[3] = get_attribute(atts, "color");
}
else if (strcmp(name, "mode") == 0) {
node_type_new = NODE_TYPE_CUSTOM_GCODE_MODE;
m_value[0] = get_attribute(atts, "value");
}
}
break;
case 3:
@ -632,18 +640,29 @@ void AMFParserContext::endElement(const char * /* name */)
break;
case NODE_TYPE_GCODE_PER_HEIGHT: {
double height = double(atof(m_value[0].c_str()));
double print_z = double(atof(m_value[0].c_str()));
const std::string& gcode = m_value[1];
int extruder = atoi(m_value[2].c_str());
const std::string& color = m_value[3];
m_model.custom_gcode_per_print_z.gcodes.push_back(Model::CustomGCode{height, gcode, extruder, color});
m_model.custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, gcode, extruder, color});
for (std::string& val: m_value)
val.clear();
break;
}
case NODE_TYPE_CUSTOM_GCODE_MODE: {
const std::string& mode = m_value[0];
m_model.custom_gcode_per_print_z.mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder :
mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle :
CustomGCode::Mode::MultiExtruder;
for (std::string& val: m_value)
val.clear();
break;
}
case NODE_TYPE_METADATA:
if ((m_config != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0)
m_config->load_from_gcode_string(m_value[1].c_str());
@ -1237,16 +1256,23 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
pt::ptree& main_tree = tree.add("custom_gcodes_per_height", "");
for (const Model::CustomGCode& code : model->custom_gcode_per_print_z.gcodes)
for (const CustomGCode::Item& code : model->custom_gcode_per_print_z.gcodes)
{
pt::ptree& code_tree = main_tree.add("code", "");
// store minX and maxZ
// store custom_gcode_per_print_z gcodes information
code_tree.put("<xmlattr>.print_z" , code.print_z );
code_tree.put("<xmlattr>.gcode" , code.gcode );
code_tree.put("<xmlattr>.extruder" , code.extruder );
code_tree.put("<xmlattr>.color" , code.color );
}
pt::ptree& mode_tree = main_tree.add("mode", "");
// store mode of a custom_gcode_per_print_z
mode_tree.put("<xmlattr>.value",
model->custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
model->custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ?
CustomGCode::MultiAsSingleMode : CustomGCode::MultiExtruderMode);
if (!tree.empty())
{
std::ostringstream oss;
@ -1259,6 +1285,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
// Post processing("beautification") of the output string
boost::replace_all(out, "><code", ">\n <code");
boost::replace_all(out, "><mode", ">\n <mode");
boost::replace_all(out, "><", ">\n<");
stream << out << "\n";

View File

@ -8,6 +8,7 @@
#include "GCode/WipeTower.hpp"
#include "ShortestPath.hpp"
#include "Utils.hpp"
#include "libslic3r.h"
#include <algorithm>
#include <cstdlib>
@ -164,12 +165,12 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP
cnt = (cnt + 1) / 2;
}
// And collect copies of the objects.
for (const Point &copy : object->copies()) {
for (const PrintInstance &instance : object->instances()) {
// All the layers were reduced to the 1st item of polygons_per_layer.
size_t i = islands.size();
polygons_append(islands, polygons_per_layer.front());
for (; i < islands.size(); ++ i)
islands[i].translate(copy);
islands[i].translate(instance.shift);
}
}
return islands;
@ -199,7 +200,7 @@ std::string OozePrevention::pre_toolchange(GCode &gcodegen)
if (gcodegen.config().standby_temperature_delta.value != 0) {
// we assume that heating is always slower than cooling, so no need to block
gcode += gcodegen.writer().set_temperature
(this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false);
(this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false, gcodegen.writer().extruder()->id());
}
return gcode;
@ -208,7 +209,7 @@ std::string OozePrevention::pre_toolchange(GCode &gcodegen)
std::string OozePrevention::post_toolchange(GCode &gcodegen)
{
return (gcodegen.config().standby_temperature_delta.value != 0) ?
gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true) :
gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true, gcodegen.writer().extruder()->id()) :
std::string();
}
@ -1086,6 +1087,41 @@ namespace DoExport {
}
}
// Sort the PrintObjects by their increasing Z, likely useful for avoiding colisions on Deltas during sequential prints.
static inline std::vector<const PrintInstance*> sort_object_instances_by_max_z(const Print &print)
{
std::vector<const PrintObject*> objects(print.objects().begin(), print.objects().end());
std::sort(objects.begin(), objects.end(), [](const PrintObject *po1, const PrintObject *po2) { return po1->size(2) < po2->size(2); });
std::vector<const PrintInstance*> instances;
instances.reserve(objects.size());
for (const PrintObject *object : objects)
for (size_t i = 0; i < object->instances().size(); ++ i)
instances.emplace_back(&object->instances()[i]);
return instances;
}
// Produce a vector of PrintObjects in the order of their respective ModelObjects in print.model().
static inline std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Print &print)
{
// Build up map from ModelInstance* to PrintInstance*
std::vector<std::pair<const ModelInstance*, const PrintInstance*>> model_instance_to_print_instance;
model_instance_to_print_instance.reserve(print.num_object_instances());
for (const PrintObject *print_object : print.objects())
for (const PrintInstance &print_instance : print_object->instances())
model_instance_to_print_instance.emplace_back(print_instance.model_instance, &print_instance);
std::sort(model_instance_to_print_instance.begin(), model_instance_to_print_instance.end(), [](auto &l, auto &r) { return l.first < r.first; });
std::vector<const PrintInstance*> instances;
instances.reserve(model_instance_to_print_instance.size());
for (const ModelObject *model_object : print.model().objects)
for (const ModelInstance *model_instance : model_object->instances) {
auto it = std::lower_bound(model_instance_to_print_instance.begin(), model_instance_to_print_instance.end(), std::make_pair(model_instance, nullptr), [](auto &l, auto &r) { return l.first < r.first; });
if (it != model_instance_to_print_instance.end() && it->first == model_instance)
instances.emplace_back(it->second);
}
return instances;
}
#if ENABLE_THUMBNAIL_GENERATOR
void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb)
#else
@ -1117,7 +1153,7 @@ void GCode::_do_export(Print& print, FILE* file)
for (auto layer : object->support_layers())
zs.push_back(layer->print_z);
std::sort(zs.begin(), zs.end());
m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin()));
m_layer_count += (unsigned int)(object->instances().size() * (std::unique(zs.begin(), zs.end()) - zs.begin()));
}
} else {
// Print all objects with the same print_z together.
@ -1210,13 +1246,18 @@ void GCode::_do_export(Print& print, FILE* file)
ToolOrdering tool_ordering;
unsigned int initial_extruder_id = (unsigned int)-1;
unsigned int final_extruder_id = (unsigned int)-1;
size_t initial_print_object_id = 0;
bool has_wipe_tower = false;
std::vector<const PrintInstance*> print_object_instances_ordering;
std::vector<const PrintInstance*>::const_iterator print_object_instance_sequential_active;
if (print.config().complete_objects.value) {
// Order object instances for sequential print.
print_object_instances_ordering = sort_object_instances_by_model_order(print);
// print_object_instances_ordering = sort_object_instances_by_max_z(print);
// Find the 1st printing object, find its tool ordering and the initial extruder ID.
for (; initial_print_object_id < print.objects().size(); ++initial_print_object_id) {
tool_ordering = ToolOrdering(*print.objects()[initial_print_object_id], initial_extruder_id);
if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
print_object_instance_sequential_active = print_object_instances_ordering.begin();
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
tool_ordering = ToolOrdering(*(*print_object_instance_sequential_active)->print_object, initial_extruder_id);
if ((initial_extruder_id = tool_ordering.first_extruder()) != static_cast<unsigned int>(-1))
break;
}
// We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode.
@ -1236,6 +1277,8 @@ void GCode::_do_export(Print& print, FILE* file)
// In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z.
// Therefore initialize the printing extruders from there.
this->set_extruders(tool_ordering.all_extruders());
// Order object instances using a nearest neighbor search.
print_object_instances_ordering = chain_print_object_instances(print);
}
if (initial_extruder_id == (unsigned int)-1) {
// Nothing to print!
@ -1316,72 +1359,64 @@ void GCode::_do_export(Print& print, FILE* file)
// Do all objects for each layer.
if (print.config().complete_objects.value) {
// Print objects from the smallest to the tallest to avoid collisions
// when moving onto next object starting point.
std::vector<PrintObject*> objects(print.objects());
std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size(2) < po2->size(2); });
size_t finished_objects = 0;
for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) {
const PrintObject &object = *objects[object_id];
for (const Point &copy : object.copies()) {
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
if (object_id != initial_print_object_id || &copy != object.copies().data()) {
// Don't initialize for the first object and first copy.
tool_ordering = ToolOrdering(object, final_extruder_id);
unsigned int new_extruder_id = tool_ordering.first_extruder();
if (new_extruder_id == (unsigned int)-1)
// Skip this object.
continue;
initial_extruder_id = new_extruder_id;
final_extruder_id = tool_ordering.last_extruder();
assert(final_extruder_id != (unsigned int)-1);
}
print.throw_if_canceled();
this->set_origin(unscale(copy));
if (finished_objects > 0) {
// Move to the origin position for the copy we're going to print.
// This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer
m_avoid_crossing_perimeters.use_external_mp_once = true;
_write(file, this->retract());
_write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
m_enable_cooling_markers = true;
// Disable motion planner when traveling to first object point.
m_avoid_crossing_perimeters.disable_once = true;
// Ff we are printing the bottom layer of an object, and we have already finished
// another one, set first layer temperatures. This happens before the Z move
// is triggered, so machine has more time to reach such temperatures.
m_placeholder_parser.set("current_object_idx", int(finished_objects));
std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id);
// Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
_writeln(file, between_objects_gcode);
}
// Reset the cooling buffer internal state (the current position, feed rate, accelerations).
m_cooling_buffer->reset();
m_cooling_buffer->set_current_extruder(initial_extruder_id);
// Pair the object layers with the support layers by z, extrude them.
std::vector<LayerToPrint> layers_to_print = collect_layers_to_print(object);
for (const LayerToPrint &ltp : layers_to_print) {
std::vector<LayerToPrint> lrs;
lrs.emplace_back(std::move(ltp));
this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), nullptr, &copy - object.copies().data());
print.throw_if_canceled();
}
#ifdef HAS_PRESSURE_EQUALIZER
if (m_pressure_equalizer)
_write(file, m_pressure_equalizer->process("", true));
#endif /* HAS_PRESSURE_EQUALIZER */
++ finished_objects;
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
// Reset it when starting another object from 1st layer.
m_second_layer_things_done = false;
const PrintObject *prev_object = (*print_object_instance_sequential_active)->print_object;
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
const PrintObject &object = *(*print_object_instance_sequential_active)->print_object;
if (&object != prev_object || tool_ordering.first_extruder() != final_extruder_id) {
tool_ordering = ToolOrdering(object, final_extruder_id);
unsigned int new_extruder_id = tool_ordering.first_extruder();
if (new_extruder_id == (unsigned int)-1)
// Skip this object.
continue;
initial_extruder_id = new_extruder_id;
final_extruder_id = tool_ordering.last_extruder();
assert(final_extruder_id != (unsigned int)-1);
}
print.throw_if_canceled();
this->set_origin(unscale((*print_object_instance_sequential_active)->shift));
if (finished_objects > 0) {
// Move to the origin position for the copy we're going to print.
// This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer
m_avoid_crossing_perimeters.use_external_mp_once = true;
_write(file, this->retract());
_write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
m_enable_cooling_markers = true;
// Disable motion planner when traveling to first object point.
m_avoid_crossing_perimeters.disable_once = true;
// Ff we are printing the bottom layer of an object, and we have already finished
// another one, set first layer temperatures. This happens before the Z move
// is triggered, so machine has more time to reach such temperatures.
m_placeholder_parser.set("current_object_idx", int(finished_objects));
std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id);
// Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
_writeln(file, between_objects_gcode);
}
// Reset the cooling buffer internal state (the current position, feed rate, accelerations).
m_cooling_buffer->reset();
m_cooling_buffer->set_current_extruder(initial_extruder_id);
// Pair the object layers with the support layers by z, extrude them.
std::vector<LayerToPrint> layers_to_print = collect_layers_to_print(object);
for (const LayerToPrint &ltp : layers_to_print) {
std::vector<LayerToPrint> lrs;
lrs.emplace_back(std::move(ltp));
this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), nullptr, *print_object_instance_sequential_active - object.instances().data());
print.throw_if_canceled();
}
#ifdef HAS_PRESSURE_EQUALIZER
if (m_pressure_equalizer)
_write(file, m_pressure_equalizer->process("", true));
#endif /* HAS_PRESSURE_EQUALIZER */
++ finished_objects;
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
// Reset it when starting another object from 1st layer.
m_second_layer_things_done = false;
prev_object = &object;
}
} else {
// Order object instances using a nearest neighbor search.
std::vector<std::pair<size_t, size_t>> print_object_instances_ordering = chain_print_object_instances(print);
// Sort layers by Z.
// All extrusion moves with the same top layer height are extruded uninterrupted.
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
@ -1683,12 +1718,12 @@ inline std::vector<GCode::ObjectByExtruder::Island>& object_islands_by_extruder(
}
std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
std::vector<GCode::ObjectByExtruder> &objects_by_extruder,
const std::vector<LayerToPrint> &layers,
std::vector<GCode::ObjectByExtruder> &objects_by_extruder,
const std::vector<LayerToPrint> &layers,
// Ordering must be defined for normal (non-sequential print).
const std::vector<std::pair<size_t, size_t>> *ordering,
const std::vector<const PrintInstance*> *ordering,
// For sequential print, the instance of the object to be printing has to be defined.
const size_t single_object_instance_idx)
const size_t single_object_instance_idx)
{
std::vector<InstanceToPrint> out;
@ -1715,13 +1750,13 @@ std::vector<GCode::InstanceToPrint> GCode::sort_print_object_instances(
if (! sorted.empty()) {
const Print &print = *sorted.front().first->print();
out.reserve(sorted.size());
for (const std::pair<size_t, size_t> &instance_id : *ordering) {
const PrintObject &print_object = *print.objects()[instance_id.first];
for (const PrintInstance *instance : *ordering) {
const PrintObject &print_object = *instance->print_object;
std::pair<const PrintObject*, ObjectByExtruder*> key(&print_object, nullptr);
auto it = std::lower_bound(sorted.begin(), sorted.end(), key);
if (it != sorted.end() && it->first == &print_object)
// ObjectByExtruder for this PrintObject was found.
out.emplace_back(*it->second, it->second - objects_by_extruder.data(), print_object, instance_id.second);
out.emplace_back(*it->second, it->second - objects_by_extruder.data(), print_object, instance - print_object.instances().data());
}
}
}
@ -1732,7 +1767,7 @@ namespace ProcessLayer
{
static std::string emit_custom_gcode_per_print_z(
const Model::CustomGCode *custom_gcode,
const CustomGCode::Item *custom_gcode,
// ID of the first extruder printing this layer.
unsigned int first_extruder_id,
bool single_material_print)
@ -1865,16 +1900,16 @@ namespace Skirt {
// and performing the extruder specific extrusions together.
void GCode::process_layer(
// Write into the output file.
FILE *file,
const Print &print,
FILE *file,
const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z.
const std::vector<LayerToPrint> &layers,
const LayerTools &layer_tools,
const std::vector<LayerToPrint> &layers,
const LayerTools &layer_tools,
// Pairs of PrintObject index and its instance index.
const std::vector<std::pair<size_t, size_t>> *ordering,
const std::vector<const PrintInstance*> *ordering,
// If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object.
const size_t single_object_instance_idx)
const size_t single_object_instance_idx)
{
assert(! layers.empty());
// assert(! layer_tools.extruders.empty());
@ -2083,7 +2118,7 @@ void GCode::process_layer(
// by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools)
correct_extruder_id = layer_tools.extruders.back();
}
entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->copies().size());
entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(extrusions, correct_extruder_id, layer_to_print.object()->instances().size());
if (entity_overrides == nullptr) {
printing_extruders.emplace_back(correct_extruder_id);
} else {
@ -2197,7 +2232,7 @@ void GCode::process_layer(
if (this->config().gcode_label_objects)
gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n";
// When starting a new object, use the external motion planner for the first travel move.
const Point &offset = instance_to_print.print_object.copies()[instance_to_print.instance_id];
const Point &offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
if (m_last_obj_copy != this_object_copy)
m_avoid_crossing_perimeters.use_external_mp_once = true;

View File

@ -226,7 +226,7 @@ private:
const std::vector<LayerToPrint> &layers,
const LayerTools &layer_tools,
// Pairs of PrintObject index and its instance index.
const std::vector<std::pair<size_t, size_t>> *ordering,
const std::vector<const PrintInstance*> *ordering,
// If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object.
const size_t single_object_idx = size_t(-1));
@ -300,7 +300,7 @@ private:
// Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances.
const std::vector<LayerToPrint> &layers,
// Ordering must be defined for normal (non-sequential print).
const std::vector<std::pair<size_t, size_t>> *ordering,
const std::vector<const PrintInstance*> *ordering,
// For sequential print, the instance of the object to be printing has to be defined.
const size_t single_object_instance_idx);

View File

@ -121,9 +121,9 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object
if (support_layer)
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
bbox_this.merge(extrusionentity_extents(extrusion_entity));
for (const Point &offset : print_object.copies()) {
for (const PrintInstance &instance : print_object.instances()) {
BoundingBoxf bbox_translated(bbox_this);
bbox_translated.translate(unscale(offset));
bbox_translated.translate(unscale(instance.shift));
bbox.merge(bbox_translated);
}
}

View File

@ -133,7 +133,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
num_extruders > 1 && print.object_extruders().size() == 1) {
// Printing a single extruder platter on a printer with more than 1 extruder (or single-extruder multi-material).
// There may be custom per-layer tool changes available at the model.
per_layer_extruder_switches = custom_tool_changes(print.model(), num_extruders);
per_layer_extruder_switches = custom_tool_changes(print.model().custom_gcode_per_print_z, num_extruders);
}
// Collect extruders reuqired to print the layers.
@ -462,7 +462,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
// Only valid for non-sequential print.
assert(! print.config().complete_objects.value);
const Model::CustomGCodeInfo &custom_gcode_per_print_z = print.model().custom_gcode_per_print_z;
const CustomGCode::Info &custom_gcode_per_print_z = print.model().custom_gcode_per_print_z;
if (custom_gcode_per_print_z.gcodes.empty())
return;
@ -483,7 +483,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
// Custom G-codes were processed.
break;
// Some custom G-code is configured for this layer or a layer below.
const Model::CustomGCode &custom_gcode = *custom_gcode_it;
const CustomGCode::Item &custom_gcode = *custom_gcode_it;
// print_z of the layer below the current layer.
coordf_t print_z_below = 0.;
if (auto it_lt_below = it_lt; ++ it_lt_below != m_layer_tools.rend())
@ -491,7 +491,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
if (custom_gcode.print_z > print_z_below + 0.5 * EPSILON) {
// The custom G-code applies to the current layer.
if ( tool_changes_as_color_changes || custom_gcode.gcode != ColorChangeCode ||
(custom_gcode.extruder <= num_extruders && extruder_printing_above[unsigned(custom_gcode.extruder - 1)]))
(custom_gcode.extruder <= int(num_extruders) && extruder_printing_above[unsigned(custom_gcode.extruder - 1)]))
// If it is color change, it will actually be useful as the exturder above will print.
lt.custom_gcode = &custom_gcode;
// Consume that custom G-code event.
@ -602,7 +602,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON);
if (this_layer == nullptr)
continue;
size_t num_of_copies = object->copies().size();
size_t num_of_copies = object->instances().size();
// iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves
for (unsigned int copy = 0; copy < num_of_copies; ++copy) {
@ -677,7 +677,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON);
if (this_layer == nullptr)
continue;
size_t num_of_copies = object->copies().size();
size_t num_of_copies = object->instances().size();
for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {

View File

@ -114,7 +114,7 @@ public:
size_t wipe_tower_partitions = 0;
coordf_t wipe_tower_layer_height = 0.;
// Custom G-code (color change, extruder switch, pause) to be performed before this layer starts to print.
const Model::CustomGCode *custom_gcode = nullptr;
const CustomGCode::Item *custom_gcode = nullptr;
WipingExtrusions& wiping_extrusions() {
m_wiping_extrusions.set_layer_tools_ptr(this);

View File

@ -1,4 +1,5 @@
#include "GCodeWriter.hpp"
#include "CustomGCode.hpp"
#include <algorithm>
#include <iomanip>
#include <iostream>

View File

@ -10,11 +10,6 @@
namespace Slic3r {
// Additional Codes which can be set by user using DoubleSlider
static constexpr char ColorChangeCode[] = "M600";
static constexpr char PausePrintCode[] = "M601";
static constexpr char ToolChangeCode[] = "tool_change";
class GCodeWriter {
public:
GCodeConfig config;

View File

@ -126,7 +126,8 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
if (add_default_instances)
model.add_default_instances();
update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z.gcodes, config);
CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
return model;
}
@ -163,7 +164,8 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
if (add_default_instances)
model.add_default_instances();
update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z.gcodes, config);
CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
return model;
}
@ -1848,19 +1850,6 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
return ret;
}
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
// print_z corresponds to the first layer printed with the new extruder.
std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Model &model, size_t num_extruders)
{
std::vector<std::pair<double, unsigned int>> custom_tool_changes;
for (const Model::CustomGCode &custom_gcode : model.custom_gcode_per_print_z.gcodes)
if (custom_gcode.gcode == ToolChangeCode) {
// If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast<unsigned int>(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder));
}
return custom_tool_changes;
}
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing.
bool model_object_list_equal(const Model &model_old, const Model &model_new)
@ -1949,28 +1938,6 @@ extern bool model_has_advanced_features(const Model &model)
return false;
}
extern void update_custom_gcode_per_print_z_from_config(std::vector<Model::CustomGCode>& custom_gcode_per_print_z, DynamicPrintConfig* config)
{
auto *colorprint_heights = config->option<ConfigOptionFloats>("colorprint_heights");
if (colorprint_heights == nullptr)
return;
if (custom_gcode_per_print_z.empty() && ! colorprint_heights->values.empty()) {
// Convert the old colorprint_heighs only if there is no equivalent data in a new format.
const std::vector<std::string>& colors = GCodePreviewData::ColorPrintColors();
const auto& colorprint_values = colorprint_heights->values;
custom_gcode_per_print_z.clear();
custom_gcode_per_print_z.reserve(colorprint_values.size());
int i = 0;
for (auto val : colorprint_values)
custom_gcode_per_print_z.emplace_back(Model::CustomGCode{ val, ColorChangeCode, 1, colors[(++i)%7] });
}
// The "colorprint_heights" config value has been deprecated. At this point of time it has been converted
// to a new format and therefore it shall be erased.
config->erase("colorprint_heights");
}
#ifndef NDEBUG
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
void check_model_ids_validity(const Model &model)

View File

@ -12,6 +12,7 @@
#include "SLA/Hollowing.hpp"
#include "TriangleMesh.hpp"
#include "Arrange.hpp"
#include "CustomGCode.hpp"
#include <map>
#include <memory>
@ -753,48 +754,7 @@ public:
ModelWipeTower wipe_tower;
// Extensions for color print
struct CustomGCode
{
bool operator<(const CustomGCode& rhs) const { return this->print_z < rhs.print_z; }
bool operator==(const CustomGCode& rhs) const
{
return (rhs.print_z == this->print_z ) &&
(rhs.gcode == this->gcode ) &&
(rhs.extruder == this->extruder ) &&
(rhs.color == this->color );
}
bool operator!=(const CustomGCode& rhs) const { return ! (*this == rhs); }
double print_z;
std::string gcode;
int extruder; // Informative value for ColorChangeCode and ToolChangeCode
// "gcode" == ColorChangeCode => M600 will be applied for "extruder" extruder
// "gcode" == ToolChangeCode => for whole print tool will be switched to "extruder" extruder
std::string color; // if gcode is equal to PausePrintCode,
// this field is used for save a short message shown on Printer display
};
struct CustomGCodeInfo
{
enum MODE
{
SingleExtruder, // single extruder printer preset is selected
MultiAsSingle, // multiple extruder printer preset is selected, but
// this mode works just for Single extruder print
// (For all print from objects settings is used just one extruder)
MultiExtruder // multiple extruder printer preset is selected
} mode;
std::vector<CustomGCode> gcodes;
bool operator==(const CustomGCodeInfo& rhs) const
{
return (rhs.mode == this->mode ) &&
(rhs.gcodes == this->gcodes );
}
bool operator!=(const CustomGCodeInfo& rhs) const { return !(*this == rhs); }
}
custom_gcode_per_print_z;
CustomGCode::Info custom_gcode_per_print_z;
// Default constructor assigns a new ID to the model.
Model() { assert(this->id().valid()); }
@ -876,10 +836,6 @@ private:
#undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE
#undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
// print_z corresponds to the first layer printed with the new extruder.
extern std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Model &model, size_t num_extruders);
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing.
extern bool model_object_list_equal(const Model &model_old, const Model &model_new);
@ -897,10 +853,6 @@ extern bool model_volume_list_changed(const ModelObject &model_object_old, const
extern bool model_has_multi_part_objects(const Model &model);
// If the model has advanced features, then it cannot be processed in simple mode.
extern bool model_has_advanced_features(const Model &model);
// If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer),
// and if model.custom_gcode_per_print_z is empty (there is no color print data available in a new format
// then model.custom_gcode_per_print_z should be updated considering this option.
extern void update_custom_gcode_per_print_z_from_config(std::vector<Model::CustomGCode>& custom_gcode_per_print_z, DynamicPrintConfig* config);
#ifndef NDEBUG
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.

View File

@ -338,7 +338,7 @@ namespace client
case TYPE_INT :
return expr<Iterator>(this->i(), start_pos, this->it_range.end());
case TYPE_DOUBLE:
return expr<Iterator>((int)(this->d()), start_pos, this->it_range.end());
return expr<Iterator>(static_cast<int>(this->d()), start_pos, this->it_range.end());
default:
this->throw_exception("Cannot convert to integer.");
}
@ -418,7 +418,7 @@ namespace client
{
this->throw_if_not_numeric("Cannot divide a non-numeric type.");
rhs.throw_if_not_numeric("Cannot divide with a non-numeric type.");
if ((this->type == TYPE_INT) ? (rhs.i() == 0) : (rhs.d() == 0.))
if ((rhs.type == TYPE_INT) ? (rhs.i() == 0) : (rhs.d() == 0.))
rhs.throw_exception("Division by zero");
if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) {
double d = this->as_d() / rhs.as_d();
@ -434,7 +434,7 @@ namespace client
{
this->throw_if_not_numeric("Cannot divide a non-numeric type.");
rhs.throw_if_not_numeric("Cannot divide with a non-numeric type.");
if ((this->type == TYPE_INT) ? (rhs.i() == 0) : (rhs.d() == 0.))
if ((rhs.type == TYPE_INT) ? (rhs.i() == 0) : (rhs.d() == 0.))
rhs.throw_exception("Division by zero");
if (this->type == TYPE_DOUBLE || rhs.type == TYPE_DOUBLE) {
double d = std::fmod(this->as_d(), rhs.as_d());
@ -845,7 +845,7 @@ namespace client
} else {
// Use the human readable error message.
msg += ". ";
msg + it->second;
msg += it->second;
}
}
msg += '\n';
@ -1134,7 +1134,7 @@ namespace client
static void string_(boost::iterator_range<Iterator> &it_range, expr<Iterator> &out)
{ out = expr<Iterator>(std::string(it_range.begin() + 1, it_range.end() - 1), it_range.begin(), it_range.end()); }
static void expr_(expr<Iterator> &value, Iterator &end_pos, expr<Iterator> &out)
{ out = expr<Iterator>(std::move(value), out.it_range.begin(), end_pos); }
{ auto begin_pos = out.it_range.begin(); out = expr<Iterator>(std::move(value), begin_pos, end_pos); }
static void minus_(expr<Iterator> &value, expr<Iterator> &out)
{ out = value.unary_minus(out.it_range.begin()); }
static void not_(expr<Iterator> &value, expr<Iterator> &out)
@ -1152,8 +1152,7 @@ namespace client
[ px::bind(&expr<Iterator>::min, _val, _2) ]
| (kw["max"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')')
[ px::bind(&expr<Iterator>::max, _val, _2) ]
//FIXME this is likley not correct
| (kw["int"] > '(' > unary_expression(_r1) /* > ')' */ ) [ px::bind(&FactorActions::to_int, _1, _val) ]
| (kw["int"] > '(' > unary_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ]
| (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ]
| (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ]
| (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ]
@ -1181,6 +1180,7 @@ namespace client
keywords.add
("and")
("if")
("int")
//("inf")
("else")
("elsif")

View File

@ -326,7 +326,7 @@ unsigned int Print::num_object_instances() const
{
unsigned int instances = 0;
for (const PrintObject *print_object : m_objects)
instances += (unsigned int)print_object->copies().size();
instances += (unsigned int)print_object->instances().size();
return instances;
}
@ -447,33 +447,30 @@ static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &
return true;
}
struct PrintInstances
struct PrintObjectTrafoAndInstances
{
Transform3d trafo;
Points copies;
bool operator<(const PrintInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); }
Transform3d trafo;
PrintInstances instances;
bool operator<(const PrintObjectTrafoAndInstances &rhs) const { return transform3d_lower(this->trafo, rhs.trafo); }
};
// Generate a list of trafos and XY offsets for instances of a ModelObject
static std::vector<PrintInstances> print_objects_from_model_object(const ModelObject &model_object)
static std::vector<PrintObjectTrafoAndInstances> print_objects_from_model_object(const ModelObject &model_object)
{
std::set<PrintInstances> trafos;
PrintInstances trafo;
trafo.copies.assign(1, Point());
std::set<PrintObjectTrafoAndInstances> trafos;
PrintObjectTrafoAndInstances trafo;
for (ModelInstance *model_instance : model_object.instances)
if (model_instance->is_printable()) {
trafo.trafo = model_instance->get_matrix();
auto shift = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
// Set the Z axis of the transformation.
trafo.copies.front() = Point::new_scale(trafo.trafo.data()[12], trafo.trafo.data()[13]);
trafo.trafo.data()[12] = 0;
trafo.trafo.data()[13] = 0;
auto it = trafos.find(trafo);
if (it == trafos.end())
trafos.emplace(trafo);
else
const_cast<PrintInstances&>(*it).copies.emplace_back(trafo.copies.front());
// Search or insert a trafo.
auto it = trafos.emplace(trafo).first;
const_cast<PrintObjectTrafoAndInstances&>(*it).instances.emplace_back(PrintInstance{ nullptr, model_instance, shift });
}
return std::vector<PrintInstances>(trafos.begin(), trafos.end());
return std::vector<PrintObjectTrafoAndInstances>(trafos.begin(), trafos.end());
}
// Compare just the layer ranges and their layer heights, not the associated configs.
@ -494,7 +491,7 @@ static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_
}
// Returns true if va == vb when all CustomGCode items that are not ToolChangeCode are ignored.
static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<Model::CustomGCode> &va, const std::vector<Model::CustomGCode> &vb)
static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<CustomGCode::Item> &va, const std::vector<CustomGCode::Item> &vb)
{
auto it_a = va.begin();
auto it_b = vb.begin();
@ -891,12 +888,27 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step.
model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file;
model_object.clear_instances();
model_object.instances.reserve(model_object_new.instances.size());
for (const ModelInstance *model_instance : model_object_new.instances) {
model_object.instances.emplace_back(new ModelInstance(*model_instance));
model_object.instances.back()->set_model_object(&model_object);
}
// Only refresh ModelInstances if there is any change.
if (model_object.instances.size() != model_object_new.instances.size() ||
! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), [](auto l, auto r){ return l->id() == r->id(); })) {
// G-code generator accesses model_object.instances to generate sequential print ordering matching the Plater object list.
update_apply_status(this->invalidate_step(psGCodeExport));
model_object.clear_instances();
model_object.instances.reserve(model_object_new.instances.size());
for (const ModelInstance *model_instance : model_object_new.instances) {
model_object.instances.emplace_back(new ModelInstance(*model_instance));
model_object.instances.back()->set_model_object(&model_object);
}
} else {
// Just synchronize the content of the instances. This avoids memory allocation and it does not invalidate ModelInstance pointers,
// which may be accessed by G-code export in the meanwhile to deduce sequential print order.
auto new_instance = model_object_new.instances.begin();
for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) {
(*old_instance)->set_transformation((*new_instance)->get_transformation());
(*old_instance)->print_volume_state = (*new_instance)->print_volume_state;
(*old_instance)->printable = (*new_instance)->printable;
}
}
}
}
@ -917,13 +929,12 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
}
// Generate a list of trafos and XY offsets for instances of a ModelObject
PrintObjectConfig config = PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders);
std::vector<PrintInstances> new_print_instances = print_objects_from_model_object(*model_object);
std::vector<PrintObjectTrafoAndInstances> new_print_instances = print_objects_from_model_object(*model_object);
if (old.empty()) {
// Simple case, just generate new instances.
for (const PrintInstances &print_instances : new_print_instances) {
for (PrintObjectTrafoAndInstances &print_instances : new_print_instances) {
PrintObject *print_object = new PrintObject(this, model_object, false);
print_object->set_trafo(print_instances.trafo);
print_object->set_copies(print_instances.copies);
print_object->set_trafo_and_instances(print_instances.trafo, std::move(print_instances.instances));
print_object->config_apply(config);
print_objects_new.emplace_back(print_object);
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
@ -936,13 +947,12 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); });
// Merge the old / new lists.
auto it_old = old.begin();
for (const PrintInstances &new_instances : new_print_instances) {
for (PrintObjectTrafoAndInstances &new_instances : new_print_instances) {
for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old);
if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) {
// This is a new instance (or a set of instances with the same trafo). Just add it.
PrintObject *print_object = new PrintObject(this, model_object, false);
print_object->set_trafo(new_instances.trafo);
print_object->set_copies(new_instances.copies);
print_object->set_trafo_and_instances(new_instances.trafo, std::move(new_instances.instances));
print_object->config_apply(config);
print_objects_new.emplace_back(print_object);
// print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
@ -951,7 +961,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Deleted;
} else {
// The PrintObject already exists and the copies differ.
PrintBase::ApplyStatus status = (*it_old)->print_object->set_copies(new_instances.copies);
PrintBase::ApplyStatus status = (*it_old)->print_object->set_instances(std::move(new_instances.instances));
if (status != PrintBase::APPLY_STATUS_UNCHANGED)
update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED);
print_objects_new.emplace_back((*it_old)->print_object);
@ -1159,7 +1169,7 @@ std::string Print::validate() const
Polygons convex_hulls_other;
for (const PrintObject *print_object : m_objects) {
assert(! print_object->model_object()->instances.empty());
assert(! print_object->copies().empty());
assert(! print_object->instances().empty());
// Get convex hull of all meshes assigned to this print object.
ModelInstance *model_instance0 = print_object->model_object()->instances.front();
Vec3d rotation = model_instance0->get_rotation();
@ -1174,9 +1184,9 @@ std::string Print::validate() const
Geometry::assemble_transform(Vec3d::Zero(), rotation, model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
float(scale_(0.5 * m_config.extruder_clearance_radius.value)), jtRound, float(scale_(0.1))).front();
// Now we check that no instance of convex_hull intersects any of the previously checked object instances.
for (const Point &copy : print_object->copies()) {
for (const PrintInstance &instance : print_object->instances()) {
Polygon convex_hull = convex_hull0;
convex_hull.translate(copy);
convex_hull.translate(instance.shift);
if (! intersection(convex_hulls_other, convex_hull).empty())
return L("Some objects are too close; your extruder will collide with them.");
polygons_append(convex_hulls_other, convex_hull);
@ -1187,7 +1197,7 @@ std::string Print::validate() const
{
std::vector<coord_t> object_height;
for (const PrintObject *object : m_objects)
object_height.insert(object_height.end(), object->copies().size(), object->size(2));
object_height.insert(object_height.end(), object->instances().size(), object->size(2));
std::sort(object_height.begin(), object_height.end());
// Ignore the tallest *copy* (this is why we repeat height for all of them):
// it will be printed as last one so its height doesn't matter.
@ -1200,7 +1210,7 @@ std::string Print::validate() const
if (m_config.spiral_vase) {
size_t total_copies_count = 0;
for (const PrintObject *object : m_objects)
total_copies_count += object->copies().size();
total_copies_count += object->instances().size();
// #4043
if (total_copies_count > 1 && ! m_config.complete_objects.value)
return L("The Spiral Vase option can only be used when printing a single object.");
@ -1417,10 +1427,9 @@ BoundingBox Print::bounding_box() const
{
BoundingBox bb;
for (const PrintObject *object : m_objects)
for (Point copy : object->m_copies) {
bb.merge(copy);
copy += to_2d(object->size);
bb.merge(copy);
for (const PrintInstance &instance : object->instances()) {
bb.merge(instance.shift);
bb.merge(instance.shift + to_2d(object->size));
}
return bb;
}
@ -1657,10 +1666,10 @@ void Print::_make_skirt()
append(object_points, extrusion_entity->as_polyline().points);
}
// Repeat points for each object copy.
for (const Point &shift : object->m_copies) {
for (const PrintInstance &instance : object->instances()) {
Points copy_points = object_points;
for (Point &pt : copy_points)
pt += shift;
pt += instance.shift;
append(points, copy_points);
}
}
@ -1778,11 +1787,11 @@ void Print::_make_brim()
object_islands.push_back(expoly.contour);
if (! object->support_layers().empty())
object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON));
islands.reserve(islands.size() + object_islands.size() * object->m_copies.size());
for (const Point &pt : object->m_copies)
islands.reserve(islands.size() + object_islands.size() * object->instances().size());
for (const PrintInstance &instance : object->instances())
for (Polygon &poly : object_islands) {
islands.push_back(poly);
islands.back().translate(pt);
islands.back().translate(instance.shift);
}
}
Polygons loops;

View File

@ -92,6 +92,21 @@ typedef std::vector<Layer*> LayerPtrs;
typedef std::vector<SupportLayer*> SupportLayerPtrs;
class BoundingBoxf3; // TODO: for temporary constructor parameter
// Single instance of a PrintObject.
// As multiple PrintObjects may be generated for a single ModelObject (their instances differ in rotation around Z),
// ModelObject's instancess will be distributed among these multiple PrintObjects.
struct PrintInstance
{
// Parent PrintObject
PrintObject *print_object;
// Source ModelInstance of a ModelObject, for which this print_object was created.
const ModelInstance *model_instance;
// Shift of this instance towards its PrintObject
Point shift;
};
typedef std::vector<PrintInstance> PrintInstances;
class PrintObject : public PrintObjectBaseWithState<Print, PrintObjectStep, posCount>
{
private: // Prevents erroneous use by other classes.
@ -111,8 +126,8 @@ public:
const LayerPtrs& layers() const { return m_layers; }
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
const Transform3d& trafo() const { return m_trafo; }
const Points& copies() const { return m_copies; }
const Point copy_center(size_t idx) const { return m_copies[idx] + m_copies_shift + Point(this->size.x() / 2, this->size.y() / 2); }
const PrintInstances& instances() const { return m_instances; }
const Point instance_center(size_t idx) const { return m_instances[idx].shift + m_copies_shift + Point(this->size.x() / 2, this->size.y() / 2); }
// since the object is aligned to origin, bounding box coincides with size
BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); }
@ -126,9 +141,9 @@ public:
// This is the *total* layer count (including support layers)
// this value is not supposed to be compared with Layer::id
// since they have different semantics.
size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); }
size_t layer_count() const { return m_layers.size(); }
void clear_layers();
size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); }
size_t layer_count() const { return m_layers.size(); }
void clear_layers();
const Layer* get_layer(int idx) const { return m_layers[idx]; }
Layer* get_layer(int idx) { return m_layers[idx]; }
// Get a layer exactly at print_z.
@ -177,7 +192,7 @@ public:
std::vector<ExPolygons> slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); }
std::vector<ExPolygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
protected:
private:
// to be called from Print only.
friend class Print;
@ -187,7 +202,8 @@ protected:
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); }
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); }
void set_trafo(const Transform3d& trafo) { m_trafo = trafo; }
PrintBase::ApplyStatus set_copies(const Points &points);
PrintBase::ApplyStatus set_instances(PrintInstances &&instances);
void set_trafo_and_instances(const Transform3d& trafo, PrintInstances &&instances) { this->set_trafo(trafo); this->set_instances(std::move(instances)); }
// Invalidates the step, and its depending steps in PrintObject and Print.
bool invalidate_step(PrintObjectStep step);
// Invalidates all PrintObject and Print steps.
@ -223,7 +239,7 @@ private:
// Translation in Z + Rotation + Scaling / Mirroring.
Transform3d m_trafo = Transform3d::Identity();
// Slic3r::Point objects in scaled G-code coordinates
Points m_copies;
std::vector<PrintInstance> m_instances;
// scaled coordinates to add to copies (to compensate for the alignment
// operated when creating the object but still preserving a coherent API
// for external callers)

View File

@ -60,32 +60,32 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta
}
if (add_instances) {
Points copies;
copies.reserve(m_model_object->instances.size());
PrintInstances instances;
instances.reserve(m_model_object->instances.size());
for (const ModelInstance *mi : m_model_object->instances) {
assert(mi->is_printable());
const Vec3d& offset = mi->get_offset();
copies.emplace_back(Point::new_scale(offset(0), offset(1)));
const Vec3d &offset = mi->get_offset();
instances.emplace_back(PrintInstance{ nullptr, mi, Point::new_scale(offset(0), offset(1)) });
}
this->set_copies(copies);
this->set_instances(std::move(instances));
}
}
PrintBase::ApplyStatus PrintObject::set_copies(const Points &points)
PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances)
{
// Order copies with a nearest-neighbor search.
std::vector<Point> copies;
copies.reserve(points.size());
for (const Point &pt : points)
copies.emplace_back(pt + m_copies_shift);
// Invalidate and set copies.
PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED;
if (copies != m_copies) {
bool equal_length = instances.size() == m_instances.size();
bool equal = equal_length && std::equal(instances.begin(), instances.end(), m_instances.begin(),
[](const PrintInstance& lhs, const PrintInstance& rhs) { return lhs.model_instance == rhs.model_instance && lhs.shift == rhs.shift; });
if (! equal) {
status = PrintBase::APPLY_STATUS_CHANGED;
if (m_print->invalidate_steps({ psSkirt, psBrim, psGCodeExport }) ||
(copies.size() != m_copies.size() && m_print->invalidate_step(psWipeTower)))
(! equal_length && m_print->invalidate_step(psWipeTower)))
status = PrintBase::APPLY_STATUS_INVALIDATED;
m_copies = copies;
m_instances = instances;
for (PrintInstance &i : m_instances)
i.print_object = this;
}
return status;
}
@ -669,7 +669,7 @@ void PrintObject::detect_surfaces_type()
m_print->throw_if_canceled();
// BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << idx_region << " and layer " << layer->print_z;
Layer *layer = m_layers[idx_layer];
LayerRegion *layerm = layer->get_region(idx_region);
LayerRegion *layerm = layer->m_regions[idx_region];
// comparison happens against the *full* slices (considering all regions)
// unless internal shells are requested
Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? m_layers[idx_layer + 1] : nullptr;
@ -684,7 +684,7 @@ void PrintObject::detect_surfaces_type()
Surfaces top;
if (upper_layer) {
Polygons upper_slices = interface_shells ?
to_polygons(upper_layer->get_region(idx_region)->slices.surfaces) :
to_polygons(upper_layer->m_regions[idx_region]->slices.surfaces) :
to_polygons(upper_layer->lslices);
surfaces_append(top,
//FIXME implement offset2_ex working over ExPolygons, that should be a bit more efficient than calling offset_ex twice.
@ -727,7 +727,7 @@ void PrintObject::detect_surfaces_type()
offset2_ex(
diff(
intersection(layerm_slices_surfaces, to_polygons(lower_layer->lslices)), // supported
to_polygons(lower_layer->get_region(idx_region)->slices.surfaces),
to_polygons(lower_layer->m_regions[idx_region]->slices.surfaces),
true),
-offset, offset),
stBottom);
@ -796,7 +796,7 @@ void PrintObject::detect_surfaces_type()
if (interface_shells) {
// Move surfaces_new to layerm->slices.surfaces
for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++ idx_layer)
m_layers[idx_layer]->get_region(idx_region)->slices.surfaces = std::move(surfaces_new[idx_layer]);
m_layers[idx_layer]->m_regions[idx_region]->slices.surfaces = std::move(surfaces_new[idx_layer]);
}
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - start";
@ -806,7 +806,7 @@ void PrintObject::detect_surfaces_type()
[this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) {
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled();
LayerRegion *layerm = m_layers[idx_layer]->get_region(idx_region);
LayerRegion *layerm = m_layers[idx_layer]->m_regions[idx_region];
layerm->slices_to_fill_surfaces_clipped();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final");

View File

@ -1946,24 +1946,26 @@ ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const Clippe
return chain_path_items(points, items);
}
std::vector<std::pair<size_t, size_t>> chain_print_object_instances(const Print &print)
std::vector<const PrintInstance*> chain_print_object_instances(const Print &print)
{
// Order objects using a nearest neighbor search.
Points object_reference_points;
std::vector<std::pair<size_t, size_t>> instances;
for (size_t i = 0; i < print.objects().size(); ++ i) {
const PrintObject &object = *print.objects()[i];
for (size_t j = 0; j < object.copies().size(); ++ j) {
object_reference_points.emplace_back(object.copy_center(j));
for (size_t j = 0; j < object.instances().size(); ++ j) {
object_reference_points.emplace_back(object.instance_center(j));
instances.emplace_back(i, j);
}
}
auto segment_end_point = [&object_reference_points](size_t idx, bool /* first_point */) -> const Point& { return object_reference_points[idx]; };
std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, instances.size(), nullptr);
std::vector<std::pair<size_t, size_t>> out;
std::vector<const PrintInstance*> out;
out.reserve(instances.size());
for (auto &segment_and_reversal : ordered)
out.emplace_back(instances[segment_and_reversal.first]);
for (auto &segment_and_reversal : ordered) {
const std::pair<size_t, size_t> &inst = instances[segment_and_reversal.first];
out.emplace_back(&print.objects()[inst.first]->instances()[inst.second]);
}
return out;
}

View File

@ -30,7 +30,8 @@ std::vector<ClipperLib::PolyNode*> chain_clipper_polynodes(const Points &points
// Chain instances of print objects by an approximate shortest path.
// Returns pairs of PrintObject idx and instance of that PrintObject.
class Print;
std::vector<std::pair<size_t, size_t>> chain_print_object_instances(const Print &print);
struct PrintInstance;
std::vector<const PrintInstance*> chain_print_object_instances(const Print &print);
} // namespace Slic3r

View File

@ -59,4 +59,8 @@
// Enable 6 degrees of freedom camera
#define ENABLE_6DOF_CAMERA (1 && ENABLE_2_2_0_BETA1)
// Enhance reload from disk to be able to work with 3mf/amf files saved with PrusaSlicer 2.1.0 and earlier
#define ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK (1 && ENABLE_2_2_0_BETA1)
#endif // _technologies_h_

View File

@ -449,7 +449,7 @@ int copy_file(const std::string &from, const std::string &to, const bool with_ch
ret_val = check_copy(from, to_temp);
if (ret_val == 0 && rename_file(to_temp, to))
ret_val = -1;
ret_val = -3;
}
return ret_val;
}
@ -460,11 +460,11 @@ int check_copy(const std::string &origin, const std::string &copy)
std::ifstream f2(copy, std::ifstream::in | std::ifstream::binary | std::ifstream::ate);
if (f1.fail() || f2.fail())
return -1;
return -2;
std::streampos fsize = f1.tellg();
if (fsize != f2.tellg())
return -1;
return -2;
f1.seekg(0, std::ifstream::beg);
f2.seekg(0, std::ifstream::beg);
@ -481,12 +481,12 @@ int check_copy(const std::string &origin, const std::string &copy)
if (origin_cnt != copy_cnt ||
(origin_cnt > 0 && std::memcmp(buffer_origin.data(), buffer_copy.data(), origin_cnt) != 0))
// Files are different.
return -1;
return -2;
fsize -= origin_cnt;
} while (f1.good() && f2.good());
// All data has been read and compared equal.
return (f1.eof() && f2.eof() && fsize == 0) ? 0 : -1;
return (f1.eof() && f2.eof() && fsize == 0) ? 0 : -2;
}
// Ignore system and hidden files, which may be created by the DropBox synchronisation process.

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- for dynamic loading of libraries without signature validation. Used for 3dconnection drivers.-->
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>

View File

@ -145,6 +145,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Job.hpp
GUI/Mouse3DController.cpp
GUI/Mouse3DController.hpp
GUI/DoubleSlider.cpp
GUI/DoubleSlider.hpp
Utils/Http.cpp
Utils/Http.hpp
Utils/FixModelByWin10.cpp
@ -176,8 +178,8 @@ if (APPLE)
Utils/MacDarkMode.mm
GUI/RemovableDriveManagerMM.mm
GUI/RemovableDriveManagerMM.h
GUI/Mouse3DHandlerMac.mm
)
#DK
FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration)
endif ()
@ -187,7 +189,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
encoding_check(libslic3r_gui)
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi)
#DK
if(APPLE)
target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY})
endif()

View File

@ -78,6 +78,9 @@ void AppConfig::set_defaults()
if (get("remember_output_path").empty())
set("remember_output_path", "1");
if (get("remember_output_path_removable").empty())
set("remember_output_path_removable", "1");
if (get("use_custom_toolbar_size").empty())
set("use_custom_toolbar_size", "0");
@ -388,7 +391,7 @@ void AppConfig::update_skein_dir(const std::string &dir)
{
this->set("recent", "skein_directory", dir);
}
/*
std::string AppConfig::get_last_output_dir(const std::string &alt) const
{
@ -406,6 +409,26 @@ void AppConfig::update_last_output_dir(const std::string &dir)
{
this->set("", "last_output_path", dir);
}
*/
std::string AppConfig::get_last_output_dir(const std::string& alt, const bool removable) const
{
std::string s1 = (removable ? "last_output_path_removable" : "last_output_path");
std::string s2 = (removable ? "remember_output_path_removable" : "remember_output_path");
const auto it = m_storage.find("");
if (it != m_storage.end()) {
const auto it2 = it->second.find(s1);
const auto it3 = it->second.find(s2);
if (it2 != it->second.end() && it3 != it->second.end() && !it2->second.empty() && it3->second == "1")
return it2->second;
}
return alt;
}
void AppConfig::update_last_output_dir(const std::string& dir, const bool removable)
{
this->set("", (removable ? "last_output_path_removable" : "last_output_path"), dir);
}
void AppConfig::reset_selections()
{

View File

@ -102,8 +102,10 @@ public:
void update_config_dir(const std::string &dir);
void update_skein_dir(const std::string &dir);
std::string get_last_output_dir(const std::string &alt) const;
void update_last_output_dir(const std::string &dir);
//std::string get_last_output_dir(const std::string &alt) const;
//void update_last_output_dir(const std::string &dir);
std::string get_last_output_dir(const std::string& alt, const bool removable = false) const;
void update_last_output_dir(const std::string &dir, const bool removable = false);
// reset the current print / filament / printer selections, so that
// the PresetBundle::load_selections(const AppConfig &config) call will select

View File

@ -100,8 +100,23 @@ void BackgroundSlicingProcess::process_fff()
//FIXME localize the messages
// Perform the final post-processing of the export path by applying the print statistics over the file name.
std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
if (copy_file(m_temp_output_path, export_path, GUI::RemovableDriveManager::get_instance().is_path_on_removable_drive(export_path)) != 0)
GUI::RemovableDriveManager::get_instance().update();
bool with_check = GUI::RemovableDriveManager::get_instance().is_path_on_removable_drive(export_path);
int copy_ret_val = copy_file(m_temp_output_path, export_path, with_check);
if (with_check && copy_ret_val == -2)
{
std::string err_msg = "Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at " + export_path + ".tmp.";
throw std::runtime_error(_utf8(L(err_msg)));
}
else if (copy_ret_val == -3)
{
std::string err_msg = "Renaming of the G-code after copying to the selected destination folder has failed. Current path is " + export_path + ".tmp. Please try exporting again.";
throw std::runtime_error(_utf8(L(err_msg)));
}
else if ( copy_ret_val != 0)
{
throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?")));
}
m_print->set_status(95, _utf8(L("Running post-processing scripts")));
run_post_process_scripts(export_path, m_fff_print->config());
m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str());

View File

@ -1702,6 +1702,9 @@ void ConfigWizard::priv::on_3rdparty_install(const VendorProfile *vendor, bool i
if (page->install && !install)
page->select_all(false);
page->install = install;
// if some 3rd vendor is selected, select first printer for them
if (install)
page->printer_pickers[0]->select_one(0, true);
page->Layout();
}
@ -2020,7 +2023,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
p->add_page(p->page_temps = new PageTemperatures(this));
// Pages for 3rd party vendors
p->create_3rdparty_pages(); // Needs to ne done _before_ creating PageVendors
p->create_3rdparty_pages(); // Needs to be done _before_ creating PageVendors
p->add_page(p->page_vendors = new PageVendors(this));
p->load_pages();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,337 @@
#ifndef slic3r_GUI_DoubleSlider_hpp_
#define slic3r_GUI_DoubleSlider_hpp_
#include "libslic3r/CustomGCode.hpp"
#include "wxExtensions.hpp"
#include <wx/wx.h>
#include <wx/window.h>
#include <wx/control.h>
#include <wx/dc.h>
#include <wx/slider.h>
#include <vector>
#include <set>
class wxMenu;
namespace Slic3r {
namespace DoubleSlider {
/* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values.
* So, let use same value as a permissible error for layer height.
*/
static double epsilon() { return 0.0011;}
// custom message the slider sends to its parent to notify a tick-change:
wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent);
enum SelectedSlider {
ssUndef,
ssLower,
ssHigher
};
enum IconFocus {
ifNone,
ifRevert,
ifCog
};
using t_mode = CustomGCode::Mode;
struct TickCode
{
bool operator<(const TickCode& other) const { return other.tick > this->tick; }
bool operator>(const TickCode& other) const { return other.tick < this->tick; }
int tick = 0;
std::string gcode = ColorChangeCode;
int extruder = 0;
std::string color;
};
class TickCodeInfo
{
std::string custom_gcode;
std::string pause_print_msg;
bool m_suppress_plus = false;
bool m_suppress_minus = false;
std::string get_color_for_tick(TickCode tick, const std::string& code, const int extruder);
public:
std::set<TickCode> ticks {};
t_mode mode = t_mode::SingleExtruder;
bool empty() const { return ticks.empty(); }
void set_pause_print_msg(const std::string& message) { pause_print_msg = message; }
bool add_tick(const int tick, std::string& code, int extruder, double print_z);
bool edit_tick(std::set<TickCode>::iterator it, double print_z);
void switch_code(const std::string& code_from, const std::string& code_to);
bool switch_code_for_tick(std::set<TickCode>::iterator it, const std::string& code_to, const int extruder);
void erase_all_ticks_with_code(const std::string& gcode);
bool has_tick_with_code(const std::string& gcode);
void suppress_plus (bool suppress) { m_suppress_plus = suppress; }
void suppress_minus(bool suppress) { m_suppress_minus = suppress; }
bool suppressed_plus () { return m_suppress_plus; }
bool suppressed_minus() { return m_suppress_minus; }
};
struct ExtrudersSequence
{
bool is_mm_intervals = true;
double interval_by_mm = 3.0;
int interval_by_layers = 10;
std::vector<size_t> extruders = { 0 };
bool operator==(const ExtrudersSequence& other) const
{
return (other.is_mm_intervals == this->is_mm_intervals ) &&
(other.interval_by_mm == this->interval_by_mm ) &&
(other.interval_by_layers == this->interval_by_layers ) &&
(other.extruders == this->extruders ) ;
}
bool operator!=(const ExtrudersSequence& other) const
{
return (other.is_mm_intervals != this->is_mm_intervals ) &&
(other.interval_by_mm != this->interval_by_mm ) &&
(other.interval_by_layers != this->interval_by_layers ) &&
(other.extruders != this->extruders ) ;
}
void add_extruder(size_t pos)
{
extruders.insert(extruders.begin() + pos+1, size_t(0));
}
void delete_extruder(size_t pos)
{
if (extruders.size() == 1)
return;// last item can't be deleted
extruders.erase(extruders.begin() + pos);
}
};
class Control : public wxControl
{
public:
Control(
wxWindow *parent,
wxWindowID id,
int lowerValue,
int higherValue,
int minValue,
int maxValue,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxSL_VERTICAL,
const wxValidator& val = wxDefaultValidator,
const wxString& name = wxEmptyString);
~Control() {}
void msw_rescale();
int GetMinValue() const { return m_min_value; }
int GetMaxValue() const { return m_max_value; }
double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value]; }
double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; }
int GetLowerValue() const { return m_lower_value; }
int GetHigherValue() const { return m_higher_value; }
int GetActiveValue() const;
double GetLowerValueD() { return get_double_value(ssLower); }
double GetHigherValueD() { return get_double_value(ssHigher); }
wxSize DoGetBestSize() const override;
wxSize get_min_size() const ;
// Set low and high slider position. If the span is non-empty, disable the "one layer" mode.
void SetLowerValue (const int lower_val);
void SetHigherValue(const int higher_val);
void SetSelectionSpan(const int lower_val, const int higher_val);
void SetMaxValue(const int max_value);
void SetKoefForLabels(const double koef) { m_label_koef = koef; }
void SetSliderValues(const std::vector<double>& values) { m_values = values; }
void ChangeOneLayerLock();
CustomGCode::Info GetTicksValues() const;
void SetTicksValues(const Slic3r::CustomGCode::Info &custom_gcode_per_print_z);
void EnableTickManipulation(bool enable = true) { m_is_enabled_tick_manipulation = enable; }
void DisableTickManipulation() { EnableTickManipulation(false); }
void SetManipulationMode(t_mode mode) { m_mode = mode; }
t_mode GetManipulationMode() const { return m_mode; }
void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder)
{
m_mode = !is_one_extruder_printed_model ? t_mode::MultiExtruder :
only_extruder < 0 ? t_mode::SingleExtruder :
t_mode::MultiAsSingle;
m_only_extruder = only_extruder;
}
bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; }
bool is_one_layer() const { return m_is_one_layer; }
bool is_lower_at_min() const { return m_lower_value == m_min_value; }
bool is_higher_at_max() const { return m_higher_value == m_max_value; }
bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); }
void OnPaint(wxPaintEvent& ) { render();}
void OnLeftDown(wxMouseEvent& event);
void OnMotion(wxMouseEvent& event);
void OnLeftUp(wxMouseEvent& event);
void OnEnterWin(wxMouseEvent& event) { enter_window(event, true); }
void OnLeaveWin(wxMouseEvent& event) { enter_window(event, false); }
void OnWheel(wxMouseEvent& event);
void OnKeyDown(wxKeyEvent &event);
void OnKeyUp(wxKeyEvent &event);
void OnChar(wxKeyEvent &event);
void OnRightDown(wxMouseEvent& event);
void OnRightUp(wxMouseEvent& event);
void add_code_as_tick(std::string code, int selected_extruder = -1);
// add default action for tick, when press "+"
void add_current_tick(bool call_from_keyboard = false);
// delete current tick, when press "-"
void delete_current_tick();
void edit_tick();
void edit_extruder_sequence();
ExtrudersSequence m_extruders_sequence;
protected:
void render();
void draw_focus_rect();
void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end);
void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos);
void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection);
void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos);
void draw_ticks(wxDC& dc);
void draw_colored_band(wxDC& dc);
void draw_one_layer_icon(wxDC& dc);
void draw_revert_icon(wxDC& dc);
void draw_cog_icon(wxDC &dc);
void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection);
void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection);
void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const;
void update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection);
void detect_selected_slider(const wxPoint& pt);
void correct_lower_value();
void correct_higher_value();
void move_current_thumb(const bool condition);
void enter_window(wxMouseEvent& event, const bool enter);
private:
bool is_point_in_rect(const wxPoint& pt, const wxRect& rect);
int is_point_near_tick(const wxPoint& pt);
double get_scroll_step();
wxString get_label(const SelectedSlider& selection) const;
void get_lower_and_higher_position(int& lower_pos, int& higher_pos);
int get_value_from_position(const wxCoord x, const wxCoord y);
wxCoord get_position_from_value(const int value);
wxSize get_size();
void get_size(int *w, int *h);
double get_double_value(const SelectedSlider& selection);
wxString get_tooltip(IconFocus icon_focus);
std::string get_color_for_tool_change_tick(std::set<TickCode>::const_iterator it) const;
std::string get_color_for_color_change_tick(std::set<TickCode>::const_iterator it) const;
// Get active extruders for tick.
// Means one current extruder for not existing tick OR
// 2 extruders - for existing tick (extruder before ToolChangeCode and extruder of current existing tick)
// Use those values to disable selection of active extruders
std::array<int, 2> get_active_extruders_for_tick(int tick) const;
// Get used extruders for tick.
// Means all extruders(toools) will be used during printing from current tick to the end
std::set<int> get_used_extruders_for_tick(int tick) const;
void post_ticks_changed_event(const std::string& gcode = "");
bool check_ticks_changed_event(const std::string& gcode);
void append_change_extruder_menu_item (wxMenu*, bool switch_current_code = false);
void append_add_color_change_menu_item(wxMenu*, bool switch_current_code = false);
bool is_osx { false };
wxFont m_font;
int m_min_value;
int m_max_value;
int m_lower_value;
int m_higher_value;
ScalableBitmap m_bmp_thumb_higher;
ScalableBitmap m_bmp_thumb_lower;
ScalableBitmap m_bmp_add_tick_on;
ScalableBitmap m_bmp_add_tick_off;
ScalableBitmap m_bmp_del_tick_on;
ScalableBitmap m_bmp_del_tick_off;
ScalableBitmap m_bmp_one_layer_lock_on;
ScalableBitmap m_bmp_one_layer_lock_off;
ScalableBitmap m_bmp_one_layer_unlock_on;
ScalableBitmap m_bmp_one_layer_unlock_off;
ScalableBitmap m_bmp_revert;
ScalableBitmap m_bmp_cog;
SelectedSlider m_selection;
bool m_is_left_down = false;
bool m_is_right_down = false;
bool m_is_one_layer = false;
bool m_is_focused = false;
bool m_is_action_icon_focesed = false;
bool m_is_one_layer_icon_focesed = false;
bool m_is_enabled_tick_manipulation = true;
bool m_show_context_menu = false;
bool m_show_edit_menu = false;
bool m_force_edit_extruder_sequence = false;
bool m_force_mode_apply = true;
bool m_force_add_tick = false;
bool m_force_delete_tick = false;
t_mode m_mode = t_mode::SingleExtruder;
int m_only_extruder = -1;
wxRect m_rect_lower_thumb;
wxRect m_rect_higher_thumb;
wxRect m_rect_tick_action;
wxRect m_rect_one_layer_icon;
wxRect m_rect_revert_icon;
wxRect m_rect_cog_icon;
wxSize m_thumb_size;
int m_tick_icon_dim;
int m_lock_icon_dim;
int m_revert_icon_dim;
int m_cog_icon_dim;
long m_style;
float m_label_koef = 1.0;
std::vector<double> m_values;
TickCodeInfo m_ticks;
// control's view variables
wxCoord SLIDER_MARGIN; // margin around slider
wxPen DARK_ORANGE_PEN;
wxPen ORANGE_PEN;
wxPen LIGHT_ORANGE_PEN;
wxPen DARK_GREY_PEN;
wxPen GREY_PEN;
wxPen LIGHT_GREY_PEN;
std::vector<wxPen*> m_line_pens;
std::vector<wxPen*> m_segm_pens;
};
} // DoubleSlider;
} // Slic3r
#endif // slic3r_GUI_DoubleSlider_hpp_

View File

@ -2,7 +2,7 @@
#define slic3r_GUI_ExtruderSequenceDialog_hpp_
#include "GUI_Utils.hpp"
#include "wxExtensions.hpp"
#include "DoubleSlider.hpp"
class wxTextCtrl;
class wxFlexGridSizer;

View File

@ -60,6 +60,7 @@
#include <float.h>
#include <algorithm>
#include <cmath>
#include "DoubleSlider.hpp"
#if ENABLE_RENDER_STATISTICS
#include <chrono>
#endif // ENABLE_RENDER_STATISTICS
@ -892,7 +893,7 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D
std::vector<float>& colors,
std::vector<std::string>& cp_legend_items)
{
std::vector<Model::CustomGCode> custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
const int extruders_cnt = wxGetApp().extruders_edited_cnt();
if (extruders_cnt == 1)
@ -910,7 +911,7 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D
{
if (custom_code.gcode != ColorChangeCode)
continue;
auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.print_z - DoubleSlider::epsilon());
auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.print_z - Slic3r::DoubleSlider::epsilon());
if (lower_b == print_zs.end())
continue;
@ -2410,7 +2411,7 @@ void GLCanvas3D::load_sla_preview()
}
}
void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors, const std::vector<Model::CustomGCode>& color_print_values)
void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values)
{
const Print *print = this->fff_print();
if (print == nullptr)
@ -5245,13 +5246,13 @@ void GLCanvas3D::_load_print_toolpaths()
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<Model::CustomGCode>& color_print_values)
void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values)
{
std::vector<float> tool_colors = _parse_colors(str_tool_colors);
struct Ctxt
{
const Points *shifted_copies;
const PrintInstances *shifted_copies;
std::vector<const Layer*> layers;
bool has_perimeters;
bool has_infill;
@ -5259,7 +5260,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
const std::vector<float>* tool_colors;
bool is_single_material_print;
int extruders_cnt;
const std::vector<Model::CustomGCode>* color_print_values;
const std::vector<CustomGCode::Item>* color_print_values;
static const float* color_perimeters() { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow
static const float* color_infill() { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish
@ -5274,7 +5275,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
// For coloring by a color_print(M600), return a parsed color.
bool color_by_color_print() const { return color_print_values!=nullptr; }
const size_t color_print_color_idx_by_layer_idx(const size_t layer_idx) const {
const Model::CustomGCode value{layers[layer_idx]->print_z + EPSILON, "", 0, ""};
const CustomGCode::Item value{layers[layer_idx]->print_z + EPSILON, "", 0, ""};
auto it = std::lower_bound(color_print_values->begin(), color_print_values->end(), value);
return (it - color_print_values->begin()) % number_tools();
}
@ -5284,7 +5285,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
const coordf_t print_z = layers[layer_idx]->print_z;
auto it = std::find_if(color_print_values->begin(), color_print_values->end(),
[print_z](const Model::CustomGCode& code)
[print_z](const CustomGCode::Item& code)
{ return fabs(code.print_z - print_z) < EPSILON; });
if (it != color_print_values->end())
{
@ -5305,7 +5306,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
}
}
const Model::CustomGCode value{print_z + EPSILON, "", 0, ""};
const CustomGCode::Item value{print_z + EPSILON, "", 0, ""};
it = std::lower_bound(color_print_values->begin(), color_print_values->end(), value);
while (it != color_print_values->begin())
{
@ -5325,7 +5326,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
}
private:
int get_m600_color_idx(std::vector<Model::CustomGCode>::const_iterator it) const
int get_m600_color_idx(std::vector<CustomGCode::Item>::const_iterator it) const
{
int shift = 0;
while (it != color_print_values->begin()) {
@ -5336,7 +5337,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
return extruders_cnt + shift;
}
int get_color_idx_for_tool_change(std::vector<Model::CustomGCode>::const_iterator it, const int extruder) const
int get_color_idx_for_tool_change(std::vector<CustomGCode::Item>::const_iterator it, const int extruder) const
{
const int current_extruder = it->extruder == 0 ? extruder : it->extruder;
if (number_tools() == extruders_cnt + 1) // there is no one "M600"
@ -5352,7 +5353,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
return std::min<int>(extruders_cnt - 1, std::max<int>(current_extruder - 1, 0));
}
int get_color_idx_for_color_change(std::vector<Model::CustomGCode>::const_iterator it, const int extruder) const
int get_color_idx_for_color_change(std::vector<CustomGCode::Item>::const_iterator it, const int extruder) const
{
if (extruders_cnt == 1)
return get_m600_color_idx(it);
@ -5384,7 +5385,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
ctxt.is_single_material_print = this->fff_print()->extruders().size()==1;
ctxt.extruders_cnt = wxGetApp().extruders_edited_cnt();
ctxt.shifted_copies = &print_object.copies();
ctxt.shifted_copies = &print_object.instances();
// order layers by print_z
{
@ -5473,7 +5474,8 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
vol->offsets.push_back(vol->indexed_vertex_array.quad_indices.size());
vol->offsets.push_back(vol->indexed_vertex_array.triangle_indices.size());
}
for (const Point &copy : *ctxt.shifted_copies) {
for (const PrintInstance &instance : *ctxt.shifted_copies) {
const Point &copy = instance.shift;
for (const LayerRegion *layerm : layer->regions()) {
if (is_selected_separate_extruder)
{

View File

@ -558,7 +558,7 @@ public:
void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors);
void load_sla_preview();
void load_preview(const std::vector<std::string>& str_tool_colors, const std::vector<Model::CustomGCode>& color_print_values);
void load_preview(const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values);
void bind_event_handlers();
void unbind_event_handlers();
@ -721,7 +721,7 @@ private:
// Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
// one for perimeters, one for infill and one for supports.
void _load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors,
const std::vector<Model::CustomGCode>& color_print_values);
const std::vector<CustomGCode::Item>& color_print_values);
// Create 3D thick extrusion lines for wipe tower extrusions
void _load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors);

View File

@ -1623,9 +1623,14 @@ void ObjectList::append_menu_item_export_stl(wxMenu* menu) const
void ObjectList::append_menu_item_reload_from_disk(wxMenu* menu) const
{
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
[this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu);
#else
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
[this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu,
[]() { return wxGetApp().plater()->can_reload_from_disk(); }, wxGetApp().plater());
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
}
void ObjectList::append_menu_item_change_extruder(wxMenu* menu) const
@ -3892,10 +3897,15 @@ void ObjectList::show_multi_selection_menu()
_(L("Select extruder number for selected objects and/or parts")),
[this](wxCommandEvent&) { extruder_selection(); }, "", menu);
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
[this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu);
#else
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
[this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu, []() {
return wxGetApp().plater()->can_reload_from_disk();
}, wxGetApp().plater());
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
wxGetApp().plater()->PopupMenu(menu);
}

View File

@ -10,7 +10,7 @@
#include "GLCanvas3DManager.hpp"
#include "GLCanvas3D.hpp"
#include "PresetBundle.hpp"
#include "wxExtensions.hpp"
#include "DoubleSlider.hpp"
#include <wx/notebook.h>
#include <wx/glcanvas.h>
@ -584,7 +584,7 @@ void Preview::update_view_type(bool slice_completed)
void Preview::create_double_slider()
{
m_slider = new DoubleSlider(this, wxID_ANY, 0, 0, 0, 100);
m_slider = new DoubleSlider::Control(this, wxID_ANY, 0, 0, 0, 100);
m_slider->EnableTickManipulation(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF);
m_double_slider_sizer->Add(m_slider, 0, wxEXPAND, 0);
@ -595,7 +595,7 @@ void Preview::create_double_slider()
m_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_sliders_scroll_changed, this);
Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) {
Bind(DoubleSlider::wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) {
Model& model = wxGetApp().plater()->model();
model.custom_gcode_per_print_z = m_slider->GetTicksValues();
m_schedule_background_process();
@ -633,7 +633,7 @@ static int find_close_layer_idx(const std::vector<double>& zs, double &z, double
return -1;
}
void Preview::check_slider_values(std::vector<Model::CustomGCode>& ticks_from_model,
void Preview::check_slider_values(std::vector<CustomGCode::Item>& ticks_from_model,
const std::vector<double>& layers_z)
{
// All ticks that would end up outside the slider range should be erased.
@ -641,7 +641,7 @@ void Preview::check_slider_values(std::vector<Model::CustomGCode>& ticks_from_mo
// this function is e.g. not called when the last object is deleted
unsigned int old_size = ticks_from_model.size();
ticks_from_model.erase(std::remove_if(ticks_from_model.begin(), ticks_from_model.end(),
[layers_z](Model::CustomGCode val)
[layers_z](CustomGCode::Item val)
{
auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.print_z - DoubleSlider::epsilon());
return it == layers_z.end();
@ -669,7 +669,7 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool kee
// Detect and set manipulation mode for double slider
update_double_slider_mode();
Model::CustomGCodeInfo &ticks_info_from_model = wxGetApp().plater()->model().custom_gcode_per_print_z;
CustomGCode::Info &ticks_info_from_model = wxGetApp().plater()->model().custom_gcode_per_print_z;
check_slider_values(ticks_info_from_model.gcodes, layers_z);
m_slider->SetSliderValues(layers_z);
@ -830,7 +830,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
bool gcode_preview_data_valid = print->is_step_done(psGCodeExport) && ! m_gcode_preview_data->empty();
// Collect colors per extruder.
std::vector<std::string> colors;
std::vector<Model::CustomGCode> color_print_values = {};
std::vector<CustomGCode::Item> color_print_values = {};
// set color print values, if it si selected "ColorPrint" view type
if (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint)
{

View File

@ -15,7 +15,6 @@ class wxChoice;
class wxComboCtrl;
class wxBitmapComboBox;
class wxCheckBox;
class DoubleSlider;
namespace Slic3r {
@ -25,6 +24,10 @@ class BackgroundSlicingProcess;
class GCodePreviewData;
class Model;
namespace DoubleSlider {
class Control;
};
namespace GUI {
class GLCanvas3D;
@ -103,7 +106,7 @@ class Preview : public wxPanel
bool m_loaded;
bool m_enabled;
DoubleSlider* m_slider {nullptr};
DoubleSlider::Control* m_slider {nullptr};
public:
Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config,
@ -155,7 +158,7 @@ private:
// Create/Update/Reset double slider on 3dPreview
void create_double_slider();
void check_slider_values(std::vector<Model::CustomGCode> &ticks_from_model,
void check_slider_values(std::vector<CustomGCode::Item> &ticks_from_model,
const std::vector<double> &layers_z);
void reset_double_slider();
void update_double_slider(const std::vector<double>& layers_z, bool keep_z_range = false);

View File

@ -15,6 +15,9 @@
#include <bitset>
//unofficial linux lib
//#include <spnav.h>
// WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules
static const std::vector<int> _3DCONNEXION_VENDORS =
@ -204,7 +207,11 @@ Mouse3DController::Mouse3DController()
, m_device_str("")
, m_running(false)
, m_show_settings_dialog(false)
, m_mac_mouse_connected(false)
, m_settings_dialog_closed_by_user(false)
#if __APPLE__
,m_handler_mac(new Mouse3DHandlerMac(this))
#endif //__APPLE__
{
m_last_time = std::chrono::high_resolution_clock::now();
}
@ -244,7 +251,7 @@ bool Mouse3DController::apply(Camera& camera)
return false;
// check if the user unplugged the device
if (!m_running && is_device_connected())
if (!is_running() && is_device_connected())
{
disconnect_device();
// hides the settings dialog if the user un-plug the device
@ -261,7 +268,7 @@ bool Mouse3DController::apply(Camera& camera)
void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
{
if (!m_running || !m_show_settings_dialog)
if (!is_running() || !m_show_settings_dialog)
return;
// when the user clicks on [X] or [Close] button we need to trigger
@ -397,6 +404,9 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
bool Mouse3DController::connect_device()
{
#ifdef __APPLE__
return false;
#endif//__APPLE__
static const long long DETECTION_TIME_MS = 2000; // two seconds
if (is_device_connected())
@ -405,7 +415,7 @@ bool Mouse3DController::connect_device()
// check time since last detection took place
if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_last_time).count() < DETECTION_TIME_MS)
return false;
m_last_time = std::chrono::high_resolution_clock::now();
// Enumerates devices
@ -528,7 +538,7 @@ bool Mouse3DController::connect_device()
{
if (device.second.size() == 1)
{
#ifdef __linux__
#if defined(__linux__)
hid_device* test_device = hid_open(device.first.first, device.first.second, nullptr);
if (test_device != nullptr)
{
@ -536,7 +546,7 @@ bool Mouse3DController::connect_device()
#else
if (device.second.front().has_valid_usage())
{
#endif // __linux__
#endif // __linux__
vendor_id = device.first.first;
product_id = device.first.second;
break;
@ -553,6 +563,7 @@ bool Mouse3DController::connect_device()
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std::cout << "Test device: " << std::hex << device.first.first << std::dec << "/" << std::hex << device.first.second << std::dec << " \"" << data.path << "\"";
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
#ifdef __linux__
hid_device* test_device = hid_open_path(data.path.c_str());
if (test_device != nullptr)
@ -567,7 +578,7 @@ bool Mouse3DController::connect_device()
hid_close(test_device);
break;
}
#else
#else // !__linux__
if (data.has_valid_usage())
{
path = data.path;
@ -632,7 +643,9 @@ bool Mouse3DController::connect_device()
BOOST_LOG_TRIVIAL(info) << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")";
if (!path.empty())
BOOST_LOG_TRIVIAL(info) << "Path................: '" << path << "'";
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std::cout << "Opened device." << std::endl;
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
// get device parameters from the config, if present
double translation_speed = 4.0;
float rotation_speed = 4.0;
@ -717,7 +730,7 @@ void Mouse3DController::run()
}
void Mouse3DController::collect_input()
{
DataPacket packet = { 0 };
DataPacketRaw packet = { 0 };
int res = hid_read_timeout(m_device, packet.data(), packet.size(), 100);
if (res < 0)
{
@ -725,10 +738,47 @@ void Mouse3DController::collect_input()
stop();
return;
}
handle_input(packet, res);
}
void Mouse3DController::handle_input_axis(const DataPacketAxis& packet)
{
if (!wxGetApp().IsActive())
return;
bool appended = false;
//translation
double deadzone = m_state.get_translation_deadzone();
Vec3d translation(std::abs(packet[0]) > deadzone ? -packet[0] : 0.0,
std::abs(packet[1]) > deadzone ? packet[1] : 0.0,
std::abs(packet[2]) > deadzone ? packet[2] : 0.0);
if (!translation.isApprox(Vec3d::Zero()))
{
m_state.append_translation(translation);
appended = true;
}
//rotation
deadzone = m_state.get_rotation_deadzone();
Vec3f rotation(std::abs(packet[3]) > deadzone ? (float)packet[3] : 0.0,
std::abs(packet[4]) > deadzone ? (float)packet[4] : 0.0,
std::abs(packet[5]) > deadzone ? (float)packet[5] : 0.0);
if (!rotation.isApprox(Vec3f::Zero()))
{
m_state.append_rotation(rotation);
appended = true;
}
if (appended)
{
wxGetApp().plater()->set_current_canvas_as_dirty();
// ask for an idle event to update 3D scene
wxWakeUpIdle();
}
}
void Mouse3DController::handle_input(const DataPacketRaw& packet, const int packet_lenght)
{
if (!wxGetApp().IsActive())
return;
int res = packet_lenght;
bool updated = false;
if (res == 7)
@ -751,7 +801,7 @@ void Mouse3DController::collect_input()
}
}
bool Mouse3DController::handle_packet(const DataPacket& packet)
bool Mouse3DController::handle_packet(const DataPacketRaw& packet)
{
switch (packet[0])
{
@ -795,7 +845,7 @@ bool Mouse3DController::handle_packet(const DataPacket& packet)
return false;
}
bool Mouse3DController::handle_wireless_packet(const DataPacket& packet)
bool Mouse3DController::handle_wireless_packet(const DataPacketRaw& packet)
{
switch (packet[0])
{
@ -842,7 +892,7 @@ double convert_input(unsigned char first, unsigned char second, double deadzone)
return (std::abs(ret) > deadzone) ? ret : 0.0;
}
bool Mouse3DController::handle_packet_translation(const DataPacket& packet)
bool Mouse3DController::handle_packet_translation(const DataPacketRaw& packet)
{
double deadzone = m_state.get_translation_deadzone();
Vec3d translation(-convert_input(packet[1], packet[2], deadzone),
@ -858,7 +908,7 @@ bool Mouse3DController::handle_packet_translation(const DataPacket& packet)
return false;
}
bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigned int first_byte)
bool Mouse3DController::handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte)
{
double deadzone = (double)m_state.get_rotation_deadzone();
#if ENABLE_6DOF_CAMERA
@ -880,7 +930,7 @@ bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigne
return false;
}
bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned int packet_size)
bool Mouse3DController::handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size)
{
unsigned int data = 0;
for (unsigned int i = 1; i < packet_size; ++i)

View File

@ -14,9 +14,15 @@
#include <vector>
#include <chrono>
namespace Slic3r {
namespace GUI {
#if __APPLE__
class Mouse3DHandlerMac;
#endif//__APPLE__
struct Camera;
class GLCanvas3D;
@ -141,6 +147,7 @@ class Mouse3DController
hid_device* m_device;
std::string m_device_str;
bool m_running;
bool m_mac_mouse_connected;
mutable bool m_show_settings_dialog;
// set to true when ther user closes the dialog by clicking on [X] or [Close] buttons
mutable bool m_settings_dialog_closed_by_user;
@ -152,9 +159,11 @@ public:
void init();
void shutdown();
bool is_device_connected() const { return m_device != nullptr; }
bool is_running() const { return m_running; }
bool is_device_connected() const { return m_device != nullptr || m_mac_mouse_connected; }
bool is_running() const { return m_running || m_mac_mouse_connected; }
void set_mac_mouse_connected(bool b){m_mac_mouse_connected = b;};
bool process_mouse_wheel() { return m_state.process_mouse_wheel(); }
bool apply(Camera& camera);
@ -163,26 +172,43 @@ public:
void show_settings_dialog(bool show) { m_show_settings_dialog = show && is_running(); }
void render_settings_dialog(GLCanvas3D& canvas) const;
typedef std::array<double, 6> DataPacketAxis;
void handle_input_axis(const DataPacketAxis& packet);
private:
bool connect_device();
void disconnect_device();
void start();
void stop() { m_running = false; }
typedef std::array<unsigned char, 13> DataPacketRaw;
// secondary thread methods
void run();
void collect_input();
void handle_input(const DataPacketRaw& packet, const int packet_lenght);
bool handle_packet(const DataPacketRaw& packet);
bool handle_wireless_packet(const DataPacketRaw& packet);
bool handle_packet_translation(const DataPacketRaw& packet);
bool handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte);
bool handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size);
typedef std::array<unsigned char, 13> DataPacket;
bool handle_packet(const DataPacket& packet);
bool handle_wireless_packet(const DataPacket& packet);
bool handle_packet_translation(const DataPacket& packet);
bool handle_packet_rotation(const DataPacket& packet, unsigned int first_byte);
bool handle_packet_button(const DataPacket& packet, unsigned int packet_size);
#if __APPLE__
Mouse3DHandlerMac* m_handler_mac;
#endif//__APPLE__
};
#if __APPLE__
class Mouse3DHandlerMac{
public:
Mouse3DHandlerMac(Mouse3DController* controller);
~Mouse3DHandlerMac();
bool available();
};
#endif//__APPLE__
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_Mouse3DController_hpp_

View File

@ -0,0 +1,255 @@
#include "Mouse3DController.hpp"
#include <stdint.h>
#include <dlfcn.h>
#include <array>
#include <boost/log/trivial.hpp>
#include <cstdio>
static Slic3r::GUI::Mouse3DController* mouse_3d_controller = NULL;
static uint16_t clientID = 0;
static bool driver_loaded = false;
static bool has_new_driver = false; // drivers >= 10.2.2 are "new", and can process events on a separate thread
// replicate just enough of the 3Dx API for our uses, not everything the driver provides
#define kConnexionClientModeTakeOver 1
#define kConnexionMaskAxis 0x3f00
#define kConnexionMaskAll 0x3fff
#define kConnexionMaskAllButtons 0xffffffff
#define kConnexionCmdHandleButtons 2
#define kConnexionCmdHandleAxis 3
#define kConnexionCmdAppSpecific 10
#define kConnexionMsgDeviceState '3dSR'
#define kConnexionCtlGetDeviceID '3did'
#pragma pack(push, 2)
struct ConnexionDeviceState {
uint16_t version;
uint16_t client;
uint16_t command;
int16_t param;
int32_t value;
uint64_t time;
uint8_t report[8];
uint16_t buttons8; // obsolete! (pre-10.x drivers)
int16_t axis[6]; // tx, ty, tz, rx, ry, rz
uint16_t address;
uint32_t buttons;
};
#pragma pack(pop)
// callback functions:
typedef void (*AddedHandler)(uint32_t);
typedef void (*RemovedHandler)(uint32_t);
typedef void (*MessageHandler)(uint32_t, uint32_t msg_type, void *msg_arg);
// driver functions:
typedef int16_t (*SetConnexionHandlers_ptr)(MessageHandler, AddedHandler, RemovedHandler, bool);
typedef int16_t (*InstallConnexionHandlers_ptr)(MessageHandler, AddedHandler, RemovedHandler);
typedef void (*CleanupConnexionHandlers_ptr)();
typedef uint16_t (*RegisterConnexionClient_ptr)(uint32_t signature,
const char *name,
uint16_t mode,
uint32_t mask);
typedef void (*SetConnexionClientButtonMask_ptr)(uint16_t clientID, uint32_t buttonMask);
typedef void (*UnregisterConnexionClient_ptr)(uint16_t clientID);
typedef int16_t (*ConnexionClientControl_ptr)(uint16_t clientID,
uint32_t message,
int32_t param,
int32_t *result);
#define DECLARE_FUNC(name) name##_ptr name = NULL
DECLARE_FUNC(SetConnexionHandlers);
DECLARE_FUNC(InstallConnexionHandlers);
DECLARE_FUNC(CleanupConnexionHandlers);
DECLARE_FUNC(RegisterConnexionClient);
DECLARE_FUNC(SetConnexionClientButtonMask);
DECLARE_FUNC(UnregisterConnexionClient);
DECLARE_FUNC(ConnexionClientControl);
static void *load_func(void *module, const char *func_name)
{
void *func = dlsym(module, func_name);
//#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
if (func) {
BOOST_LOG_TRIVIAL(info) << func_name <<" loaded";
}
else {
//printf("<!> %s\n", dlerror());
BOOST_LOG_TRIVIAL(error) <<"loading 3dx drivers dlsym error: "<< dlerror();
}
//#endif
return func;
}
#define LOAD_FUNC(name) name = (name##_ptr)load_func(module, #name)
static void *module; // handle to the whole driver
static bool load_driver_functions()
{
if (driver_loaded) {
return true;
}
module = dlopen("/Library/Frameworks/3DconnexionClient.framework/3DconnexionClient",
RTLD_LAZY | RTLD_LOCAL);
if (module) {
BOOST_LOG_TRIVIAL(info) << "loading 3dx drivers";
LOAD_FUNC(SetConnexionHandlers);
if (SetConnexionHandlers != NULL) {
driver_loaded = true;
has_new_driver = true;
}
else {
BOOST_LOG_TRIVIAL(info) << "installing 3dx drivers";
LOAD_FUNC(InstallConnexionHandlers);
driver_loaded = (InstallConnexionHandlers != NULL);
}
if (driver_loaded) {
LOAD_FUNC(CleanupConnexionHandlers);
LOAD_FUNC(RegisterConnexionClient);
LOAD_FUNC(SetConnexionClientButtonMask);
LOAD_FUNC(UnregisterConnexionClient);
LOAD_FUNC(ConnexionClientControl);
}
}
else {
BOOST_LOG_TRIVIAL(error) << "3dx drivers module loading error: "<< dlerror() ;
#if DENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
printf("<!> %s\n", dlerror());
#endif
}
#if DENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
printf("loaded: %s\n", driver_loaded ? "YES" : "NO");
printf("new: %s\n", has_new_driver ? "YES" : "NO");
#endif
BOOST_LOG_TRIVIAL(info) << "3dx drivers loaded: "<< driver_loaded ? "YES" : "NO" ;
return driver_loaded;
}
static void unload_driver()
{
dlclose(module);
}
static void DeviceAdded(uint32_t unused)
{
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std::cout<<"3D device added"<<std::endl;
#endif
BOOST_LOG_TRIVIAL(info)<<"3dx device added";
// determine exactly which device is plugged in
int32_t result;
ConnexionClientControl(clientID, kConnexionCtlGetDeviceID, 0, &result);
int16_t vendorID = result >> 16;
int16_t productID = result & 0xffff;
//TODO: verify device
mouse_3d_controller->set_mac_mouse_connected(true);
}
static void DeviceRemoved(uint32_t unused)
{
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
printf("3d device removed\n");
#endif
BOOST_LOG_TRIVIAL(info) << "3dx device removed\n";
mouse_3d_controller->set_mac_mouse_connected(true);
}
static void DeviceEvent(uint32_t unused, uint32_t msg_type, void *msg_arg)
{
if (msg_type == kConnexionMsgDeviceState) {
ConnexionDeviceState *s = (ConnexionDeviceState *)msg_arg;
if (s->client == clientID) {
switch (s->command) {
case kConnexionCmdHandleAxis: {
/*
The axis field is an array of 6 signed 16-bit integers corresponding to the 6 device axes. Data is ordered as Tx, Tz, Ty, Rx, Rz, Ry. The values reported are scaled by the driver according to the speed slider settings on the 3Dconnexion preference panel. At maximum speed, the range is - 1024 to 1024. Typical range that you should optimize your application for should be -500 to 500.
*/
//Actually we are getting values way over 1024. Max is probably 2048 now.
std::array<double, 6> packet;
for (int i = 0; i < 6; i++) {
packet[i] = (double)s->axis[i]/350.0;//wanted to divide by 500 but 350 is used at raw input so i used same value.
}
mouse_3d_controller->handle_input_axis(packet);
break;
}
case kConnexionCmdHandleButtons:
break;
case kConnexionCmdAppSpecific:
break;
default:
break;
}
}
}
}
namespace Slic3r {
namespace GUI {
Mouse3DHandlerMac::Mouse3DHandlerMac(Mouse3DController* controller)
{
BOOST_LOG_TRIVIAL(info) << "3dx mac handler starts";
if (load_driver_functions()) {
mouse_3d_controller = controller;
uint16_t error;
if (has_new_driver) {
error = SetConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved, false);
}
else {
error = InstallConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved);
}
if (error) {
return;
}
// Registration is done either by 4letter constant (CFBundleSignature - obsolete
//and we dont have that) or Executable name in pascal string(first byte is string lenght).
//If no packets are recieved the name might be different - check cmake. If debugging try commenting
// set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
clientID = RegisterConnexionClient(
0, "\013PrusaSlicer", kConnexionClientModeTakeOver, kConnexionMaskAxis);
BOOST_LOG_TRIVIAL(info) << "3dx mac handler registered";
}
}
Mouse3DHandlerMac::~Mouse3DHandlerMac()
{
if (driver_loaded) {
UnregisterConnexionClient(clientID);
CleanupConnexionHandlers();
unload_driver();
}
mouse_3d_controller = nullptr;
}
bool Mouse3DHandlerMac::available()
{
return driver_loaded;
}
}}//namespace Slic3r::GUI

View File

@ -26,6 +26,9 @@
#include <wx/colordlg.h>
#include <wx/numdlg.h>
#include <wx/debug.h>
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
#include <wx/busyinfo.h>
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
#include "libslic3r/libslic3r.h"
#include "libslic3r/Format/STL.hpp"
@ -84,6 +87,7 @@
#include <wx/glcanvas.h> // Needs to be last because reasons :-/
#include "WipeTowerDialog.hpp"
#include "libslic3r/CustomGCode.hpp"
using boost::optional;
namespace fs = boost::filesystem;
@ -704,6 +708,7 @@ struct Sidebar::priv
wxButton *btn_reslice;
ScalableButton *btn_send_gcode;
ScalableButton *btn_remove_device;
ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected)
priv(Plater *plater) : plater(plater) {}
~priv();
@ -869,6 +874,7 @@ Sidebar::Sidebar(Plater *parent)
init_scalable_btn(&p->btn_send_gcode , "export_gcode", _(L("Send to printer")));
init_scalable_btn(&p->btn_remove_device, "cross" , _(L("Remove device")));
init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _(L("Export to SD card/ USB thumb drive")));
// regular buttons "Slice now" and "Export G-code"
@ -889,7 +895,9 @@ Sidebar::Sidebar(Plater *parent)
auto* complect_btns_sizer = new wxBoxSizer(wxHORIZONTAL);
complect_btns_sizer->Add(p->btn_export_gcode, 1, wxEXPAND);
complect_btns_sizer->Add(p->btn_send_gcode);
complect_btns_sizer->Add(p->btn_export_gcode_removable);
complect_btns_sizer->Add(p->btn_remove_device);
btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5);
btns_sizer->Add(complect_btns_sizer, 0, wxEXPAND | wxTOP, margin_5);
@ -900,7 +908,7 @@ Sidebar::Sidebar(Plater *parent)
SetSizer(sizer);
// Events
p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(); });
p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(false); });
p->btn_reslice->Bind(wxEVT_BUTTON, [this](wxCommandEvent&)
{
const bool export_gcode_after_slicing = wxGetKeyState(WXK_SHIFT);
@ -912,6 +920,7 @@ Sidebar::Sidebar(Plater *parent)
});
p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); });
p->btn_remove_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); });
p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); });
}
Sidebar::~Sidebar() {}
@ -1059,6 +1068,7 @@ void Sidebar::msw_rescale()
p->btn_send_gcode->msw_rescale();
p->btn_remove_device->msw_rescale();
p->btn_export_gcode_removable->msw_rescale();
const int scaled_height = p->btn_remove_device->GetBitmap().GetHeight() + 4;
p->btn_export_gcode->SetMinSize(wxSize(-1, scaled_height));
p->btn_reslice ->SetMinSize(wxSize(-1, scaled_height));
@ -1292,12 +1302,14 @@ void Sidebar::enable_buttons(bool enable)
p->btn_export_gcode->Enable(enable);
p->btn_send_gcode->Enable(enable);
p->btn_remove_device->Enable(enable);
p->btn_export_gcode_removable->Enable(enable);
}
bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); }
bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); }
bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); }
bool Sidebar::show_disconnect(bool show)const { return p->btn_remove_device->Show(show); }
bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); }
bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); }
bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); }
bool Sidebar::show_disconnect(bool show) const { return p->btn_remove_device->Show(show); }
bool Sidebar::show_export_removable(bool show)const { return p->btn_export_gcode_removable->Show(show); }
bool Sidebar::is_multifilament()
{
@ -1773,6 +1785,9 @@ struct Plater::priv
bool is_view3D_shown() const { return current_panel == view3D; }
void set_current_canvas_as_dirty();
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
GLCanvas3D* get_current_canvas3D();
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
bool init_view_toolbar();
@ -1902,7 +1917,9 @@ struct Plater::priv
bool can_fix_through_netfabb() const;
bool can_set_instance_to_object() const;
bool can_mirror() const;
#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
bool can_reload_from_disk() const;
#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
#if ENABLE_THUMBNAIL_GENERATOR
void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
@ -2110,6 +2127,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
// Initialize the Undo / Redo stack with a first snapshot.
this->take_snapshot(_(L("New Project")));
//void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
RemovableDriveManager::get_instance().set_drive_count_changed_callback(std::bind(&Plater::priv::show_action_buttons, this, std::placeholders::_1));
}
Plater::priv::~priv()
@ -3230,6 +3250,10 @@ void Plater::priv::reload_from_disk()
else
missing_input_paths.push_back(volume->source.input_file);
}
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
else if (!volume->name.empty())
missing_input_paths.push_back(volume->name);
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
}
std::sort(missing_input_paths.begin(), missing_input_paths.end());
@ -3241,10 +3265,9 @@ void Plater::priv::reload_from_disk()
fs::path search = missing_input_paths.back();
wxString title = _(L("Please select the file to reload"));
#if defined(__APPLE__)
title += " (" + from_u8(search.filename().string()) + "):";
#else
title += ":";
title += " (" + from_u8(search.filename().string()) + ")";
#endif // __APPLE__
title += ":";
wxFileDialog dialog(q, title, "", from_u8(search.filename().string()), file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() != wxID_OK)
return;
@ -3285,10 +3308,20 @@ void Plater::priv::reload_from_disk()
std::sort(input_paths.begin(), input_paths.end());
input_paths.erase(std::unique(input_paths.begin(), input_paths.end()), input_paths.end());
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
std::vector<wxString> fail_list;
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
// load one file at a time
for (size_t i = 0; i < input_paths.size(); ++i)
{
const auto& path = input_paths[i].string();
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
wxBusyCursor wait;
wxBusyInfo info(_(L("Reload from: ")) + from_u8(path), q->get_current_canvas3D()->get_wxglcanvas());
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
Model new_model;
try
{
@ -3306,18 +3339,70 @@ void Plater::priv::reload_from_disk()
}
// update the selected volumes whose source is the current file
for (const SelectedVolume& old_v : selected_volumes)
for (const SelectedVolume& sel_v : selected_volumes)
{
ModelObject* old_model_object = model.objects[old_v.object_idx];
ModelVolume* old_volume = old_model_object->volumes[old_v.volume_idx];
ModelObject* old_model_object = model.objects[sel_v.object_idx];
ModelVolume* old_volume = old_model_object->volumes[sel_v.volume_idx];
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
bool has_source = !old_volume->source.input_file.empty() && boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(), fs::path(path).filename().string());
bool has_name = !old_volume->name.empty() && boost::algorithm::iequals(old_volume->name, fs::path(path).filename().string());
if (has_source || has_name)
#else
int new_volume_idx = old_volume->source.volume_idx;
int new_object_idx = old_volume->source.object_idx;
if (boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(),
fs::path(path).filename().string()))
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
{
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
int new_volume_idx = -1;
int new_object_idx = -1;
if (has_source)
{
// take idxs from source
new_volume_idx = old_volume->source.volume_idx;
new_object_idx = old_volume->source.object_idx;
}
else
{
// take idxs from the 1st matching volume
for (size_t o = 0; o < new_model.objects.size(); ++o)
{
ModelObject* obj = new_model.objects[o];
bool found = false;
for (size_t v = 0; v < obj->volumes.size(); ++v)
{
if (obj->volumes[v]->name == old_volume->name)
{
new_volume_idx = (int)v;
new_object_idx = (int)o;
found = true;
break;
}
}
if (found)
break;
}
}
if ((new_object_idx < 0) && ((int)new_model.objects.size() <= new_object_idx))
{
fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name));
continue;
}
#else
assert(new_object_idx < (int)new_model.objects.size());
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
ModelObject* new_model_object = new_model.objects[new_object_idx];
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
if ((new_volume_idx < 0) && ((int)new_model.objects.size() <= new_volume_idx))
{
fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name));
continue;
}
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
if (new_volume_idx < (int)new_model_object->volumes.size())
{
old_model_object->add_volume(*new_model_object->volumes[new_volume_idx]);
@ -3328,8 +3413,10 @@ void Plater::priv::reload_from_disk()
new_volume->set_material_id(old_volume->material_id());
new_volume->set_transformation(old_volume->get_transformation() * old_volume->source.transform);
new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset));
#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
new_volume->source.input_file = path;
std::swap(old_model_object->volumes[old_v.volume_idx], old_model_object->volumes.back());
#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back());
old_model_object->delete_volume(old_model_object->volumes.size() - 1);
old_model_object->ensure_on_bed();
}
@ -3337,6 +3424,19 @@ void Plater::priv::reload_from_disk()
}
}
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
if (!fail_list.empty())
{
wxString message = _(L("Unable to reload:")) + "\n";
for (const wxString& s : fail_list)
{
message += s + "\n";
}
wxMessageDialog dlg(q, message, _(L("Error during reload")), wxOK | wxOK_DEFAULT | wxICON_WARNING);
dlg.ShowModal();
}
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
// update 3D scene
update();
@ -3582,12 +3682,8 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
if(!canceled && RemovableDriveManager::get_instance().get_is_writing())
{
//if (!RemovableDriveManager::get_instance().is_last_drive_removed())
//{
RemovableDriveManager::get_instance().set_is_writing(false);
show_action_buttons(false);
//}
RemovableDriveManager::get_instance().set_is_writing(false);
show_action_buttons(false);
}
}
@ -3817,8 +3913,13 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")),
[this](wxCommandEvent&) { q->remove_selected(); }, "delete", nullptr, [this]() { return can_delete(); }, q);
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
[this](wxCommandEvent&) { q->reload_from_disk(); }, "", menu);
#else
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
[this](wxCommandEvent&) { q->reload_from_disk(); }, "", menu, [this]() { return can_reload_from_disk(); }, q);
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
sidebar->obj_list()->append_menu_item_export_stl(menu);
}
@ -3846,8 +3947,13 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
wxMenuItem* menu_item_printable = sidebar->obj_list()->append_menu_item_printable(menu, q);
menu->AppendSeparator();
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected object from disk")),
[this](wxCommandEvent&) { reload_from_disk(); }, "", nullptr);
#else
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected object from disk")),
[this](wxCommandEvent&) { reload_from_disk(); }, "", nullptr, [this]() { return can_reload_from_disk(); }, q);
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, _(L("Export the selected object as STL file")),
[this](wxCommandEvent&) { q->export_stl(false, true); }, "", nullptr,
@ -3950,6 +4056,13 @@ void Plater::priv::set_current_canvas_as_dirty()
preview->set_as_dirty();
}
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
GLCanvas3D* Plater::priv::get_current_canvas3D()
{
return (current_panel == view3D) ? view3D->get_canvas3d() : ((current_panel == preview) ? preview->get_canvas3d() : nullptr);
}
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
bool Plater::priv::init_view_toolbar()
{
if (view_toolbar.get_items_count() > 0)
@ -4020,6 +4133,7 @@ bool Plater::priv::can_mirror() const
return get_selection().is_from_single_instance();
}
#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
bool Plater::priv::can_reload_from_disk() const
{
// struct to hold selected ModelVolumes by their indices
@ -4065,6 +4179,7 @@ bool Plater::priv::can_reload_from_disk() const
return !paths.empty();
}
#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model)
{
@ -4142,18 +4257,21 @@ void Plater::priv::update_object_menu()
void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
{
RemovableDriveManager::get_instance().set_plater_ready_to_slice(is_ready_to_slice);
wxWindowUpdateLocker noUpdater(sidebar);
const auto prin_host_opt = config->option<ConfigOptionString>("print_host");
const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty();
bool disconnect_shown = !RemovableDriveManager::get_instance().is_last_drive_removed() ; // #dk_FIXME
bool disconnect_shown = !RemovableDriveManager::get_instance().is_last_drive_removed();
bool export_removable_shown = RemovableDriveManager::get_instance().get_drives_count() > 0;
// when a background processing is ON, export_btn and/or send_btn are showing
if (wxGetApp().app_config->get("background_processing") == "1")
{
if (sidebar->show_reslice(false) |
sidebar->show_export(true) |
sidebar->show_send(send_gcode_shown) |
sidebar->show_disconnect(disconnect_shown))
if (sidebar->show_reslice(false) |
sidebar->show_export(true) |
sidebar->show_send(send_gcode_shown) |
sidebar->show_export_removable(export_removable_shown) |
sidebar->show_disconnect(disconnect_shown))
sidebar->Layout();
}
else
@ -4161,6 +4279,7 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
if (sidebar->show_reslice(is_ready_to_slice) |
sidebar->show_export(!is_ready_to_slice) |
sidebar->show_send(send_gcode_shown && !is_ready_to_slice) |
sidebar->show_export_removable(export_removable_shown && !is_ready_to_slice) |
sidebar->show_disconnect(disconnect_shown && !is_ready_to_slice))
sidebar->Layout();
}
@ -4537,7 +4656,7 @@ void Plater::remove(size_t obj_idx) { p->remove(obj_idx); }
void Plater::reset() { p->reset(); }
void Plater::reset_with_confirm()
{
if (wxMessageDialog((wxWindow*)this, _(L("All objects will be removed, continue ?")), wxString(SLIC3R_APP_NAME) + " - " + _(L("Delete all")), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES)
if (wxMessageDialog((wxWindow*)this, _(L("All objects will be removed, continue?")), wxString(SLIC3R_APP_NAME) + " - " + _(L("Delete all")), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES)
reset();
}
@ -4670,7 +4789,7 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe
}
}
void Plater::export_gcode()
void Plater::export_gcode(bool prefer_removable)
{
if (p->model.objects.empty())
return;
@ -4692,11 +4811,19 @@ void Plater::export_gcode()
}
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string());
if (GUI::RemovableDriveManager::get_instance().update())
bool removable_drives_connected = GUI::RemovableDriveManager::get_instance().update();
if(prefer_removable)
{
if (!RemovableDriveManager::get_instance().is_path_on_removable_drive(start_dir))
if(removable_drives_connected)
{
start_dir = RemovableDriveManager::get_instance().get_drive_path();
auto start_dir_removable = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string(), true);
if (RemovableDriveManager::get_instance().is_path_on_removable_drive(start_dir_removable))
{
start_dir = start_dir_removable;
}else
{
start_dir = RemovableDriveManager::get_instance().get_drive_path();
}
}
}
wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save SL1 file as:")),
@ -4709,7 +4836,7 @@ void Plater::export_gcode()
fs::path output_path;
if (dlg.ShowModal() == wxID_OK) {
fs::path path = into_path(dlg.GetPath());
wxGetApp().app_config->update_last_output_dir(path.parent_path().string());
wxGetApp().app_config->update_last_output_dir(path.parent_path().string(), RemovableDriveManager::get_instance().is_path_on_removable_drive(path.parent_path().string()));
output_path = std::move(path);
}
if (! output_path.empty())
@ -4725,7 +4852,7 @@ void Plater::export_gcode()
{
RemovableDriveManager::get_instance().set_is_writing(true);
RemovableDriveManager::get_instance().erase_callbacks();
RemovableDriveManager::get_instance().add_callback(std::bind(&Plater::drive_ejected_callback, this));
RemovableDriveManager::get_instance().add_remove_callback(std::bind(&Plater::drive_ejected_callback, this));
}
}
@ -5032,8 +5159,8 @@ void Plater::send_gcode()
void Plater::eject_drive()
{
RemovableDriveManager::get_instance().update(0, true);
//RemovableDriveManager::get_instance().erase_callbacks();
//RemovableDriveManager::get_instance().add_callback(std::bind(&Plater::drive_ejected_callback, this));
RemovableDriveManager::get_instance().erase_callbacks();
RemovableDriveManager::get_instance().add_remove_callback(std::bind(&Plater::drive_ejected_callback, this));
RemovableDriveManager::get_instance().eject_drive(RemovableDriveManager::get_instance().get_last_save_path());
}
@ -5042,7 +5169,7 @@ void Plater::drive_ejected_callback()
if (RemovableDriveManager::get_instance().get_did_eject())
{
RemovableDriveManager::get_instance().set_did_eject(false);
wxString message = "Unmounting succesesful. The device " + RemovableDriveManager::get_instance().get_last_save_name() + "(" + RemovableDriveManager::get_instance().get_last_save_path() + ")" + " can now be safely removed from the computer.";
wxString message = "Unmounting successful. The device " + RemovableDriveManager::get_instance().get_ejected_name() + "(" + RemovableDriveManager::get_instance().get_ejected_path() + ")" + " can now be safely removed from the computer.";
wxMessageBox(message);
}
p->show_action_buttons(false);
@ -5288,7 +5415,7 @@ std::vector<std::string> Plater::get_colors_for_color_print() const
std::vector<std::string> colors = get_extruder_colors_from_plater_config();
colors.reserve(colors.size() + p->model.custom_gcode_per_print_z.gcodes.size());
for (const Model::CustomGCode& code : p->model.custom_gcode_per_print_z.gcodes)
for (const CustomGCode::Item& code : p->model.custom_gcode_per_print_z.gcodes)
if (code.gcode == ColorChangeCode)
colors.emplace_back(code.color);
@ -5325,6 +5452,13 @@ GLCanvas3D* Plater::canvas3D()
return p->view3D->get_canvas3d();
}
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
GLCanvas3D* Plater::get_current_canvas3D()
{
return p->get_current_canvas3D();
}
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
BoundingBoxf Plater::bed_shape_bb() const
{
return p->bed_shape_bb();
@ -5517,7 +5651,9 @@ bool Plater::can_copy_to_clipboard() const
bool Plater::can_undo() const { return p->undo_redo_stack().has_undo_snapshot(); }
bool Plater::can_redo() const { return p->undo_redo_stack().has_redo_snapshot(); }
#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); }
#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); }
void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); }
void Plater::leave_gizmos_stack() { p->leave_gizmos_stack(); }

View File

@ -120,6 +120,7 @@ public:
bool show_export(bool show) const;
bool show_send(bool show) const;
bool show_disconnect(bool show)const;
bool show_export_removable(bool show) const;
bool is_multifilament();
void update_mode();
@ -186,7 +187,7 @@ public:
void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false);
void export_gcode();
void export_gcode(bool prefer_removable = true);
void export_stl(bool extended = false, bool selection_only = false);
void export_amf();
void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
@ -239,6 +240,9 @@ public:
int get_selected_object_idx();
bool is_single_full_object_selection() const;
GLCanvas3D* canvas3D();
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
GLCanvas3D* get_current_canvas3D();
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
BoundingBoxf bed_shape_bb() const;
void set_current_canvas_as_dirty();
@ -263,7 +267,9 @@ public:
bool can_copy_to_clipboard() const;
bool can_undo() const;
bool can_redo() const;
#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
bool can_reload_from_disk() const;
#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
void msw_rescale();

View File

@ -604,6 +604,7 @@ void PresetCollection::reset(bool delete_files)
m_presets.erase(m_presets.begin() + m_num_default_presets, m_presets.end());
this->select_preset(0);
}
m_map_alias_to_profile_name.clear();
m_map_system_profile_renamed.clear();
}
@ -948,15 +949,15 @@ PresetWithVendorProfile PresetCollection::get_preset_with_vendor_profile(const P
const std::string& PresetCollection::get_preset_name_by_alias(const std::string& alias) const
{
for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) {
const Preset& preset = this->m_presets[i];
if (!preset.is_visible || (!preset.is_compatible && i != m_idx_selected))
continue;
if (preset.alias == alias)
return preset.name;
}
for (
// Find the 1st profile name with the alias.
auto it = Slic3r::lower_bound_by_predicate(m_map_alias_to_profile_name.begin(), m_map_alias_to_profile_name.end(), [&alias](auto &l){ return l.first < alias; });
// Continue over all profile names with the same alias.
it != m_map_alias_to_profile_name.end() && it->first == alias; ++ it)
if (auto it_preset = this->find_preset_internal(it->second);
it_preset != m_presets.end() && it_preset->name == it->second &&
it_preset->is_visible && (it_preset->is_compatible || (it_preset - m_presets.begin()) == m_idx_selected))
return it_preset->name;
return alias;
}
@ -1430,6 +1431,14 @@ std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&othe
return duplicates;
}
void PresetCollection::update_map_alias_to_profile_name()
{
m_map_alias_to_profile_name.clear();
for (const Preset &preset : m_presets)
m_map_alias_to_profile_name.emplace_back(preset.alias, preset.name);
std::sort(m_map_alias_to_profile_name.begin(), m_map_alias_to_profile_name.end(), [](auto &l, auto &r) { return l.first < r.first; });
}
void PresetCollection::update_map_system_profile_renamed()
{
m_map_system_profile_renamed.clear();

View File

@ -166,7 +166,7 @@ public:
DynamicPrintConfig config;
// Alias of the preset
std::string alias = "";
std::string alias;
// List of profile names, from which this profile was renamed at some point of time.
// This list is then used to match profiles by their names when loaded from .gcode, .3mf, .amf,
// and to match the "inherits" field of user profiles with updated system profiles.
@ -356,7 +356,7 @@ public:
// used to update preset_choice from Tab
const std::deque<Preset>& get_presets() const { return m_presets; }
int get_idx_selected() { return m_idx_selected; }
int get_idx_selected() { return m_idx_selected; }
static const std::string& get_suffix_modified();
// Return a preset possibly with modifications.
@ -475,6 +475,9 @@ protected:
// Merge one vendor's presets with the other vendor's presets, report duplicates.
std::vector<std::string> merge_presets(PresetCollection &&other, const VendorMap &new_vendors);
// Update m_map_alias_to_profile_name from loaded system profiles.
void update_map_alias_to_profile_name();
// Update m_map_system_profile_renamed from loaded system profiles.
void update_map_system_profile_renamed();
@ -522,6 +525,8 @@ private:
// Use deque to force the container to allocate an object per each entry,
// so that the addresses of the presets don't change during resizing of the container.
std::deque<Preset> m_presets;
// System profiles may have aliases. Map to the full profile name.
std::vector<std::pair<std::string, std::string>> m_map_alias_to_profile_name;
// Map from old system profile name to a current system profile name.
std::map<std::string, std::string> m_map_system_profile_renamed;
// Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user.

View File

@ -288,11 +288,18 @@ std::string PresetBundle::load_system_presets()
// No config bundle loaded, reset.
this->reset(false);
}
this->prints .update_map_system_profile_renamed();
this->sla_prints .update_map_system_profile_renamed();
this->filaments .update_map_system_profile_renamed();
this->sla_materials.update_map_system_profile_renamed();
this->printers .update_map_system_profile_renamed();
this->prints .update_map_alias_to_profile_name();
this->sla_prints .update_map_alias_to_profile_name();
this->filaments .update_map_alias_to_profile_name();
this->sla_materials.update_map_alias_to_profile_name();
return errors_cummulative;
}
@ -877,7 +884,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
// 4) Load the project config values (the per extruder wipe matrix etc).
this->project_config.apply_only(config, s_project_options);
update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes, &this->project_config);
update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z, &this->project_config);
break;
}

View File

@ -27,7 +27,9 @@ namespace Slic3r {
namespace GUI {
#if _WIN32
/* currently not used, left for possible future use
INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
*/
void RemovableDriveManager::search_for_drives()
{
m_current_drives.clear();
@ -44,7 +46,7 @@ void RemovableDriveManager::search_for_drives()
if (drive_type == DRIVE_REMOVABLE)
{
// get name of drive
std::wstring wpath = boost::nowide::widen(path);//std::wstring(path.begin(), path.end());
std::wstring wpath = boost::nowide::widen(path);
std::wstring volume_name;
volume_name.resize(1024);
std::wstring file_system_name;
@ -54,12 +56,6 @@ void RemovableDriveManager::search_for_drives()
if(error != 0)
{
volume_name.erase(std::find(volume_name.begin(), volume_name.end(), '\0'), volume_name.end());
/*
if (volume_name == L"")
{
volume_name = L"REMOVABLE DRIVE";
}
*/
if (file_system_name != L"")
{
ULARGE_INTEGER free_space;
@ -109,6 +105,8 @@ void RemovableDriveManager::eject_drive(const std::string &path)
CloseHandle(handle);
m_did_eject = true;
m_current_drives.erase(it);
m_ejected_path = m_last_save_path;
m_ejected_name = m_last_save_name;
break;
}
}
@ -145,6 +143,7 @@ void RemovableDriveManager::register_window()
{
//creates new unvisible window that is recieving callbacks from system
// structure to register
/* currently not used, left for possible future use
WNDCLASSEX wndClass;
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
@ -180,13 +179,15 @@ void RemovableDriveManager::register_window()
}
//ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
*/
}
/* currently not used, left for possible future use
INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// here we need to catch messeges about device removal
// problem is that when ejecting usb (how is it implemented above) there is no messege dispached. Only after physical removal of the device.
//uncomment register_window() in init() to register and comment update() in GUI_App.cpp (only for windows!) to stop recieving periodical updates
LRESULT lRet = 1;
static HDEVNOTIFY hDeviceNotify;
@ -219,8 +220,9 @@ INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lP
break;
}
return lRet;
}
*/
#else
void RemovableDriveManager::search_for_drives()
{
@ -239,35 +241,10 @@ void RemovableDriveManager::search_for_drives()
//search /media/* folder
search_path("/media/*", "/media");
//search /Volumes/* folder (OSX)
//search_path("/Volumes/*", "/Volumes");
std::string path(std::getenv("USER"));
std::string pp(path);
//std::cout << "user: "<< path << "\n";
//if program is run with sudo, we have to search for all users
// but do we want that?
/*
if(path == "root"){
while (true) {
passwd* entry = getpwent();
if (!entry) {
break;
}
path = entry->pw_name;
pp = path;
//search /media/USERNAME/* folder
pp = "/media/"+pp;
path = "/media/" + path + "/*";
search_path(path, pp);
//search /run/media/USERNAME/* folder
path = "/run" + path;
pp = "/run"+pp;
search_path(path, pp);
}
endpwent();
}else
*/
{
//search /media/USERNAME/* folder
pp = "/media/"+pp;
@ -310,7 +287,6 @@ void RemovableDriveManager::inspect_file(const std::string &path, const std::str
{
//free space
boost::filesystem::space_info si = boost::filesystem::space(path);
//std::cout << "Free space: " << fs_si.free << "Available space: " << fs_si.available << " " << path << '\n';
if(si.available != 0)
{
//user id
@ -353,7 +329,7 @@ void RemovableDriveManager::eject_drive(const std::string &path)
i++;
}
}
std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n";
//std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n";
// there is no usable command in c++ so terminal command is used instead
// but neither triggers "succesful safe removal messege"
std::string command = "";
@ -373,7 +349,8 @@ void RemovableDriveManager::eject_drive(const std::string &path)
m_did_eject = true;
m_current_drives.erase(it);
m_ejected_path = m_last_save_path;
m_ejected_name = m_last_save_name;
break;
}
@ -385,7 +362,7 @@ bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path)
if (m_current_drives.empty())
return false;
std::size_t found = path.find_last_of("/");
std::string new_path = path.substr(0,found);
std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path;
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
{
if(compare_filesystem_id(new_path, (*it).path))
@ -393,10 +370,10 @@ bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path)
}
return false;
}
std::string RemovableDriveManager::get_drive_from_path(const std::string& path)
std::string RemovableDriveManager::get_drive_from_path(const std::string& path)
{
std::size_t found = path.find_last_of("/");
std::string new_path = path.substr(0, found);
std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path;
//check if same filesystem
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
{
@ -414,7 +391,10 @@ RemovableDriveManager::RemovableDriveManager():
m_last_save_name(""),
m_last_save_path_verified(false),
m_is_writing(false),
m_did_eject(false)
m_did_eject(false),
m_plater_ready_to_slice(true),
m_ejected_path(""),
m_ejected_name("")
#if __APPLE__
, m_rdmmm(new RDMMMWrapper())
#endif
@ -451,13 +431,16 @@ bool RemovableDriveManager::update(const long time,const bool check)
search_for_drives();
if (m_drives_count != m_current_drives.size())
{
if (check)check_and_notify();
if (check)
{
check_and_notify();
}
m_drives_count = m_current_drives.size();
}
return !m_current_drives.empty();
}
bool RemovableDriveManager::is_drive_mounted(const std::string &path)
bool RemovableDriveManager::is_drive_mounted(const std::string &path) const
{
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
{
@ -468,7 +451,7 @@ bool RemovableDriveManager::is_drive_mounted(const std::string &path)
}
return false;
}
std::string RemovableDriveManager::get_drive_path()
std::string RemovableDriveManager::get_drive_path()
{
if (m_current_drives.size() == 0)
{
@ -479,23 +462,27 @@ std::string RemovableDriveManager::get_drive_path()
return m_last_save_path;
return m_current_drives.back().path;
}
std::string RemovableDriveManager::get_last_save_path()
std::string RemovableDriveManager::get_last_save_path() const
{
if (!m_last_save_path_verified)
return "";
return m_last_save_path;
}
std::string RemovableDriveManager::get_last_save_name()
std::string RemovableDriveManager::get_last_save_name() const
{
return m_last_save_name;
}
std::vector<DriveData> RemovableDriveManager::get_all_drives()
std::vector<DriveData> RemovableDriveManager::get_all_drives() const
{
return m_current_drives;
}
void RemovableDriveManager::check_and_notify()
{
if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() && m_last_save_path_verified && !is_drive_mounted(m_last_save_path))
if(m_drive_count_changed_callback)
{
m_drive_count_changed_callback(m_plater_ready_to_slice);
}
if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() && !is_drive_mounted(m_last_save_path))
{
for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it)
{
@ -503,18 +490,35 @@ void RemovableDriveManager::check_and_notify()
}
}
}
void RemovableDriveManager::add_callback(std::function<void()> callback)
void RemovableDriveManager::add_remove_callback(std::function<void()> callback)
{
m_callbacks.push_back(callback);
}
void RemovableDriveManager::erase_callbacks()
void RemovableDriveManager::erase_callbacks()
{
m_callbacks.clear();
}
void RemovableDriveManager::set_drive_count_changed_callback(std::function<void(const bool)> callback)
{
m_drive_count_changed_callback = callback;
}
void RemovableDriveManager::set_plater_ready_to_slice(bool b)
{
m_plater_ready_to_slice = b;
}
void RemovableDriveManager::set_last_save_path(const std::string& path)
{
m_last_save_path_verified = false;
m_last_save_path = path;
if(m_last_save_path_verified)// if old path is on drive
{
if(get_drive_from_path(path) != "") //and new is too, rewrite the path
{
m_last_save_path_verified = false;
m_last_save_path = path;
}//else do nothing
}else
{
m_last_save_path = path;
}
}
void RemovableDriveManager::verify_last_save_path()
{
@ -529,7 +533,7 @@ void RemovableDriveManager::verify_last_save_path()
reset_last_save_path();
}
}
std::string RemovableDriveManager::get_drive_name(const std::string& path)
std::string RemovableDriveManager::get_drive_name(const std::string& path) const
{
if (m_current_drives.size() == 0)
return "";
@ -542,18 +546,17 @@ std::string RemovableDriveManager::get_drive_name(const std::string& path)
}
return "";
}
bool RemovableDriveManager::is_last_drive_removed()
bool RemovableDriveManager::is_last_drive_removed()
{
//std::cout<<"is last: "<<m_last_save_path;
//m_drives_count = m_current_drives.size();
if(!m_last_save_path_verified)
{
//std::cout<<"\n";
return true;
}
bool r = !is_drive_mounted(m_last_save_path);
if (r) reset_last_save_path();
//std::cout<<" "<< r <<"\n";
if (r)
{
reset_last_save_path();
}
return r;
}
bool RemovableDriveManager::is_last_drive_removed_with_update(const long time)
@ -575,16 +578,28 @@ void RemovableDriveManager::set_is_writing(const bool b)
m_did_eject = false;
}
}
bool RemovableDriveManager::get_is_writing()
bool RemovableDriveManager::get_is_writing() const
{
return m_is_writing;
}
bool RemovableDriveManager::get_did_eject()
bool RemovableDriveManager::get_did_eject() const
{
return m_did_eject;
}
void RemovableDriveManager::set_did_eject(const bool b)
void RemovableDriveManager::set_did_eject(const bool b)
{
m_did_eject = b;
}
size_t RemovableDriveManager::get_drives_count() const
{
return m_current_drives.size();
}
std::string RemovableDriveManager::get_ejected_path() const
{
return m_ejected_path;
}
std::string RemovableDriveManager::get_ejected_name() const
{
return m_ejected_name;
}
}}//namespace Slicer::Gui

View File

@ -35,19 +35,23 @@ public:
void init();
//update() searches for removable devices, returns false if empty. /time = 0 is forced update, time expects wxGetLocalTime()
bool update(const long time = 0,const bool check = false);
bool is_drive_mounted(const std::string &path);
bool is_drive_mounted(const std::string &path) const;
void eject_drive(const std::string &path);
//returns path to last drive which was used, if none was used, returns device that was enumerated last
std::string get_last_save_path();
std::string get_last_save_name();
std::string get_last_save_path() const;
std::string get_last_save_name() const;
//returns path to last drive which was used, if none was used, returns empty string
std::string get_drive_path();
std::vector<DriveData> get_all_drives();
std::vector<DriveData> get_all_drives() const;
bool is_path_on_removable_drive(const std::string &path);
// callback will notify only if device with last save path was removed
void add_callback(std::function<void()> callback);
// erases all callbacks added by add_callback()
void add_remove_callback(std::function<void()> callback);
// erases all remove callbacks added by add_remove_callback()
void erase_callbacks();
//drive_count_changed callback is called on every added or removed device
void set_drive_count_changed_callback(std::function<void(const bool)> callback);
//thi serves to set correct value for drive_count_changed callback
void set_plater_ready_to_slice(bool b);
// marks one of the eveices in vector as last used
void set_last_save_path(const std::string &path);
void verify_last_save_path();
@ -55,10 +59,13 @@ public:
// param as update()
bool is_last_drive_removed_with_update(const long time = 0);
void set_is_writing(const bool b);
bool get_is_writing();
bool get_did_eject();
bool get_is_writing() const;
bool get_did_eject() const;
void set_did_eject(const bool b);
std::string get_drive_name(const std::string& path);
std::string get_drive_name(const std::string& path) const;
size_t get_drives_count() const;
std::string get_ejected_path() const;
std::string get_ejected_name() const;
private:
RemovableDriveManager();
void search_for_drives();
@ -70,6 +77,7 @@ private:
std::vector<DriveData> m_current_drives;
std::vector<std::function<void()>> m_callbacks;
std::function<void(const bool)> m_drive_count_changed_callback;
size_t m_drives_count;
long m_last_update;
std::string m_last_save_path;
@ -77,6 +85,9 @@ private:
std::string m_last_save_name;
bool m_is_writing;//on device
bool m_did_eject;
bool m_plater_ready_to_slice;
std::string m_ejected_path;
std::string m_ejected_name;
#if _WIN32
//registers for notifications by creating invisible window
void register_window();

View File

@ -3033,9 +3033,9 @@ void Tab::save_preset(std::string name /*= ""*/)
show_error(this, _(L("Cannot overwrite an external profile.")));
return;
}
if (existing/* && name != preset.name*/)
if (existing && name != preset.name)
{
wxString msg_text = GUI::from_u8((boost::format(_utf8(L("Preset with name \"%1%\" already exist."))) % name).str());
wxString msg_text = GUI::from_u8((boost::format(_utf8(L("Preset with name \"%1%\" already exists."))) % name).str());
msg_text += "\n" + _(L("Replace?"));
wxMessageDialog dialog(nullptr, msg_text, _(L("Warning")), wxICON_WARNING | wxYES | wxNO);

File diff suppressed because it is too large Load Diff

View File

@ -5,19 +5,15 @@
#include <wx/combo.h>
#include <wx/dataview.h>
#include <wx/dc.h>
#include <wx/collpane.h>
#include <wx/wupdlock.h>
#include <wx/button.h>
#include <wx/sizer.h>
#include <wx/slider.h>
#include <wx/menu.h>
#include <wx/wx.h>
#include <vector>
#include <set>
#include <functional>
#include "libslic3r/Model.hpp"
#include "libslic3r/GCodeWriter.hpp"
namespace Slic3r {
enum class ModelVolumeType : int;
@ -49,12 +45,15 @@ wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string,
wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
std::function<void(wxCommandEvent& event)> cb, wxEvtHandler* event_handler);
void enable_menu_item(wxUpdateUIEvent& evt, std::function<bool()> const cb_condition, wxMenuItem* item, wxWindow* win);
class wxDialog;
class wxBitmapComboBox;
void edit_tooltip(wxString& tooltip);
void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector<int>& btn_ids);
int em_unit(wxWindow* win);
float get_svg_scale_factor(wxWindow* win);
wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name,
const int px_cnt = 16, const bool is_horizontal = false, const bool grayscale = false);
@ -742,308 +741,6 @@ private:
};
// ----------------------------------------------------------------------------
// DoubleSlider
// ----------------------------------------------------------------------------
// custom message the slider sends to its parent to notify a tick-change:
wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent);
enum SelectedSlider {
ssUndef,
ssLower,
ssHigher
};
enum TicksAction{
taOnIcon,
taAdd,
taDel
};
class DoubleSlider : public wxControl
{
enum IconFocus {
ifNone,
ifRevert,
ifCog
};
public:
DoubleSlider(
wxWindow *parent,
wxWindowID id,
int lowerValue,
int higherValue,
int minValue,
int maxValue,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxSL_VERTICAL,
const wxValidator& val = wxDefaultValidator,
const wxString& name = wxEmptyString);
~DoubleSlider() {}
using t_mode = Slic3r::Model::CustomGCodeInfo::MODE;
/* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values.
* So, let use same value as a permissible error for layer height.
*/
static double epsilon() { return 0.0011;}
void msw_rescale();
int GetMinValue() const { return m_min_value; }
int GetMaxValue() const { return m_max_value; }
double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value]; }
double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; }
int GetLowerValue() const { return m_lower_value; }
int GetHigherValue() const { return m_higher_value; }
int GetActiveValue() const;
wxSize get_min_size() const ;
double GetLowerValueD() { return get_double_value(ssLower); }
double GetHigherValueD() { return get_double_value(ssHigher); }
wxSize DoGetBestSize() const override;
void SetLowerValue(const int lower_val);
void SetHigherValue(const int higher_val);
// Set low and high slider position. If the span is non-empty, disable the "one layer" mode.
void SetSelectionSpan(const int lower_val, const int higher_val);
void SetMaxValue(const int max_value);
void SetKoefForLabels(const double koef) { m_label_koef = koef; }
void SetSliderValues(const std::vector<double>& values) { m_values = values; }
void ChangeOneLayerLock();
Slic3r::Model::CustomGCodeInfo GetTicksValues() const;
void SetTicksValues(const Slic3r::Model::CustomGCodeInfo &custom_gcode_per_print_z);
void EnableTickManipulation(bool enable = true) { m_is_enabled_tick_manipulation = enable; }
void DisableTickManipulation() { EnableTickManipulation(false); }
void SetManipulationMode(t_mode mode) { m_mode = mode; }
t_mode GetManipulationMode() const { return m_mode; }
void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder)
{
m_mode = !is_one_extruder_printed_model ? t_mode::MultiExtruder :
only_extruder < 0 ? t_mode::SingleExtruder :
t_mode::MultiAsSingle;
m_only_extruder = only_extruder;
}
bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; }
bool is_one_layer() const { return m_is_one_layer; }
bool is_lower_at_min() const { return m_lower_value == m_min_value; }
bool is_higher_at_max() const { return m_higher_value == m_max_value; }
bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); }
void OnPaint(wxPaintEvent& ) { render();}
void OnLeftDown(wxMouseEvent& event);
void OnMotion(wxMouseEvent& event);
void OnLeftUp(wxMouseEvent& event);
void OnEnterWin(wxMouseEvent& event) { enter_window(event, true); }
void OnLeaveWin(wxMouseEvent& event) { enter_window(event, false); }
void OnWheel(wxMouseEvent& event);
void OnKeyDown(wxKeyEvent &event);
void OnKeyUp(wxKeyEvent &event);
void OnChar(wxKeyEvent &event);
void OnRightDown(wxMouseEvent& event);
void OnRightUp(wxMouseEvent& event);
void add_code_as_tick(std::string code, int selected_extruder = -1);
// add default action for tick, when press "+"
void add_current_tick(bool call_from_keyboard = false);
// delete current tick, when press "-"
void delete_current_tick();
void edit_tick();
void edit_extruder_sequence();
struct TICK_CODE
{
bool operator<(const TICK_CODE& other) const { return other.tick > this->tick; }
bool operator>(const TICK_CODE& other) const { return other.tick < this->tick; }
int tick = 0;
std::string gcode = Slic3r::ColorChangeCode;
int extruder = 0;
std::string color;
};
protected:
void render();
void draw_focus_rect();
void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end);
void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos);
void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection);
void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos);
void draw_ticks(wxDC& dc);
void draw_colored_band(wxDC& dc);
void draw_one_layer_icon(wxDC& dc);
void draw_revert_icon(wxDC& dc);
void draw_cog_icon(wxDC &dc);
void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection);
void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection);
void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const;
void update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection);
void detect_selected_slider(const wxPoint& pt);
void correct_lower_value();
void correct_higher_value();
void move_current_thumb(const bool condition);
void enter_window(wxMouseEvent& event, const bool enter);
private:
bool is_point_in_rect(const wxPoint& pt, const wxRect& rect);
int is_point_near_tick(const wxPoint& pt);
double get_scroll_step();
wxString get_label(const SelectedSlider& selection) const;
void get_lower_and_higher_position(int& lower_pos, int& higher_pos);
int get_value_from_position(const wxCoord x, const wxCoord y);
wxCoord get_position_from_value(const int value);
wxSize get_size();
void get_size(int *w, int *h);
double get_double_value(const SelectedSlider& selection);
wxString get_tooltip(IconFocus icon_focus);
std::string get_color_for_tool_change_tick(std::set<TICK_CODE>::const_iterator it) const;
std::string get_color_for_color_change_tick(std::set<TICK_CODE>::const_iterator it) const;
int get_extruder_for_tick(int tick);
std::set<int> get_used_extruders_for_tick(int tick);
void post_ticks_changed_event(const std::string& gcode = "");
bool check_ticks_changed_event(const std::string& gcode);
void append_change_extruder_menu_item(wxMenu*);
void append_add_color_change_menu_item(wxMenu*);
bool is_osx { false };
wxFont m_font;
int m_min_value;
int m_max_value;
int m_lower_value;
int m_higher_value;
ScalableBitmap m_bmp_thumb_higher;
ScalableBitmap m_bmp_thumb_lower;
ScalableBitmap m_bmp_add_tick_on;
ScalableBitmap m_bmp_add_tick_off;
ScalableBitmap m_bmp_del_tick_on;
ScalableBitmap m_bmp_del_tick_off;
ScalableBitmap m_bmp_one_layer_lock_on;
ScalableBitmap m_bmp_one_layer_lock_off;
ScalableBitmap m_bmp_one_layer_unlock_on;
ScalableBitmap m_bmp_one_layer_unlock_off;
ScalableBitmap m_bmp_revert;
ScalableBitmap m_bmp_cog;
SelectedSlider m_selection;
bool m_is_left_down = false;
bool m_is_right_down = false;
bool m_is_one_layer = false;
bool m_is_focused = false;
bool m_is_action_icon_focesed = false;
bool m_is_one_layer_icon_focesed = false;
bool m_is_enabled_tick_manipulation = true;
bool m_show_context_menu = false;
bool m_show_edit_menu = false;
bool m_force_edit_extruder_sequence = false;
bool m_force_mode_apply = true;
bool m_force_add_tick = false;
bool m_force_delete_tick = false;
t_mode m_mode = t_mode::SingleExtruder;
int m_only_extruder = -1;
wxRect m_rect_lower_thumb;
wxRect m_rect_higher_thumb;
wxRect m_rect_tick_action;
wxRect m_rect_one_layer_icon;
wxRect m_rect_revert_icon;
wxRect m_rect_cog_icon;
wxSize m_thumb_size;
int m_tick_icon_dim;
int m_lock_icon_dim;
int m_revert_icon_dim;
int m_cog_icon_dim;
long m_style;
float m_label_koef = 1.0;
// control's view variables
wxCoord SLIDER_MARGIN; // margin around slider
wxPen DARK_ORANGE_PEN;
wxPen ORANGE_PEN;
wxPen LIGHT_ORANGE_PEN;
wxPen DARK_GREY_PEN;
wxPen GREY_PEN;
wxPen LIGHT_GREY_PEN;
std::vector<wxPen*> m_line_pens;
std::vector<wxPen*> m_segm_pens;
std::vector<double> m_values;
struct TICK_CODE_INFO
{
std::set<TICK_CODE> ticks;
t_mode mode = t_mode::SingleExtruder;
bool empty() const { return ticks.empty(); }
void set_pause_print_msg(const std::string& message) { pause_print_msg = message; }
bool add_tick (const int tick, std::string &code, int extruder, double print_z);
bool edit_tick (std::set<TICK_CODE>::iterator it, double print_z);
void switch_code(const std::string& code_from, const std::string& code_to);
void erase_all_ticks_with_code (const std::string& gcode);
bool has_tick_with_code (const std::string& gcode);
void suppress_plus (bool suppress) { m_suppress_plus = suppress;}
void suppress_minus(bool suppress) { m_suppress_minus = suppress;}
bool suppressed_plus () { return m_suppress_plus ; }
bool suppressed_minus() { return m_suppress_minus; }
private:
std::string custom_gcode = "";
std::string pause_print_msg = "";
bool m_suppress_plus = false;
bool m_suppress_minus = false;
}
m_ticks;
public:
struct ExtrudersSequence
{
bool is_mm_intervals = true;
double interval_by_mm = 3.0;
int interval_by_layers = 10;
std::vector<size_t> extruders = { 0 };
bool operator==(const ExtrudersSequence& other) const
{
return (other.is_mm_intervals == this->is_mm_intervals ) &&
(other.interval_by_mm == this->interval_by_mm ) &&
(other.interval_by_layers == this->interval_by_layers ) &&
(other.extruders == this->extruders ) ;
}
bool operator!=(const ExtrudersSequence& other) const
{
return (other.is_mm_intervals != this->is_mm_intervals ) &&
(other.interval_by_mm != this->interval_by_mm ) &&
(other.interval_by_layers != this->interval_by_layers ) &&
(other.extruders != this->extruders ) ;
}
void add_extruder(size_t pos)
{
extruders.insert(extruders.begin() + pos+1, size_t(0));
}
void delete_extruder(size_t pos)
{
if (extruders.size() == 1)
return;// last item can't be deleted
extruders.erase(extruders.begin() + pos);
}
}
m_extruders_sequence;
};
// ----------------------------------------------------------------------------
// LockButton
// ----------------------------------------------------------------------------

View File

@ -1,4 +1,4 @@
use Test::More tests => 41;
use Test::More tests => 38;
use strict;
use warnings;

View File

@ -28,8 +28,8 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
SECTION("math: 2*3/6") { REQUIRE(parser.process("{2*3/6}") == "1"); }
SECTION("math: 2*3/12") { REQUIRE(parser.process("{2*3/12}") == "0"); }
SECTION("math: 2.*3/12") { REQUIRE(std::stod(parser.process("{2.*3/12}")) == Approx(0.5)); }
// SECTION("math: 10 % 2.5") { REQUIRE(parser.process("{10%2.5}") == "0"); }
// SECTION("math: 11 / 2.5") { REQUIRE(parser.process("{11/2.5-1}") == "1"); }
SECTION("math: 10 % 2.5") { REQUIRE(std::stod(parser.process("{10%2.5}")) == Approx(0.)); }
SECTION("math: 11 % 2.5") { REQUIRE(std::stod(parser.process("{11%2.5}")) == Approx(1.)); }
SECTION("math: 2*(3-12)") { REQUIRE(parser.process("{2*(3-12)}") == "-18"); }
SECTION("math: 2*foo*(3-12)") { REQUIRE(parser.process("{2*foo*(3-12)}") == "0"); }
SECTION("math: 2*bar*(3-12)") { REQUIRE(parser.process("{2*bar*(3-12)}") == "-36"); }
@ -38,7 +38,8 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
SECTION("math: max(12, 14)") { REQUIRE(parser.process("{max(12, 14)}") == "14"); }
SECTION("math: min(13.4, -1238.1)") { REQUIRE(std::stod(parser.process("{min(13.4, -1238.1)}")) == Approx(-1238.1)); }
SECTION("math: max(13.4, -1238.1)") { REQUIRE(std::stod(parser.process("{max(13.4, -1238.1)}")) == Approx(13.4)); }
// SECTION("math: int(13.4)") { REQUIRE(parser.process("{int(13.4)}") == "13"); }
SECTION("math: int(13.4)") { REQUIRE(parser.process("{int(13.4)}") == "13"); }
SECTION("math: int(-13.4)") { REQUIRE(parser.process("{int(-13.4)}") == "-13"); }
// Test the boolean expression parser.
auto boolean_expression = [&parser](const std::string& templ) { return parser.evaluate_boolean_expression(templ, parser.config()); };

View File

@ -3,7 +3,7 @@
set(SLIC3R_APP_NAME "PrusaSlicer")
set(SLIC3R_APP_KEY "PrusaSlicer")
set(SLIC3R_VERSION "2.2.0-alpha2")
set(SLIC3R_VERSION "2.2.0-alpha3")
set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN")
set(SLIC3R_RC_VERSION "2,2,0,0")
set(SLIC3R_RC_VERSION_DOTS "2.2.0.0")

View File

@ -48,12 +48,8 @@ _constant()
Ref<ModelObject> model_object();
Ref<StaticPrintConfig> config()
%code%{ RETVAL = &THIS->config(); %};
Points copies();
Clone<BoundingBox> bounding_box();
Points _shifted_copies()
%code%{ RETVAL = THIS->copies(); %};
size_t layer_count();
Ref<Layer> get_layer(int idx);