Merge branch 'master' of https://github.com/prusa3d/Slic3r into et_canvas_gui_refactoring
This commit is contained in:
commit
6ca49c05df
@ -1,25 +1,27 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
|
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||||
<g>
|
<g id="layers">
|
||||||
<g>
|
<g>
|
||||||
<rect x="7.98" y="105" fill="#FFFFFF" width="112.04" height="15"/>
|
<g>
|
||||||
|
<rect x="1" y="13" fill="#808080" width="14" height="2"/>
|
||||||
</g>
|
</g>
|
||||||
<g>
|
<g>
|
||||||
<rect x="7.98" y="85.67" fill="#FFFFFF" width="112.04" height="13"/>
|
<rect x="1" y="10.6" fill="#808080" width="14" height="1.74"/>
|
||||||
</g>
|
</g>
|
||||||
<g>
|
<g>
|
||||||
<rect x="7.98" y="66.33" fill="#FFFFFF" width="112.04" height="11"/>
|
<rect x="1" y="8.19" fill="#808080" width="14" height="1.47"/>
|
||||||
</g>
|
</g>
|
||||||
<g>
|
<g>
|
||||||
<rect x="7.98" y="47" fill="#ED6B21" width="112.04" height="9"/>
|
<rect x="1" y="5.79" fill="#ED6B21" width="14" height="1.2"/>
|
||||||
</g>
|
</g>
|
||||||
<g>
|
<g>
|
||||||
<rect x="7.98" y="27.67" fill="#ED6B21" width="112.04" height="7"/>
|
<rect x="1" y="3.39" fill="#ED6B21" width="14" height="0.93"/>
|
||||||
</g>
|
</g>
|
||||||
<g>
|
<g>
|
||||||
<rect x="7.98" y="8.33" fill="#ED6B21" width="112.04" height="5"/>
|
<rect x="1" y="0.99" fill="#808080" width="14" height="0.67"/>
|
||||||
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 841 B After Width: | Height: | Size: 845 B |
@ -39,8 +39,7 @@ static void stl_record_neighbors(stl_file *stl,
|
|||||||
stl_hash_edge *edge_a, stl_hash_edge *edge_b);
|
stl_hash_edge *edge_a, stl_hash_edge *edge_b);
|
||||||
static void stl_initialize_facet_check_exact(stl_file *stl);
|
static void stl_initialize_facet_check_exact(stl_file *stl);
|
||||||
static void stl_initialize_facet_check_nearby(stl_file *stl);
|
static void stl_initialize_facet_check_nearby(stl_file *stl);
|
||||||
static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge,
|
static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, const stl_vertex *a, const stl_vertex *b);
|
||||||
stl_vertex *a, stl_vertex *b);
|
|
||||||
static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge,
|
static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge,
|
||||||
stl_vertex *a, stl_vertex *b, float tolerance);
|
stl_vertex *a, stl_vertex *b, float tolerance);
|
||||||
static void insert_hash_edge(stl_file *stl, stl_hash_edge edge,
|
static void insert_hash_edge(stl_file *stl, stl_hash_edge edge,
|
||||||
@ -60,40 +59,39 @@ extern int stl_check_normal_vector(stl_file *stl,
|
|||||||
int facet_num, int normal_fix_flag);
|
int facet_num, int normal_fix_flag);
|
||||||
static void stl_update_connects_remove_1(stl_file *stl, int facet_num);
|
static void stl_update_connects_remove_1(stl_file *stl, int facet_num);
|
||||||
|
|
||||||
|
// This function builds the neighbors list. No modifications are made
|
||||||
void
|
// to any of the facets. The edges are said to match only if all six
|
||||||
stl_check_facets_exact(stl_file *stl) {
|
// floats of the first edge matches all six floats of the second edge.
|
||||||
/* This function builds the neighbors list. No modifications are made
|
void stl_check_facets_exact(stl_file *stl)
|
||||||
* to any of the facets. The edges are said to match only if all six
|
{
|
||||||
* floats of the first edge matches all six floats of the second edge.
|
if (stl->error)
|
||||||
*/
|
return;
|
||||||
|
|
||||||
stl_hash_edge edge;
|
|
||||||
stl_facet facet;
|
|
||||||
int i;
|
|
||||||
int j;
|
|
||||||
|
|
||||||
if (stl->error) return;
|
|
||||||
|
|
||||||
stl->stats.connected_edges = 0;
|
stl->stats.connected_edges = 0;
|
||||||
stl->stats.connected_facets_1_edge = 0;
|
stl->stats.connected_facets_1_edge = 0;
|
||||||
stl->stats.connected_facets_2_edge = 0;
|
stl->stats.connected_facets_2_edge = 0;
|
||||||
stl->stats.connected_facets_3_edge = 0;
|
stl->stats.connected_facets_3_edge = 0;
|
||||||
|
|
||||||
stl_initialize_facet_check_exact(stl);
|
|
||||||
|
|
||||||
for(i = 0; i < stl->stats.number_of_facets; i++) {
|
|
||||||
facet = stl->facet_start[i];
|
|
||||||
// If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet.
|
// If any two of the three vertices are found to be exactally the same, call them degenerate and remove the facet.
|
||||||
if (facet.vertex[0] == facet.vertex[1] ||
|
// Do it before the next step, as the next step stores references to the face indices in the hash tables and removing a facet
|
||||||
facet.vertex[1] == facet.vertex[2] ||
|
// will break the references.
|
||||||
facet.vertex[0] == facet.vertex[2]) {
|
for (int i = 0; i < stl->stats.number_of_facets;) {
|
||||||
|
stl_facet &facet = stl->facet_start[i];
|
||||||
|
if (facet.vertex[0] == facet.vertex[1] || facet.vertex[1] == facet.vertex[2] || facet.vertex[0] == facet.vertex[2]) {
|
||||||
|
// Remove the degenerate facet.
|
||||||
|
facet = stl->facet_start[--stl->stats.number_of_facets];
|
||||||
|
stl->stats.facets_removed += 1;
|
||||||
stl->stats.degenerate_facets += 1;
|
stl->stats.degenerate_facets += 1;
|
||||||
stl_remove_facet(stl, i);
|
} else
|
||||||
-- i;
|
++ i;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
for(j = 0; j < 3; j++) {
|
|
||||||
|
// Connect neighbor edges.
|
||||||
|
stl_initialize_facet_check_exact(stl);
|
||||||
|
for (int i = 0; i < stl->stats.number_of_facets; i++) {
|
||||||
|
const stl_facet &facet = stl->facet_start[i];
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
stl_hash_edge edge;
|
||||||
edge.facet_number = i;
|
edge.facet_number = i;
|
||||||
edge.which_edge = j;
|
edge.which_edge = j;
|
||||||
stl_load_edge_exact(stl, &edge, &facet.vertex[j], &facet.vertex[(j + 1) % 3]);
|
stl_load_edge_exact(stl, &edge, &facet.vertex[j], &facet.vertex[(j + 1) % 3]);
|
||||||
@ -109,9 +107,7 @@ stl_check_facets_exact(stl_file *stl) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, const stl_vertex *a, const stl_vertex *b) {
|
||||||
stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge,
|
|
||||||
stl_vertex *a, stl_vertex *b) {
|
|
||||||
|
|
||||||
if (stl->error) return;
|
if (stl->error) return;
|
||||||
|
|
||||||
@ -333,7 +329,9 @@ static void stl_free_edges(stl_file *stl)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(stl->heads);
|
free(stl->heads);
|
||||||
|
stl->heads = nullptr;
|
||||||
free(stl->tail);
|
free(stl->tail);
|
||||||
|
stl->tail = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stl_initialize_facet_check_nearby(stl_file *stl)
|
static void stl_initialize_facet_check_nearby(stl_file *stl)
|
||||||
|
@ -127,7 +127,6 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
stl_facet *facet_start;
|
stl_facet *facet_start;
|
||||||
stl_edge *edge_start;
|
|
||||||
stl_hash_edge **heads;
|
stl_hash_edge **heads;
|
||||||
stl_hash_edge *tail;
|
stl_hash_edge *tail;
|
||||||
int M;
|
int M;
|
||||||
@ -142,7 +141,6 @@ typedef struct {
|
|||||||
extern void stl_open(stl_file *stl, const char *file);
|
extern void stl_open(stl_file *stl, const char *file);
|
||||||
extern void stl_close(stl_file *stl);
|
extern void stl_close(stl_file *stl);
|
||||||
extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file);
|
extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file);
|
||||||
extern void stl_print_edges(stl_file *stl, FILE *file);
|
|
||||||
extern void stl_print_neighbors(stl_file *stl, char *file);
|
extern void stl_print_neighbors(stl_file *stl, char *file);
|
||||||
extern void stl_put_little_int(FILE *fp, int value_in);
|
extern void stl_put_little_int(FILE *fp, int value_in);
|
||||||
extern void stl_put_little_float(FILE *fp, float value_in);
|
extern void stl_put_little_float(FILE *fp, float value_in);
|
||||||
|
@ -33,24 +33,6 @@
|
|||||||
#define SEEK_END 2
|
#define SEEK_END 2
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void
|
|
||||||
stl_print_edges(stl_file *stl, FILE *file) {
|
|
||||||
int i;
|
|
||||||
int edges_allocated;
|
|
||||||
|
|
||||||
if (stl->error) return;
|
|
||||||
|
|
||||||
edges_allocated = stl->stats.number_of_facets * 3;
|
|
||||||
for(i = 0; i < edges_allocated; i++) {
|
|
||||||
fprintf(file, "%d, %f, %f, %f, %f, %f, %f\n",
|
|
||||||
stl->edge_start[i].facet_number,
|
|
||||||
stl->edge_start[i].p1(0), stl->edge_start[i].p1(1),
|
|
||||||
stl->edge_start[i].p1(2), stl->edge_start[i].p2(0),
|
|
||||||
stl->edge_start[i].p2(1), stl->edge_start[i].p2(2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
stl_stats_out(stl_file *stl, FILE *file, char *input_file) {
|
stl_stats_out(stl_file *stl, FILE *file, char *input_file) {
|
||||||
if (stl->error) return;
|
if (stl->error) return;
|
||||||
|
@ -556,19 +556,9 @@ std::string Model::propose_export_file_name_and_path() const
|
|||||||
for (const ModelObject *model_object : this->objects)
|
for (const ModelObject *model_object : this->objects)
|
||||||
for (ModelInstance *model_instance : model_object->instances)
|
for (ModelInstance *model_instance : model_object->instances)
|
||||||
if (model_instance->is_printable()) {
|
if (model_instance->is_printable()) {
|
||||||
input_file = model_object->input_file;
|
input_file = model_object->get_export_filename();
|
||||||
if (! model_object->name.empty()) {
|
|
||||||
if (input_file.empty())
|
if (!input_file.empty())
|
||||||
// model_object->input_file was empty, just use model_object->name
|
|
||||||
input_file = model_object->name;
|
|
||||||
else {
|
|
||||||
// Replace file name in input_file with model_object->name, but keep the path and file extension.
|
|
||||||
input_file = (boost::filesystem::path(model_object->name).parent_path().empty()) ?
|
|
||||||
(boost::filesystem::path(input_file).parent_path() / model_object->name).make_preferred().string() :
|
|
||||||
model_object->name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (! input_file.empty())
|
|
||||||
goto end;
|
goto end;
|
||||||
// Other instances will produce the same name, skip them.
|
// Other instances will produce the same name, skip them.
|
||||||
break;
|
break;
|
||||||
@ -1433,6 +1423,26 @@ void ModelObject::print_info() const
|
|||||||
cout << "volume = " << mesh.volume() << endl;
|
cout << "volume = " << mesh.volume() << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ModelObject::get_export_filename() const
|
||||||
|
{
|
||||||
|
std::string ret = input_file;
|
||||||
|
|
||||||
|
if (!name.empty())
|
||||||
|
{
|
||||||
|
if (ret.empty())
|
||||||
|
// input_file was empty, just use name
|
||||||
|
ret = name;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Replace file name in input_file with name, but keep the path and file extension.
|
||||||
|
ret = (boost::filesystem::path(name).parent_path().empty()) ?
|
||||||
|
(boost::filesystem::path(ret).parent_path() / name).make_preferred().string() : name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void ModelVolume::set_material_id(t_model_material_id material_id)
|
void ModelVolume::set_material_id(t_model_material_id material_id)
|
||||||
{
|
{
|
||||||
m_material_id = material_id;
|
m_material_id = material_id;
|
||||||
|
@ -275,6 +275,8 @@ public:
|
|||||||
// Print object statistics to console.
|
// Print object statistics to console.
|
||||||
void print_info() const;
|
void print_info() const;
|
||||||
|
|
||||||
|
std::string get_export_filename() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class Print;
|
friend class Print;
|
||||||
friend class SLAPrint;
|
friend class SLAPrint;
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#include "GCode/WipeTowerPrusaMM.hpp"
|
#include "GCode/WipeTowerPrusaMM.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
|
||||||
#include "PrintExport.hpp"
|
//#include "PrintExport.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
#include "Rasterizer/Rasterizer.hpp"
|
#include "Rasterizer/Rasterizer.hpp"
|
||||||
//#include <tbb/parallel_for.h>
|
//#include <tbb/parallel_for.h>
|
||||||
@ -72,7 +73,8 @@ public:
|
|||||||
void finish_layer();
|
void finish_layer();
|
||||||
|
|
||||||
// Save all the layers into the file (or dir) specified in the path argument
|
// Save all the layers into the file (or dir) specified in the path argument
|
||||||
void save(const std::string& path);
|
// An optional project name can be added to be used for the layer file names
|
||||||
|
void save(const std::string& path, const std::string& projectname = "");
|
||||||
|
|
||||||
// Save only the selected layer to the file specified in path argument.
|
// Save only the selected layer to the file specified in path argument.
|
||||||
void save_layer(unsigned lyr, const std::string& path);
|
void save_layer(unsigned lyr, const std::string& path);
|
||||||
@ -86,7 +88,8 @@ template<class T = void> struct VeryFalse { static const bool value = false; };
|
|||||||
template<class Fmt> class LayerWriter {
|
template<class Fmt> class LayerWriter {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
LayerWriter(const std::string& /*zipfile_path*/) {
|
LayerWriter(const std::string& /*zipfile_path*/)
|
||||||
|
{
|
||||||
static_assert(VeryFalse<Fmt>::value,
|
static_assert(VeryFalse<Fmt>::value,
|
||||||
"No layer writer implementation provided!");
|
"No layer writer implementation provided!");
|
||||||
}
|
}
|
||||||
@ -99,10 +102,6 @@ public:
|
|||||||
void binary_entry(const std::string& /*fname*/,
|
void binary_entry(const std::string& /*fname*/,
|
||||||
const std::uint8_t* buf, size_t len);
|
const std::uint8_t* buf, size_t len);
|
||||||
|
|
||||||
// Get the name of the archive but only the name part without the path or
|
|
||||||
// the extension.
|
|
||||||
std::string get_name() { return ""; }
|
|
||||||
|
|
||||||
// Test whether the object can still be used for writing.
|
// Test whether the object can still be used for writing.
|
||||||
bool is_ok() { return false; }
|
bool is_ok() { return false; }
|
||||||
|
|
||||||
@ -253,12 +252,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class LyrFmt>
|
template<class LyrFmt>
|
||||||
inline void save(const std::string& path) {
|
inline void save(const std::string& fpath, const std::string& prjname = "")
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
LayerWriter<LyrFmt> writer(path);
|
LayerWriter<LyrFmt> writer(fpath);
|
||||||
if(!writer.is_ok()) return;
|
if(!writer.is_ok()) return;
|
||||||
|
|
||||||
std::string project = writer.get_name();
|
std::string project = prjname.empty()?
|
||||||
|
boost::filesystem::path(fpath).stem().string() : prjname;
|
||||||
|
|
||||||
writer.next_entry("config.ini");
|
writer.next_entry("config.ini");
|
||||||
if(!writer.is_ok()) return;
|
if(!writer.is_ok()) return;
|
||||||
|
@ -1792,6 +1792,11 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
|
|||||||
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
|
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
|
||||||
TriangleMesh mesh(volumes.front()->mesh);
|
TriangleMesh mesh(volumes.front()->mesh);
|
||||||
mesh.transform(volumes.front()->get_matrix(), true);
|
mesh.transform(volumes.front()->get_matrix(), true);
|
||||||
|
assert(mesh.repaired);
|
||||||
|
if (volumes.size() == 1 && mesh.repaired) {
|
||||||
|
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
|
||||||
|
stl_check_facets_exact(&mesh.stl);
|
||||||
|
}
|
||||||
for (size_t idx_volume = 1; idx_volume < volumes.size(); ++ idx_volume) {
|
for (size_t idx_volume = 1; idx_volume < volumes.size(); ++ idx_volume) {
|
||||||
const ModelVolume &model_volume = *volumes[idx_volume];
|
const ModelVolume &model_volume = *volumes[idx_volume];
|
||||||
TriangleMesh vol_mesh(model_volume.mesh);
|
TriangleMesh vol_mesh(model_volume.mesh);
|
||||||
@ -1821,6 +1826,10 @@ std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z,
|
|||||||
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
|
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
|
||||||
TriangleMesh mesh(volume.mesh);
|
TriangleMesh mesh(volume.mesh);
|
||||||
mesh.transform(volume.get_matrix(), true);
|
mesh.transform(volume.get_matrix(), true);
|
||||||
|
if (mesh.repaired) {
|
||||||
|
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
|
||||||
|
stl_check_facets_exact(&mesh.stl);
|
||||||
|
}
|
||||||
if (mesh.stl.stats.number_of_facets > 0) {
|
if (mesh.stl.stats.number_of_facets > 0) {
|
||||||
mesh.transform(m_trafo, true);
|
mesh.transform(m_trafo, true);
|
||||||
// apply XY shift
|
// apply XY shift
|
||||||
|
@ -320,10 +320,8 @@ struct SLAPrintStatistics
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SLAminzZipper {};
|
|
||||||
|
|
||||||
// The implementation of creating zipped archives with wxWidgets
|
// The implementation of creating zipped archives with wxWidgets
|
||||||
template<> class LayerWriter<SLAminzZipper> {
|
template<> class LayerWriter<Zipper> {
|
||||||
Zipper m_zip;
|
Zipper m_zip;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -338,10 +336,6 @@ public:
|
|||||||
m_zip.add_entry(fname, buf, l);
|
m_zip.add_entry(fname, buf, l);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_name() const {
|
|
||||||
return m_zip.get_name();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T> inline LayerWriter& operator<<(T&& arg) {
|
template<class T> inline LayerWriter& operator<<(T&& arg) {
|
||||||
m_zip << std::forward<T>(arg); return *this;
|
m_zip << std::forward<T>(arg); return *this;
|
||||||
}
|
}
|
||||||
@ -389,9 +383,11 @@ public:
|
|||||||
// Returns true if the last step was finished with success.
|
// Returns true if the last step was finished with success.
|
||||||
bool finished() const override { return this->is_step_done(slaposSliceSupports) && this->Inherited::is_step_done(slapsRasterize); }
|
bool finished() const override { return this->is_step_done(slaposSliceSupports) && this->Inherited::is_step_done(slapsRasterize); }
|
||||||
|
|
||||||
template<class Fmt = SLAminzZipper>
|
template<class Fmt = Zipper>
|
||||||
void export_raster(const std::string& fname) {
|
inline void export_raster(const std::string& fpath,
|
||||||
if(m_printer) m_printer->save<Fmt>(fname);
|
const std::string& projectname = "")
|
||||||
|
{
|
||||||
|
if(m_printer) m_printer->save<Fmt>(fpath, projectname);
|
||||||
}
|
}
|
||||||
|
|
||||||
const PrintObjects& objects() const { return m_objects; }
|
const PrintObjects& objects() const { return m_objects; }
|
||||||
|
@ -273,6 +273,11 @@ void TriangleMesh::translate(float x, float y, float z)
|
|||||||
stl_invalidate_shared_vertices(&this->stl);
|
stl_invalidate_shared_vertices(&this->stl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TriangleMesh::translate(const Vec3f &displacement)
|
||||||
|
{
|
||||||
|
translate(displacement(0), displacement(1), displacement(2));
|
||||||
|
}
|
||||||
|
|
||||||
void TriangleMesh::rotate(float angle, const Axis &axis)
|
void TriangleMesh::rotate(float angle, const Axis &axis)
|
||||||
{
|
{
|
||||||
if (angle == 0.f)
|
if (angle == 0.f)
|
||||||
|
@ -40,6 +40,7 @@ public:
|
|||||||
void scale(float factor);
|
void scale(float factor);
|
||||||
void scale(const Vec3d &versor);
|
void scale(const Vec3d &versor);
|
||||||
void translate(float x, float y, float z);
|
void translate(float x, float y, float z);
|
||||||
|
void translate(const Vec3f &displacement);
|
||||||
void rotate(float angle, const Axis &axis);
|
void rotate(float angle, const Axis &axis);
|
||||||
void rotate(float angle, const Vec3d& axis);
|
void rotate(float angle, const Vec3d& axis);
|
||||||
void rotate_x(float angle) { this->rotate(angle, X); }
|
void rotate_x(float angle) { this->rotate(angle, X); }
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
#include "Zipper.hpp"
|
#include "Zipper.hpp"
|
||||||
#include "miniz/miniz_zip.h"
|
#include "miniz/miniz_zip.h"
|
||||||
#include <boost/filesystem/path.hpp>
|
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
@ -213,10 +212,6 @@ void Zipper::finish_entry()
|
|||||||
m_entry.clear();
|
m_entry.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Zipper::get_name() const {
|
|
||||||
return boost::filesystem::path(m_impl->m_zipname).stem().string();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Zipper::finalize()
|
void Zipper::finalize()
|
||||||
{
|
{
|
||||||
finish_entry();
|
finish_entry();
|
||||||
|
@ -81,9 +81,6 @@ public:
|
|||||||
/// file is up to minz after the erroneous write.
|
/// file is up to minz after the erroneous write.
|
||||||
void finish_entry();
|
void finish_entry();
|
||||||
|
|
||||||
/// Gets the name of the archive without the path or extension.
|
|
||||||
std::string get_name() const;
|
|
||||||
|
|
||||||
void finalize();
|
void finalize();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -397,8 +397,9 @@ int CLI::run(int argc, char **argv)
|
|||||||
outfile_final = fff_print.print_statistics().finalize_output_path(outfile);
|
outfile_final = fff_print.print_statistics().finalize_output_path(outfile);
|
||||||
} else {
|
} else {
|
||||||
outfile = sla_print.output_filepath(outfile);
|
outfile = sla_print.output_filepath(outfile);
|
||||||
sla_print.export_raster(outfile);
|
// We need to finalize the filename beforehand because the export function sets the filename inside the zip metadata
|
||||||
outfile_final = sla_print.print_statistics().finalize_output_path(outfile);
|
outfile_final = sla_print.print_statistics().finalize_output_path(outfile);
|
||||||
|
sla_print.export_raster(outfile_final);
|
||||||
}
|
}
|
||||||
if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final) != 0) {
|
if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final) != 0) {
|
||||||
boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl;
|
boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl;
|
||||||
|
@ -98,8 +98,9 @@ void BackgroundSlicingProcess::process_sla()
|
|||||||
m_print->process();
|
m_print->process();
|
||||||
if (this->set_step_started(bspsGCodeFinalize)) {
|
if (this->set_step_started(bspsGCodeFinalize)) {
|
||||||
if (! m_export_path.empty()) {
|
if (! m_export_path.empty()) {
|
||||||
m_sla_print->export_raster(m_export_path);
|
const std::string export_path = m_sla_print->print_statistics().finalize_output_path(m_export_path);
|
||||||
m_print->set_status(100, "Masked SLA file exported to " + m_export_path);
|
m_sla_print->export_raster(export_path);
|
||||||
|
m_print->set_status(100, "Masked SLA file exported to " + export_path);
|
||||||
} else if (! m_upload_job.empty()) {
|
} else if (! m_upload_job.empty()) {
|
||||||
prepare_upload();
|
prepare_upload();
|
||||||
} else {
|
} else {
|
||||||
@ -389,7 +390,7 @@ void BackgroundSlicingProcess::prepare_upload()
|
|||||||
|
|
||||||
// Generate a unique temp path to which the gcode/zip file is copied/exported
|
// Generate a unique temp path to which the gcode/zip file is copied/exported
|
||||||
boost::filesystem::path source_path = boost::filesystem::temp_directory_path()
|
boost::filesystem::path source_path = boost::filesystem::temp_directory_path()
|
||||||
/ boost::filesystem::unique_path(".printhost.%%%%-%%%%-%%%%-%%%%.gcode");
|
/ boost::filesystem::unique_path(".Slic3rPE.upload.%%%%-%%%%-%%%%-%%%%");
|
||||||
|
|
||||||
if (m_print == m_fff_print) {
|
if (m_print == m_fff_print) {
|
||||||
m_print->set_status(95, "Running post-processing scripts");
|
m_print->set_status(95, "Running post-processing scripts");
|
||||||
@ -399,8 +400,8 @@ void BackgroundSlicingProcess::prepare_upload()
|
|||||||
run_post_process_scripts(source_path.string(), m_fff_print->config());
|
run_post_process_scripts(source_path.string(), m_fff_print->config());
|
||||||
m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
|
m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
|
||||||
} else {
|
} else {
|
||||||
m_sla_print->export_raster(source_path.string());
|
m_upload_job.upload_data.upload_path = m_sla_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string());
|
||||||
// TODO: Also finalize upload path like with FFF when there are statistics for SLA print
|
m_sla_print->export_raster(source_path.string(), m_upload_job.upload_data.upload_path.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str());
|
m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str());
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include "BitmapCache.hpp"
|
#include "BitmapCache.hpp"
|
||||||
|
|
||||||
|
#include "libslic3r/Utils.hpp"
|
||||||
|
|
||||||
#if ! defined(WIN32) && ! defined(__APPLE__)
|
#if ! defined(WIN32) && ! defined(__APPLE__)
|
||||||
#define BROKEN_ALPHA
|
#define BROKEN_ALPHA
|
||||||
#endif
|
#endif
|
||||||
@ -9,6 +11,11 @@
|
|||||||
#include <wx/rawbmp.h>
|
#include <wx/rawbmp.h>
|
||||||
#endif /* BROKEN_ALPHA */
|
#endif /* BROKEN_ALPHA */
|
||||||
|
|
||||||
|
#define NANOSVG_IMPLEMENTATION
|
||||||
|
#include "nanosvg/nanosvg.h"
|
||||||
|
#define NANOSVGRAST_IMPLEMENTATION
|
||||||
|
#include "nanosvg/nanosvgrast.h"
|
||||||
|
|
||||||
namespace Slic3r { namespace GUI {
|
namespace Slic3r { namespace GUI {
|
||||||
|
|
||||||
void BitmapCache::clear()
|
void BitmapCache::clear()
|
||||||
@ -155,6 +162,86 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *beg
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxBitmap* BitmapCache::insert_raw_rgba(const std::string &bitmap_key, unsigned int width, unsigned int height, const unsigned char *raw_data)
|
||||||
|
{
|
||||||
|
wxImage image(width, height);
|
||||||
|
image.InitAlpha();
|
||||||
|
unsigned char *rgb = image.GetData();
|
||||||
|
unsigned char *alpha = image.GetAlpha();
|
||||||
|
unsigned int pixels = width * height;
|
||||||
|
for (unsigned int i = 0; i < pixels; ++ i) {
|
||||||
|
*rgb ++ = *raw_data ++;
|
||||||
|
*rgb ++ = *raw_data ++;
|
||||||
|
*rgb ++ = *raw_data ++;
|
||||||
|
*alpha ++ = *raw_data ++;
|
||||||
|
}
|
||||||
|
return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)));
|
||||||
|
}
|
||||||
|
|
||||||
|
wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned int width, unsigned int height)
|
||||||
|
{
|
||||||
|
std::string bitmap_key = bitmap_name + ( height !=0 ?
|
||||||
|
"-h" + std::to_string(height) :
|
||||||
|
"-w" + std::to_string(width));
|
||||||
|
auto it = m_map.find(bitmap_key);
|
||||||
|
if (it != m_map.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
wxImage image;
|
||||||
|
if (! image.LoadFile(Slic3r::GUI::from_u8(Slic3r::var(bitmap_name + ".png")), wxBITMAP_TYPE_PNG) ||
|
||||||
|
image.GetWidth() == 0 || image.GetHeight() == 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (height != 0 && image.GetHeight() != height)
|
||||||
|
width = int(0.5f + float(image.GetWidth()) * height / image.GetHeight());
|
||||||
|
else if (width != 0 && image.GetWidth() != width)
|
||||||
|
height = int(0.5f + float(image.GetHeight()) * width / image.GetWidth());
|
||||||
|
|
||||||
|
if (height != 0 && width != 0)
|
||||||
|
image.Rescale(width, height, wxIMAGE_QUALITY_BILINEAR);
|
||||||
|
|
||||||
|
return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)));
|
||||||
|
}
|
||||||
|
|
||||||
|
wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned int target_width, unsigned int target_height)
|
||||||
|
{
|
||||||
|
std::string bitmap_key = bitmap_name + (target_height != 0 ?
|
||||||
|
"-h" + std::to_string(target_height) :
|
||||||
|
"-w" + std::to_string(target_width));
|
||||||
|
auto it = m_map.find(bitmap_key);
|
||||||
|
if (it != m_map.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
NSVGimage *image = ::nsvgParseFromFile(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f);
|
||||||
|
if (image == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
float scale = target_height != 0 ?
|
||||||
|
(float)target_height / image->height : target_width != 0 ?
|
||||||
|
(float)target_width / image->width : 1;
|
||||||
|
|
||||||
|
int width = (int)(scale * image->width + 0.5f);
|
||||||
|
int height = (int)(scale * image->height + 0.5f);
|
||||||
|
int n_pixels = width * height;
|
||||||
|
if (n_pixels <= 0) {
|
||||||
|
::nsvgDelete(image);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSVGrasterizer *rast = ::nsvgCreateRasterizer();
|
||||||
|
if (rast == nullptr) {
|
||||||
|
::nsvgDelete(image);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> data(n_pixels * 4, 0);
|
||||||
|
::nsvgRasterize(rast, image, 0, 0, scale, data.data(), width, height, width * 4);
|
||||||
|
::nsvgDeleteRasterizer(rast);
|
||||||
|
::nsvgDelete(image);
|
||||||
|
|
||||||
|
return this->insert_raw_rgba(bitmap_key, width, height, data.data());
|
||||||
|
}
|
||||||
|
|
||||||
wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency)
|
wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency)
|
||||||
{
|
{
|
||||||
wxImage image(width, height);
|
wxImage image(width, height);
|
||||||
|
@ -29,6 +29,12 @@ public:
|
|||||||
wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3);
|
wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3);
|
||||||
wxBitmap* insert(const std::string &name, const std::vector<wxBitmap> &bmps) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size()); }
|
wxBitmap* insert(const std::string &name, const std::vector<wxBitmap> &bmps) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size()); }
|
||||||
wxBitmap* insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end);
|
wxBitmap* insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end);
|
||||||
|
wxBitmap* insert_raw_rgba(const std::string &bitmap_key, unsigned int width, unsigned int height, const unsigned char *raw_data);
|
||||||
|
|
||||||
|
// Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero.
|
||||||
|
wxBitmap* load_png(const std::string &bitmap_key, unsigned int width = 0, unsigned int height = 0);
|
||||||
|
// Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width.
|
||||||
|
wxBitmap* load_svg(const std::string &bitmap_key, unsigned int width = 0, unsigned int height = 0);
|
||||||
|
|
||||||
static wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency);
|
static wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency);
|
||||||
static wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3]) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); }
|
static wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3]) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); }
|
||||||
|
@ -3283,7 +3283,7 @@ bool GLCanvas3D::_init_toolbar()
|
|||||||
item.sprite_id = 8;
|
item.sprite_id = 8;
|
||||||
item.is_toggable = true;
|
item.is_toggable = true;
|
||||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); };
|
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); };
|
||||||
item.visibility_callback = GLToolbarItem::Default_Visibility_Callback;
|
item.visibility_callback = [this]()->bool { return m_process->current_printer_technology() == ptFFF; };
|
||||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); };
|
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); };
|
||||||
if (!m_toolbar.add_item(item))
|
if (!m_toolbar.add_item(item))
|
||||||
return false;
|
return false;
|
||||||
|
@ -13,15 +13,11 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#define NANOSVG_IMPLEMENTATION
|
|
||||||
#include "nanosvg/nanosvg.h"
|
#include "nanosvg/nanosvg.h"
|
||||||
#define NANOSVGRAST_IMPLEMENTATION
|
|
||||||
#include "nanosvg/nanosvgrast.h"
|
#include "nanosvg/nanosvgrast.h"
|
||||||
|
|
||||||
#include "libslic3r/Utils.hpp"
|
#include "libslic3r/Utils.hpp"
|
||||||
|
|
||||||
#include "libslic3r/Utils.hpp"
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
@ -380,6 +376,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
|
|||||||
if (n_pixels <= 0)
|
if (n_pixels <= 0)
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
nsvgDelete(image);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,13 +82,31 @@ ObjectList::ObjectList(wxWindow* parent) :
|
|||||||
init_icons();
|
init_icons();
|
||||||
|
|
||||||
// describe control behavior
|
// describe control behavior
|
||||||
Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxEvent& event) {
|
Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent& event) {
|
||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
// On Windows and Linux, forces a kill focus emulation on the object manipulator fields because this event handler is called
|
// On Windows and Linux, forces a kill focus emulation on the object manipulator fields because this event handler is called
|
||||||
// before the kill focus event handler on the object manipulator when changing selection in the list, invalidating the object
|
// before the kill focus event handler on the object manipulator when changing selection in the list, invalidating the object
|
||||||
// manipulator cache with the following call to selection_changed()
|
// manipulator cache with the following call to selection_changed()
|
||||||
wxGetApp().obj_manipul()->emulate_kill_focus();
|
wxGetApp().obj_manipul()->emulate_kill_focus();
|
||||||
#endif // __APPLE__
|
#endif // __APPLE__
|
||||||
|
|
||||||
|
/* For multiple selection with pressed SHIFT,
|
||||||
|
* event.GetItem() returns value of a first item in selection list
|
||||||
|
* instead of real last clicked item.
|
||||||
|
* So, let check last selected item in such strange way
|
||||||
|
*/
|
||||||
|
if (wxGetKeyState(WXK_SHIFT))
|
||||||
|
{
|
||||||
|
wxDataViewItemArray sels;
|
||||||
|
GetSelections(sels);
|
||||||
|
if (sels.front() == m_last_selected_item)
|
||||||
|
m_last_selected_item = sels.back();
|
||||||
|
else
|
||||||
|
m_last_selected_item = event.GetItem();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_last_selected_item = event.GetItem();
|
||||||
|
|
||||||
selection_changed();
|
selection_changed();
|
||||||
#ifndef __WXMSW__
|
#ifndef __WXMSW__
|
||||||
set_tooltip_for_item(get_mouse_position_in_control());
|
set_tooltip_for_item(get_mouse_position_in_control());
|
||||||
@ -509,7 +527,7 @@ void ObjectList::key_event(wxKeyEvent& event)
|
|||||||
) {
|
) {
|
||||||
remove();
|
remove();
|
||||||
}
|
}
|
||||||
else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_SHIFT))
|
else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/))
|
||||||
select_item_all_children();
|
select_item_all_children();
|
||||||
else
|
else
|
||||||
event.Skip();
|
event.Skip();
|
||||||
@ -1005,8 +1023,7 @@ wxMenuItem* ObjectList::append_menu_item_instance_to_object(wxMenu* menu)
|
|||||||
|
|
||||||
void ObjectList::append_menu_items_osx(wxMenu* menu)
|
void ObjectList::append_menu_items_osx(wxMenu* menu)
|
||||||
{
|
{
|
||||||
append_menu_item(menu, wxID_ANY, _(L("Delete item")), "",
|
append_menu_item_delete(menu);
|
||||||
[this](wxCommandEvent&) { remove(); }, "", menu);
|
|
||||||
|
|
||||||
append_menu_item(menu, wxID_ANY, _(L("Rename")), "",
|
append_menu_item(menu, wxID_ANY, _(L("Rename")), "",
|
||||||
[this](wxCommandEvent&) { rename_item(); }, "", menu);
|
[this](wxCommandEvent&) { rename_item(); }, "", menu);
|
||||||
@ -1025,7 +1042,7 @@ void ObjectList::append_menu_item_fix_through_netfabb(wxMenu* menu)
|
|||||||
|
|
||||||
void ObjectList::append_menu_item_export_stl(wxMenu* menu) const
|
void ObjectList::append_menu_item_export_stl(wxMenu* menu) const
|
||||||
{
|
{
|
||||||
append_menu_item(menu, wxID_ANY, _(L("Export object as STL")) + dots, "",
|
append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, "",
|
||||||
[](wxCommandEvent&) { wxGetApp().plater()->export_stl(true); }, "", menu);
|
[](wxCommandEvent&) { wxGetApp().plater()->export_stl(true); }, "", menu);
|
||||||
menu->AppendSeparator();
|
menu->AppendSeparator();
|
||||||
}
|
}
|
||||||
@ -1061,6 +1078,12 @@ void ObjectList::append_menu_item_change_extruder(wxMenu* menu) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ObjectList::append_menu_item_delete(wxMenu* menu)
|
||||||
|
{
|
||||||
|
append_menu_item(menu, wxID_ANY, _(L("Delete")), "",
|
||||||
|
[this](wxCommandEvent&) { remove(); }, "", menu);
|
||||||
|
}
|
||||||
|
|
||||||
void ObjectList::create_object_popupmenu(wxMenu *menu)
|
void ObjectList::create_object_popupmenu(wxMenu *menu)
|
||||||
{
|
{
|
||||||
#ifdef __WXOSX__
|
#ifdef __WXOSX__
|
||||||
@ -1101,6 +1124,7 @@ void ObjectList::create_part_popupmenu(wxMenu *menu)
|
|||||||
#endif // __WXOSX__
|
#endif // __WXOSX__
|
||||||
|
|
||||||
append_menu_item_fix_through_netfabb(menu);
|
append_menu_item_fix_through_netfabb(menu);
|
||||||
|
append_menu_item_export_stl(menu);
|
||||||
|
|
||||||
m_menu_item_split_part = append_menu_item_split(menu);
|
m_menu_item_split_part = append_menu_item_split(menu);
|
||||||
|
|
||||||
@ -1117,10 +1141,21 @@ void ObjectList::create_part_popupmenu(wxMenu *menu)
|
|||||||
|
|
||||||
void ObjectList::create_instance_popupmenu(wxMenu*menu)
|
void ObjectList::create_instance_popupmenu(wxMenu*menu)
|
||||||
{
|
{
|
||||||
|
#ifdef __WXOSX__
|
||||||
|
append_menu_item_delete(menu);
|
||||||
|
#endif // __WXOSX__
|
||||||
m_menu_item_split_instances = append_menu_item_instance_to_object(menu);
|
m_menu_item_split_instances = append_menu_item_instance_to_object(menu);
|
||||||
|
|
||||||
|
/* New behavior logic:
|
||||||
|
* 1. Split Object to several separated object, if ALL instances are selected
|
||||||
|
* 2. Separate selected instances from the initial object to the separated object,
|
||||||
|
* if some (not all) instances are selected
|
||||||
|
*/
|
||||||
wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) {
|
wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) {
|
||||||
evt.Enable(can_split_instances()); }, m_menu_item_split_instances->GetId());
|
// evt.Enable(can_split_instances()); }, m_menu_item_split_instances->GetId());
|
||||||
|
evt.SetText(wxGetApp().plater()->canvas3D()->get_selection().is_single_full_object() ?
|
||||||
|
_(L("Set as a Separated Objects")) : _(L("Set as a Separated Object")));
|
||||||
|
}, m_menu_item_split_instances->GetId());
|
||||||
}
|
}
|
||||||
|
|
||||||
wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu)
|
wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu)
|
||||||
@ -1551,7 +1586,7 @@ void ObjectList::split()
|
|||||||
auto opt_keys = model_object->volumes[id]->config.keys();
|
auto opt_keys = model_object->volumes[id]->config.keys();
|
||||||
if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) {
|
if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) {
|
||||||
select_item(m_objects_model->AddSettingsChild(vol_item));
|
select_item(m_objects_model->AddSettingsChild(vol_item));
|
||||||
Collapse(vol_item);
|
/*Collapse*/Expand(vol_item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1638,16 +1673,20 @@ void ObjectList::part_selection_changed()
|
|||||||
bool update_and_show_manipulations = false;
|
bool update_and_show_manipulations = false;
|
||||||
bool update_and_show_settings = false;
|
bool update_and_show_settings = false;
|
||||||
|
|
||||||
if (multiple_selection()) {
|
const auto item = GetSelection();
|
||||||
|
|
||||||
|
if ( multiple_selection() || item && m_objects_model->GetItemType(item) == itInstanceRoot )
|
||||||
|
{
|
||||||
og_name = _(L("Group manipulation"));
|
og_name = _(L("Group manipulation"));
|
||||||
update_and_show_manipulations = true;
|
|
||||||
|
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||||
|
// don't show manipulation panel for case of all Object's parts selection
|
||||||
|
update_and_show_manipulations = !selection.is_single_full_instance();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const auto item = GetSelection();
|
|
||||||
if (item)
|
if (item)
|
||||||
{
|
{
|
||||||
bool is_part = false;
|
|
||||||
if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
|
if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
|
||||||
obj_idx = m_objects_model->GetIdByItem(item);
|
obj_idx = m_objects_model->GetIdByItem(item);
|
||||||
og_name = _(L("Object manipulation"));
|
og_name = _(L("Object manipulation"));
|
||||||
@ -1665,7 +1704,6 @@ void ObjectList::part_selection_changed()
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
og_name = _(L("Part Settings to modify"));
|
og_name = _(L("Part Settings to modify"));
|
||||||
is_part = true;
|
|
||||||
auto main_parent = m_objects_model->GetParent(parent);
|
auto main_parent = m_objects_model->GetParent(parent);
|
||||||
obj_idx = m_objects_model->GetIdByItem(main_parent);
|
obj_idx = m_objects_model->GetIdByItem(main_parent);
|
||||||
const auto volume_id = m_objects_model->GetVolumeIdByItem(parent);
|
const auto volume_id = m_objects_model->GetVolumeIdByItem(parent);
|
||||||
@ -1675,7 +1713,6 @@ void ObjectList::part_selection_changed()
|
|||||||
}
|
}
|
||||||
else if (m_objects_model->GetItemType(item) == itVolume) {
|
else if (m_objects_model->GetItemType(item) == itVolume) {
|
||||||
og_name = _(L("Part manipulation"));
|
og_name = _(L("Part manipulation"));
|
||||||
is_part = true;
|
|
||||||
const auto volume_id = m_objects_model->GetVolumeIdByItem(item);
|
const auto volume_id = m_objects_model->GetVolumeIdByItem(item);
|
||||||
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
|
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
|
||||||
update_and_show_manipulations = true;
|
update_and_show_manipulations = true;
|
||||||
@ -1743,7 +1780,7 @@ void ObjectList::add_object_to_list(size_t obj_idx)
|
|||||||
auto opt_keys = model_object->volumes[id]->config.keys();
|
auto opt_keys = model_object->volumes[id]->config.keys();
|
||||||
if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) {
|
if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) {
|
||||||
select_item(m_objects_model->AddSettingsChild(vol_item));
|
select_item(m_objects_model->AddSettingsChild(vol_item));
|
||||||
Collapse(vol_item);
|
/*Collapse*/Expand(vol_item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expand(item);
|
Expand(item);
|
||||||
@ -1757,7 +1794,7 @@ void ObjectList::add_object_to_list(size_t obj_idx)
|
|||||||
auto opt_keys = model_object->config.keys();
|
auto opt_keys = model_object->config.keys();
|
||||||
if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) {
|
if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) {
|
||||||
select_item(m_objects_model->AddSettingsChild(item));
|
select_item(m_objects_model->AddSettingsChild(item));
|
||||||
Collapse(item);
|
/*Collapse*/Expand(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __WXOSX__
|
#ifndef __WXOSX__
|
||||||
@ -1923,11 +1960,22 @@ bool ObjectList::multiple_selection() const
|
|||||||
return GetSelectedItemsCount() > 1;
|
return GetSelectedItemsCount() > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ObjectList::is_selected(const ItemType type) const
|
||||||
|
{
|
||||||
|
const wxDataViewItem& item = GetSelection();
|
||||||
|
if (item)
|
||||||
|
return m_objects_model->GetItemType(item) == type;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void ObjectList::update_selections()
|
void ObjectList::update_selections()
|
||||||
{
|
{
|
||||||
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||||
wxDataViewItemArray sels;
|
wxDataViewItemArray sels;
|
||||||
|
|
||||||
|
m_selection_mode = smInstance;
|
||||||
|
|
||||||
// We doesn't update selection if SettingsItem for the current object/part is selected
|
// We doesn't update selection if SettingsItem for the current object/part is selected
|
||||||
if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings )
|
if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings )
|
||||||
{
|
{
|
||||||
@ -1941,32 +1989,56 @@ void ObjectList::update_selections()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (selection.is_single_full_object() || selection.is_multiple_full_object())
|
||||||
if (selection.is_single_full_object())
|
|
||||||
{
|
{
|
||||||
sels.Add(m_objects_model->GetItemById(selection.get_object_idx()));
|
const Selection::ObjectIdxsToInstanceIdxsMap& objects_content = selection.get_content();
|
||||||
|
for (const auto& object : objects_content) {
|
||||||
|
if (object.second.size() == 1) // object with 1 instance
|
||||||
|
sels.Add(m_objects_model->GetItemById(object.first));
|
||||||
|
else if (object.second.size() > 1) // object with several instances
|
||||||
|
{
|
||||||
|
wxDataViewItemArray current_sels;
|
||||||
|
GetSelections(current_sels);
|
||||||
|
const wxDataViewItem frst_inst_item = m_objects_model->GetItemByInstanceId(object.first, 0);
|
||||||
|
|
||||||
|
bool root_is_selected = false;
|
||||||
|
for (const auto& item:current_sels)
|
||||||
|
if (item == m_objects_model->GetParent(frst_inst_item) ||
|
||||||
|
item == m_objects_model->GetTopParent(frst_inst_item)) {
|
||||||
|
root_is_selected = true;
|
||||||
|
sels.Add(item);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (selection.is_single_volume() || selection.is_modifier() ||
|
if (root_is_selected)
|
||||||
selection.is_multiple_volume() || selection.is_multiple_full_object()) {
|
continue;
|
||||||
|
|
||||||
|
const Selection::InstanceIdxsList& instances = object.second;
|
||||||
|
for (const auto& inst : instances)
|
||||||
|
sels.Add(m_objects_model->GetItemByInstanceId(object.first, inst));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (selection.is_single_volume() || selection.is_modifier() || selection.is_multiple_volume())
|
||||||
|
{
|
||||||
for (auto idx : selection.get_volume_idxs()) {
|
for (auto idx : selection.get_volume_idxs()) {
|
||||||
const auto gl_vol = selection.get_volume(idx);
|
const auto gl_vol = selection.get_volume(idx);
|
||||||
if (selection.is_multiple_full_object())
|
if (gl_vol->volume_idx() >= 0)
|
||||||
sels.Add(m_objects_model->GetItemById(gl_vol->object_idx()));
|
|
||||||
else if (gl_vol->volume_idx() >= 0)
|
|
||||||
// Only add GLVolumes with non-negative volume_ids. GLVolumes with negative volume ids
|
// Only add GLVolumes with non-negative volume_ids. GLVolumes with negative volume ids
|
||||||
// are not associated with ModelVolumes, but they are temporarily generated by the backend
|
// are not associated with ModelVolumes, but they are temporarily generated by the backend
|
||||||
// (for example, SLA supports or SLA pad).
|
// (for example, SLA supports or SLA pad).
|
||||||
sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx()));
|
sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx()));
|
||||||
}
|
}
|
||||||
|
m_selection_mode = smVolume;
|
||||||
}
|
}
|
||||||
else if (selection.is_single_full_instance() || selection.is_multiple_full_instance()) {
|
else if (selection.is_single_full_instance() || selection.is_multiple_full_instance())
|
||||||
|
{
|
||||||
for (auto idx : selection.get_instance_idxs()) {
|
for (auto idx : selection.get_instance_idxs()) {
|
||||||
sels.Add(m_objects_model->GetItemByInstanceId(selection.get_object_idx(), idx));
|
sels.Add(m_objects_model->GetItemByInstanceId(selection.get_object_idx(), idx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (selection.is_mixed())
|
else if (selection.is_mixed())
|
||||||
{
|
{
|
||||||
auto& objects_content_list = selection.get_content();
|
const Selection::ObjectIdxsToInstanceIdxsMap& objects_content_list = selection.get_content();
|
||||||
|
|
||||||
for (auto idx : selection.get_volume_idxs()) {
|
for (auto idx : selection.get_volume_idxs()) {
|
||||||
const auto gl_vol = selection.get_volume(idx);
|
const auto gl_vol = selection.get_volume(idx);
|
||||||
@ -2000,6 +2072,9 @@ void ObjectList::update_selections()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sels.size() == 0)
|
||||||
|
m_selection_mode = smUndef;
|
||||||
|
|
||||||
select_items(sels);
|
select_items(sels);
|
||||||
|
|
||||||
/* Because of ScrollLines() and GetItemRect() functions are implemented
|
/* Because of ScrollLines() and GetItemRect() functions are implemented
|
||||||
@ -2034,29 +2109,34 @@ void ObjectList::update_selections_on_canvas()
|
|||||||
|
|
||||||
auto add_to_selection = [this](const wxDataViewItem& item, Selection& selection, int instance_idx, bool as_single_selection)
|
auto add_to_selection = [this](const wxDataViewItem& item, Selection& selection, int instance_idx, bool as_single_selection)
|
||||||
{
|
{
|
||||||
if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
|
const ItemType& type = m_objects_model->GetItemType(item);
|
||||||
selection.add_object(m_objects_model->GetIdByItem(item), as_single_selection);
|
if ( type == itInstanceRoot || m_objects_model->GetParent(item) == wxDataViewItem(0) ) {
|
||||||
|
wxDataViewItem obj_item = type == itInstanceRoot ? m_objects_model->GetParent(item) : item;
|
||||||
|
selection.add_object(m_objects_model->GetIdByItem(obj_item), as_single_selection);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_objects_model->GetItemType(item) == itVolume) {
|
if (type == itVolume) {
|
||||||
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item));
|
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item));
|
||||||
const int vol_idx = m_objects_model->GetVolumeIdByItem(item);
|
const int vol_idx = m_objects_model->GetVolumeIdByItem(item);
|
||||||
selection.add_volume(obj_idx, vol_idx, std::max(instance_idx, 0), as_single_selection);
|
selection.add_volume(obj_idx, vol_idx, std::max(instance_idx, 0), as_single_selection);
|
||||||
}
|
}
|
||||||
else if (m_objects_model->GetItemType(item) == itInstance) {
|
else if (type == itInstance) {
|
||||||
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
|
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
|
||||||
const int inst_idx = m_objects_model->GetInstanceIdByItem(item);
|
const int inst_idx = m_objects_model->GetInstanceIdByItem(item);
|
||||||
selection.add_instance(obj_idx, inst_idx, as_single_selection);
|
selection.add_instance(obj_idx, inst_idx, as_single_selection);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// stores current instance idx before to clear the selection
|
||||||
|
int instance_idx = selection.get_instance_idx();
|
||||||
|
|
||||||
if (sel_cnt == 1) {
|
if (sel_cnt == 1) {
|
||||||
wxDataViewItem item = GetSelection();
|
wxDataViewItem item = GetSelection();
|
||||||
if (m_objects_model->GetItemType(item) & (itSettings|itInstanceRoot))
|
if (m_objects_model->GetItemType(item) & (itSettings|itInstanceRoot))
|
||||||
add_to_selection(m_objects_model->GetParent(item), selection, -1, true);
|
add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, true);
|
||||||
else
|
else
|
||||||
add_to_selection(item, selection, -1, true);
|
add_to_selection(item, selection, instance_idx, true);
|
||||||
|
|
||||||
wxGetApp().plater()->canvas3D()->update_gizmos_on_off_state();
|
wxGetApp().plater()->canvas3D()->update_gizmos_on_off_state();
|
||||||
return;
|
return;
|
||||||
@ -2065,8 +2145,6 @@ void ObjectList::update_selections_on_canvas()
|
|||||||
wxDataViewItemArray sels;
|
wxDataViewItemArray sels;
|
||||||
GetSelections(sels);
|
GetSelections(sels);
|
||||||
|
|
||||||
// stores current instance idx before to clear the selection
|
|
||||||
int instance_idx = selection.get_instance_idx();
|
|
||||||
selection.clear();
|
selection.clear();
|
||||||
for (auto item: sels)
|
for (auto item: sels)
|
||||||
add_to_selection(item, selection, instance_idx, false);
|
add_to_selection(item, selection, instance_idx, false);
|
||||||
@ -2112,22 +2190,101 @@ void ObjectList::select_item_all_children()
|
|||||||
if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject) {
|
if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject) {
|
||||||
for (int i = 0; i < m_objects->size(); i++)
|
for (int i = 0; i < m_objects->size(); i++)
|
||||||
sels.Add(m_objects_model->GetItemById(i));
|
sels.Add(m_objects_model->GetItemById(i));
|
||||||
|
m_selection_mode = smInstance;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const auto item = GetSelection();
|
const auto item = GetSelection();
|
||||||
// Some volume(instance) is selected => select all volumes(instances) inside the current object
|
// Some volume(instance) is selected => select all volumes(instances) inside the current object
|
||||||
if (m_objects_model->GetItemType(item) & (itVolume | itInstance)) {
|
if (m_objects_model->GetItemType(item) & (itVolume | itInstance))
|
||||||
m_objects_model->GetChildren(m_objects_model->GetParent(item), sels);
|
m_objects_model->GetChildren(m_objects_model->GetParent(item), sels);
|
||||||
}
|
|
||||||
|
m_selection_mode = m_objects_model->GetItemType(item)&itVolume ? smVolume : smInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetSelections(sels);
|
SetSelections(sels);
|
||||||
selection_changed();
|
selection_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update selection mode for non-multiple selection
|
||||||
|
void ObjectList::update_selection_mode()
|
||||||
|
{
|
||||||
|
// All items are unselected
|
||||||
|
if (!GetSelection())
|
||||||
|
{
|
||||||
|
m_last_selected_item = wxDataViewItem(0);
|
||||||
|
m_selection_mode = smUndef;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ItemType type = m_objects_model->GetItemType(GetSelection());
|
||||||
|
m_selection_mode = type&itSettings ? smUndef :
|
||||||
|
type&itVolume ? smVolume : smInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check last selected item. If is it possible to select it
|
||||||
|
bool ObjectList::check_last_selection(wxString& msg_str)
|
||||||
|
{
|
||||||
|
if (!m_last_selected_item)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const bool is_shift_pressed = wxGetKeyState(WXK_SHIFT);
|
||||||
|
|
||||||
|
/* We can't mix Parts and Objects/Instances.
|
||||||
|
* So, show information about it
|
||||||
|
*/
|
||||||
|
const ItemType type = m_objects_model->GetItemType(m_last_selected_item);
|
||||||
|
|
||||||
|
// check a case of a selection of the Parts from different Objects
|
||||||
|
bool impossible_multipart_selection = false;
|
||||||
|
if (type & itVolume && m_selection_mode == smVolume)
|
||||||
|
{
|
||||||
|
wxDataViewItemArray sels;
|
||||||
|
GetSelections(sels);
|
||||||
|
for (const auto& sel: sels)
|
||||||
|
if (sel != m_last_selected_item &&
|
||||||
|
m_objects_model->GetParent(sel) != m_objects_model->GetParent(m_last_selected_item))
|
||||||
|
{
|
||||||
|
impossible_multipart_selection = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (impossible_multipart_selection ||
|
||||||
|
type & itSettings ||
|
||||||
|
type & itVolume && m_selection_mode == smInstance ||
|
||||||
|
!(type & itVolume) && m_selection_mode == smVolume)
|
||||||
|
{
|
||||||
|
// Inform user why selection isn't complited
|
||||||
|
const wxString item_type = m_selection_mode == smInstance ? _(L("Object or Instance")) : _(L("Part"));
|
||||||
|
|
||||||
|
msg_str = wxString::Format( _(L("Unsupported selection")) + "\n\n" +
|
||||||
|
_(L("You started your selection with %s Item.")) + "\n" +
|
||||||
|
_(L("In this mode you can select only other %s Items%s")),
|
||||||
|
item_type, item_type,
|
||||||
|
m_selection_mode == smInstance ? "." :
|
||||||
|
" " + _(L("of a current Object")));
|
||||||
|
|
||||||
|
// Unselect last selected item, if selection is without SHIFT
|
||||||
|
if (!is_shift_pressed) {
|
||||||
|
Unselect(m_last_selected_item);
|
||||||
|
show_info(this, msg_str, _(L("Info")));
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_shift_pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void ObjectList::fix_multiselection_conflicts()
|
void ObjectList::fix_multiselection_conflicts()
|
||||||
{
|
{
|
||||||
if (GetSelectedItemsCount() <= 1)
|
if (GetSelectedItemsCount() <= 1) {
|
||||||
|
update_selection_mode();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString msg_string;
|
||||||
|
if (!check_last_selection(msg_string))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_prevent_list_events = true;
|
m_prevent_list_events = true;
|
||||||
@ -2135,12 +2292,58 @@ void ObjectList::fix_multiselection_conflicts()
|
|||||||
wxDataViewItemArray sels;
|
wxDataViewItemArray sels;
|
||||||
GetSelections(sels);
|
GetSelections(sels);
|
||||||
|
|
||||||
for (auto item : sels) {
|
if (m_selection_mode == smVolume)
|
||||||
if (m_objects_model->GetItemType(item) & (itSettings|itInstanceRoot))
|
{
|
||||||
Unselect(item);
|
// identify correct parent of the initial selected item
|
||||||
else if (m_objects_model->GetParent(item) != wxDataViewItem(0))
|
const wxDataViewItem& parent = m_objects_model->GetParent(m_last_selected_item == sels.front() ? sels.back() : sels.front());
|
||||||
Unselect(m_objects_model->GetParent(item));
|
|
||||||
|
sels.clear();
|
||||||
|
wxDataViewItemArray children; // selected volumes from current parent
|
||||||
|
m_objects_model->GetChildren(parent, children);
|
||||||
|
|
||||||
|
for (const auto child : children)
|
||||||
|
if (IsSelected(child) && m_objects_model->GetItemType(child)&itVolume)
|
||||||
|
sels.Add(child);
|
||||||
|
|
||||||
|
// If some part is selected, unselect all items except of selected parts of the current object
|
||||||
|
UnselectAll();
|
||||||
|
SetSelections(sels);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (const auto item : sels)
|
||||||
|
{
|
||||||
|
if (!IsSelected(item)) // if this item is unselected now (from previous actions)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (m_objects_model->GetItemType(item) & itSettings) {
|
||||||
|
Unselect(item);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wxDataViewItem& parent = m_objects_model->GetParent(item);
|
||||||
|
if (parent != wxDataViewItem(0) && IsSelected(parent))
|
||||||
|
Unselect(parent);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxDataViewItemArray unsels;
|
||||||
|
m_objects_model->GetAllChildren(item, unsels);
|
||||||
|
for (const auto unsel_item : unsels)
|
||||||
|
Unselect(unsel_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_objects_model->GetItemType(item) & itVolume)
|
||||||
|
Unselect(item);
|
||||||
|
|
||||||
|
m_selection_mode = smInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!msg_string.IsEmpty())
|
||||||
|
show_info(this, msg_string, _(L("Info")));
|
||||||
|
|
||||||
|
if (!IsSelected(m_last_selected_item))
|
||||||
|
m_last_selected_item = wxDataViewItem(0);
|
||||||
|
|
||||||
m_prevent_list_events = false;
|
m_prevent_list_events = false;
|
||||||
}
|
}
|
||||||
@ -2272,6 +2475,12 @@ void ObjectList::update_object_menu()
|
|||||||
|
|
||||||
void ObjectList::instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idxs)
|
void ObjectList::instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idxs)
|
||||||
{
|
{
|
||||||
|
if ((*m_objects)[obj_idx]->instances.size() == inst_idxs.size())
|
||||||
|
{
|
||||||
|
instances_to_separated_objects(obj_idx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// create new object from selected instance
|
// create new object from selected instance
|
||||||
ModelObject* model_object = (*m_objects)[obj_idx]->get_model()->add_object(*(*m_objects)[obj_idx]);
|
ModelObject* model_object = (*m_objects)[obj_idx]->get_model()->add_object(*(*m_objects)[obj_idx]);
|
||||||
for (int inst_idx = model_object->instances.size() - 1; inst_idx >= 0; inst_idx--)
|
for (int inst_idx = model_object->instances.size() - 1; inst_idx >= 0; inst_idx--)
|
||||||
@ -2292,6 +2501,31 @@ void ObjectList::instances_to_separated_object(const int obj_idx, const std::set
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ObjectList::instances_to_separated_objects(const int obj_idx)
|
||||||
|
{
|
||||||
|
const int inst_cnt = (*m_objects)[obj_idx]->instances.size();
|
||||||
|
|
||||||
|
for (int i = inst_cnt-1; i > 0 ; i--)
|
||||||
|
{
|
||||||
|
// create new object from initial
|
||||||
|
ModelObject* object = (*m_objects)[obj_idx]->get_model()->add_object(*(*m_objects)[obj_idx]);
|
||||||
|
for (int inst_idx = object->instances.size() - 1; inst_idx >= 0; inst_idx--)
|
||||||
|
{
|
||||||
|
if (inst_idx == i)
|
||||||
|
continue;
|
||||||
|
// delete unnecessary instances
|
||||||
|
object->delete_instance(inst_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new object to the object_list
|
||||||
|
add_object_to_list(m_objects->size() - 1);
|
||||||
|
|
||||||
|
// delete current instance from the initial object
|
||||||
|
del_subobject_from_object(obj_idx, i, itInstance);
|
||||||
|
delete_instance_from_list(obj_idx, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ObjectList::split_instances()
|
void ObjectList::split_instances()
|
||||||
{
|
{
|
||||||
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
|
||||||
@ -2299,6 +2533,12 @@ void ObjectList::split_instances()
|
|||||||
if (obj_idx == -1)
|
if (obj_idx == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (selection.is_single_full_object())
|
||||||
|
{
|
||||||
|
instances_to_separated_objects(obj_idx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const int inst_idx = selection.get_instance_idx();
|
const int inst_idx = selection.get_instance_idx();
|
||||||
const std::set<int> inst_idxs = inst_idx < 0 ?
|
const std::set<int> inst_idxs = inst_idx < 0 ?
|
||||||
selection.get_instance_idxs() :
|
selection.get_instance_idxs() :
|
||||||
@ -2424,8 +2664,7 @@ void ObjectList::show_multi_selection_menu()
|
|||||||
wxMenu* menu = new wxMenu();
|
wxMenu* menu = new wxMenu();
|
||||||
|
|
||||||
#ifdef __WXOSX__
|
#ifdef __WXOSX__
|
||||||
append_menu_item(menu, wxID_ANY, _(L("Delete items")), "",
|
append_menu_item_delete(menu);
|
||||||
[this](wxCommandEvent&) { remove(); }, "", menu);
|
|
||||||
#endif //__WXOSX__
|
#endif //__WXOSX__
|
||||||
|
|
||||||
if (extruders_count() > 1)
|
if (extruders_count() > 1)
|
||||||
|
@ -60,6 +60,12 @@ struct ItemForDelete
|
|||||||
|
|
||||||
class ObjectList : public wxDataViewCtrl
|
class ObjectList : public wxDataViewCtrl
|
||||||
{
|
{
|
||||||
|
enum SELECTION_MODE
|
||||||
|
{
|
||||||
|
smUndef,
|
||||||
|
smVolume,
|
||||||
|
smInstance
|
||||||
|
} m_selection_mode {smUndef};
|
||||||
|
|
||||||
struct dragged_item_data
|
struct dragged_item_data
|
||||||
{
|
{
|
||||||
@ -135,6 +141,7 @@ class ObjectList : public wxDataViewCtrl
|
|||||||
bool m_part_settings_changed = false;
|
bool m_part_settings_changed = false;
|
||||||
|
|
||||||
int m_selected_row = 0;
|
int m_selected_row = 0;
|
||||||
|
wxDataViewItem m_last_selected_item {nullptr};
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
FreqSettingsBundle m_freq_settings_fff;
|
FreqSettingsBundle m_freq_settings_fff;
|
||||||
@ -188,6 +195,7 @@ public:
|
|||||||
void append_menu_item_fix_through_netfabb(wxMenu* menu);
|
void append_menu_item_fix_through_netfabb(wxMenu* menu);
|
||||||
void append_menu_item_export_stl(wxMenu* menu) const ;
|
void append_menu_item_export_stl(wxMenu* menu) const ;
|
||||||
void append_menu_item_change_extruder(wxMenu* menu) const;
|
void append_menu_item_change_extruder(wxMenu* menu) const;
|
||||||
|
void append_menu_item_delete(wxMenu* menu);
|
||||||
void create_object_popupmenu(wxMenu *menu);
|
void create_object_popupmenu(wxMenu *menu);
|
||||||
void create_sla_object_popupmenu(wxMenu*menu);
|
void create_sla_object_popupmenu(wxMenu*menu);
|
||||||
void create_part_popupmenu(wxMenu*menu);
|
void create_part_popupmenu(wxMenu*menu);
|
||||||
@ -251,12 +259,15 @@ public:
|
|||||||
|
|
||||||
void init_objects();
|
void init_objects();
|
||||||
bool multiple_selection() const ;
|
bool multiple_selection() const ;
|
||||||
|
bool is_selected(const ItemType type) const;
|
||||||
void update_selections();
|
void update_selections();
|
||||||
void update_selections_on_canvas();
|
void update_selections_on_canvas();
|
||||||
void select_item(const wxDataViewItem& item);
|
void select_item(const wxDataViewItem& item);
|
||||||
void select_items(const wxDataViewItemArray& sels);
|
void select_items(const wxDataViewItemArray& sels);
|
||||||
void select_all();
|
void select_all();
|
||||||
void select_item_all_children();
|
void select_item_all_children();
|
||||||
|
void update_selection_mode();
|
||||||
|
bool check_last_selection(wxString& msg_str);
|
||||||
// correct current selections to avoid of the possible conflicts
|
// correct current selections to avoid of the possible conflicts
|
||||||
void fix_multiselection_conflicts();
|
void fix_multiselection_conflicts();
|
||||||
|
|
||||||
@ -269,6 +280,7 @@ public:
|
|||||||
void update_object_menu();
|
void update_object_menu();
|
||||||
|
|
||||||
void instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idx);
|
void instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idx);
|
||||||
|
void instances_to_separated_objects(const int obj_idx);
|
||||||
void split_instances();
|
void split_instances();
|
||||||
void rename_item();
|
void rename_item();
|
||||||
void fix_through_netfabb() const;
|
void fix_through_netfabb() const;
|
||||||
|
@ -161,6 +161,8 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
|
|||||||
m_new_move_label_string = L("Position");
|
m_new_move_label_string = L("Position");
|
||||||
m_new_rotate_label_string = L("Rotation");
|
m_new_rotate_label_string = L("Rotation");
|
||||||
m_new_scale_label_string = L("Scale factors");
|
m_new_scale_label_string = L("Scale factors");
|
||||||
|
|
||||||
|
ObjectList* obj_list = wxGetApp().obj_list();
|
||||||
if (selection.is_single_full_instance())
|
if (selection.is_single_full_instance())
|
||||||
{
|
{
|
||||||
// all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one
|
// all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one
|
||||||
@ -187,7 +189,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
|
|||||||
|
|
||||||
m_new_enabled = true;
|
m_new_enabled = true;
|
||||||
}
|
}
|
||||||
else if (selection.is_single_full_object())
|
else if (selection.is_single_full_object() && obj_list->is_selected(itObject))
|
||||||
{
|
{
|
||||||
m_cache.instance.reset();
|
m_cache.instance.reset();
|
||||||
|
|
||||||
@ -212,7 +214,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
|
|||||||
m_new_size = (volume->get_volume_transformation().get_matrix(true, true) * volume->bounding_box.size()).cwiseAbs();
|
m_new_size = (volume->get_volume_transformation().get_matrix(true, true) * volume->bounding_box.size()).cwiseAbs();
|
||||||
m_new_enabled = true;
|
m_new_enabled = true;
|
||||||
}
|
}
|
||||||
else if (wxGetApp().obj_list()->multiple_selection())
|
else if (obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot))
|
||||||
{
|
{
|
||||||
reset_settings_value();
|
reset_settings_value();
|
||||||
m_new_move_label_string = L("Translate");
|
m_new_move_label_string = L("Translate");
|
||||||
|
@ -16,12 +16,6 @@ namespace Slic3r {
|
|||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// GLGizmoCut
|
|
||||||
|
|
||||||
class GLGizmoCutPanel : public wxPanel
|
class GLGizmoCutPanel : public wxPanel
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -193,7 +187,7 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, co
|
|||||||
m_imgui->set_next_window_bg_alpha(0.5f);
|
m_imgui->set_next_window_bg_alpha(0.5f);
|
||||||
m_imgui->begin(_(L("Cut")), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
m_imgui->begin(_(L("Cut")), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
||||||
|
|
||||||
ImGui::PushItemWidth(100.0f);
|
ImGui::PushItemWidth(m_imgui->scaled(5.0f));
|
||||||
bool _value_changed = ImGui::InputDouble("Z", &m_cut_z, 0.0f, 0.0f, "%.2f");
|
bool _value_changed = ImGui::InputDouble("Z", &m_cut_z, 0.0f, 0.0f, "%.2f");
|
||||||
|
|
||||||
m_imgui->checkbox(_(L("Keep upper part")), m_keep_upper);
|
m_imgui->checkbox(_(L("Keep upper part")), m_keep_upper);
|
||||||
|
@ -38,6 +38,8 @@
|
|||||||
#include "libslic3r/SLA/SLARotfinder.hpp"
|
#include "libslic3r/SLA/SLARotfinder.hpp"
|
||||||
#include "libslic3r/Utils.hpp"
|
#include "libslic3r/Utils.hpp"
|
||||||
|
|
||||||
|
#include "libnest2d/optimizers/nlopt/genetic.hpp"
|
||||||
|
|
||||||
#include "GUI.hpp"
|
#include "GUI.hpp"
|
||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
#include "GUI_ObjectList.hpp"
|
#include "GUI_ObjectList.hpp"
|
||||||
@ -1199,7 +1201,7 @@ struct Plater::priv
|
|||||||
BoundingBox scaled_bed_shape_bb() const;
|
BoundingBox scaled_bed_shape_bb() const;
|
||||||
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config);
|
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config);
|
||||||
std::vector<size_t> load_model_objects(const ModelObjectPtrs &model_objects);
|
std::vector<size_t> load_model_objects(const ModelObjectPtrs &model_objects);
|
||||||
std::unique_ptr<CheckboxFileDialog> get_export_file(GUI::FileType file_type);
|
wxString get_export_file(GUI::FileType file_type);
|
||||||
|
|
||||||
const Selection& get_selection() const;
|
const Selection& get_selection() const;
|
||||||
Selection& get_selection();
|
Selection& get_selection();
|
||||||
@ -1617,6 +1619,45 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
}
|
}
|
||||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
}
|
}
|
||||||
|
else if ((wxGetApp().get_mode() == comSimple) && (type_3mf || type_any_amf))
|
||||||
|
{
|
||||||
|
bool advanced = false;
|
||||||
|
for (const ModelObject* model_object : model.objects)
|
||||||
|
{
|
||||||
|
// is there more than one instance ?
|
||||||
|
if (model_object->instances.size() > 1)
|
||||||
|
{
|
||||||
|
advanced = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// is there any modifier ?
|
||||||
|
for (const ModelVolume* model_volume : model_object->volumes)
|
||||||
|
{
|
||||||
|
if (!model_volume->is_model_part())
|
||||||
|
{
|
||||||
|
advanced = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (advanced)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (advanced)
|
||||||
|
{
|
||||||
|
wxMessageDialog dlg(q, _(L("This file cannot be loaded in simple mode. Do you want to switch to expert mode?\n")),
|
||||||
|
_(L("Detected advanced data")), wxICON_WARNING | wxYES | wxNO);
|
||||||
|
if (dlg.ShowModal() == wxID_YES)
|
||||||
|
{
|
||||||
|
Slic3r::GUI::wxGetApp().save_mode(comExpert);
|
||||||
|
view3D->set_as_dirty();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return obj_idxs;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
|
|
||||||
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||||
@ -1642,7 +1683,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
Slic3r::GUI::show_error(nullptr,
|
Slic3r::GUI::show_error(nullptr,
|
||||||
wxString::Format(_(L("You can't to add the object(s) from %s because of one or some of them is(are) multi-part")),
|
wxString::Format(_(L("You can't to add the object(s) from %s because of one or some of them is(are) multi-part")),
|
||||||
from_path(filename)));
|
from_path(filename)));
|
||||||
return std::vector<size_t>();
|
return obj_idxs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1784,7 +1825,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
|
|||||||
return obj_idxs;
|
return obj_idxs;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<CheckboxFileDialog> Plater::priv::get_export_file(GUI::FileType file_type)
|
wxString Plater::priv::get_export_file(GUI::FileType file_type)
|
||||||
{
|
{
|
||||||
wxString wildcard;
|
wxString wildcard;
|
||||||
switch (file_type) {
|
switch (file_type) {
|
||||||
@ -1801,34 +1842,56 @@ std::unique_ptr<CheckboxFileDialog> Plater::priv::get_export_file(GUI::FileType
|
|||||||
|
|
||||||
// Update printbility state of each of the ModelInstances.
|
// Update printbility state of each of the ModelInstances.
|
||||||
this->update_print_volume_state();
|
this->update_print_volume_state();
|
||||||
// Find the file name of the first printable object.
|
|
||||||
fs::path output_file = this->model.propose_export_file_name_and_path();
|
|
||||||
|
|
||||||
|
const Selection& selection = get_selection();
|
||||||
|
int obj_idx = selection.get_object_idx();
|
||||||
|
|
||||||
|
fs::path output_file;
|
||||||
|
// first try to get the file name from the current selection
|
||||||
|
if ((0 <= obj_idx) && (obj_idx < (int)this->model.objects.size()))
|
||||||
|
output_file = this->model.objects[obj_idx]->get_export_filename();
|
||||||
|
|
||||||
|
if (output_file.empty())
|
||||||
|
// Find the file name of the first printable object.
|
||||||
|
output_file = this->model.propose_export_file_name_and_path();
|
||||||
|
|
||||||
|
wxString dlg_title;
|
||||||
switch (file_type) {
|
switch (file_type) {
|
||||||
case FT_STL: output_file.replace_extension("stl"); break;
|
case FT_STL:
|
||||||
case FT_AMF: output_file.replace_extension("zip.amf"); break; // XXX: Problem on OS X with double extension?
|
{
|
||||||
case FT_3MF: output_file.replace_extension("3mf"); break;
|
output_file.replace_extension("stl");
|
||||||
|
dlg_title = _(L("Export STL file:"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FT_AMF:
|
||||||
|
{
|
||||||
|
// XXX: Problem on OS X with double extension?
|
||||||
|
output_file.replace_extension("zip.amf");
|
||||||
|
dlg_title = _(L("Export AMF file:"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FT_3MF:
|
||||||
|
{
|
||||||
|
output_file.replace_extension("3mf");
|
||||||
|
dlg_title = _(L("Save file as:"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto dlg = Slic3r::make_unique<CheckboxFileDialog>(q,
|
wxFileDialog* dlg = new wxFileDialog(q, dlg_title,
|
||||||
((file_type == FT_AMF) || (file_type == FT_3MF)) ? _(L("Export print config")) : "",
|
from_path(output_file.parent_path()), from_path(output_file.filename()),
|
||||||
true,
|
wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||||
_(L("Save file as:")),
|
|
||||||
from_path(output_file.parent_path()),
|
|
||||||
from_path(output_file.filename()),
|
|
||||||
wildcard,
|
|
||||||
wxFD_SAVE | wxFD_OVERWRITE_PROMPT
|
|
||||||
);
|
|
||||||
|
|
||||||
if (dlg->ShowModal() != wxID_OK) {
|
if (dlg->ShowModal() != wxID_OK) {
|
||||||
return nullptr;
|
return wxEmptyString;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::path path(into_path(dlg->GetPath()));
|
wxString out_path = dlg->GetPath();
|
||||||
|
fs::path path(into_path(out_path));
|
||||||
wxGetApp().app_config->update_last_output_dir(path.parent_path().string());
|
wxGetApp().app_config->update_last_output_dir(path.parent_path().string());
|
||||||
|
|
||||||
return dlg;
|
return out_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Selection& Plater::priv::get_selection() const
|
const Selection& Plater::priv::get_selection() const
|
||||||
@ -2039,7 +2102,9 @@ void Plater::priv::sla_optimize_rotation() {
|
|||||||
rotoptimizing.store(true);
|
rotoptimizing.store(true);
|
||||||
|
|
||||||
int obj_idx = get_selected_object_idx();
|
int obj_idx = get_selected_object_idx();
|
||||||
ModelObject * o = model.objects[obj_idx];
|
if(obj_idx < 0) { rotoptimizing.store(false); return; }
|
||||||
|
|
||||||
|
ModelObject * o = model.objects[size_t(obj_idx)];
|
||||||
|
|
||||||
background_process.stop();
|
background_process.stop();
|
||||||
|
|
||||||
@ -2047,7 +2112,7 @@ void Plater::priv::sla_optimize_rotation() {
|
|||||||
statusbar()->set_range(100);
|
statusbar()->set_range(100);
|
||||||
|
|
||||||
auto stfn = [this] (unsigned st, const std::string& msg) {
|
auto stfn = [this] (unsigned st, const std::string& msg) {
|
||||||
statusbar()->set_progress(st);
|
statusbar()->set_progress(int(st));
|
||||||
statusbar()->set_status_text(msg);
|
statusbar()->set_status_text(msg);
|
||||||
|
|
||||||
// could be problematic, but we need the cancel button.
|
// could be problematic, but we need the cancel button.
|
||||||
@ -2065,8 +2130,59 @@ void Plater::priv::sla_optimize_rotation() {
|
|||||||
[this](){ return !rotoptimizing.load(); }
|
[this](){ return !rotoptimizing.load(); }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
|
||||||
|
assert(bed_shape_opt);
|
||||||
|
|
||||||
|
auto& bedpoints = bed_shape_opt->values;
|
||||||
|
Polyline bed; bed.points.reserve(bedpoints.size());
|
||||||
|
for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1)));
|
||||||
|
|
||||||
|
double mindist = 6.0; // FIXME
|
||||||
|
double offs = mindist / 2.0 - EPSILON;
|
||||||
|
|
||||||
if(rotoptimizing.load()) // wasn't canceled
|
if(rotoptimizing.load()) // wasn't canceled
|
||||||
for(ModelInstance * oi : o->instances) oi->set_rotation({r[X], r[Y], r[Z]});
|
for(ModelInstance * oi : o->instances) {
|
||||||
|
oi->set_rotation({r[X], r[Y], r[Z]});
|
||||||
|
|
||||||
|
auto trchull = o->convex_hull_2d(oi->get_transformation().get_matrix());
|
||||||
|
|
||||||
|
namespace opt = libnest2d::opt;
|
||||||
|
opt::StopCriteria stopcr;
|
||||||
|
stopcr.relative_score_difference = 0.01;
|
||||||
|
stopcr.max_iterations = 10000;
|
||||||
|
stopcr.stop_score = 0.0;
|
||||||
|
opt::GeneticOptimizer solver(stopcr);
|
||||||
|
Polygon pbed(bed);
|
||||||
|
|
||||||
|
auto bin = pbed.bounding_box();
|
||||||
|
double binw = bin.size()(X) * SCALING_FACTOR - offs;
|
||||||
|
double binh = bin.size()(Y) * SCALING_FACTOR - offs;
|
||||||
|
|
||||||
|
auto result = solver.optimize_min([&trchull, binw, binh](double rot){
|
||||||
|
auto chull = trchull;
|
||||||
|
chull.rotate(rot);
|
||||||
|
|
||||||
|
auto bb = chull.bounding_box();
|
||||||
|
double bbw = bb.size()(X) * SCALING_FACTOR;
|
||||||
|
double bbh = bb.size()(Y) * SCALING_FACTOR;
|
||||||
|
|
||||||
|
auto wdiff = bbw - binw;
|
||||||
|
auto hdiff = bbh - binh;
|
||||||
|
double diff = 0;
|
||||||
|
if(wdiff < 0 && hdiff < 0) diff = wdiff + hdiff;
|
||||||
|
if(wdiff > 0) diff += wdiff;
|
||||||
|
if(hdiff > 0) diff += hdiff;
|
||||||
|
|
||||||
|
return diff;
|
||||||
|
}, opt::initvals(0.0), opt::bound(-PI/2, PI/2));
|
||||||
|
|
||||||
|
double r = std::get<0>(result.optimum);
|
||||||
|
|
||||||
|
Vec3d rt = oi->get_rotation(); rt(Z) += r;
|
||||||
|
oi->set_rotation(rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
arr::find_new_position(model, o->instances, coord_t(mindist/SCALING_FACTOR), bed);
|
||||||
|
|
||||||
// Correct the z offset of the object which was corrupted be the rotation
|
// Correct the z offset of the object which was corrupted be the rotation
|
||||||
o->ensure_on_bed();
|
o->ensure_on_bed();
|
||||||
@ -2714,7 +2830,10 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
|
|||||||
if (is_part) {
|
if (is_part) {
|
||||||
item_delete = append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")),
|
item_delete = append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")),
|
||||||
[this](wxCommandEvent&) { q->remove_selected(); }, "brick_delete.png");
|
[this](wxCommandEvent&) { q->remove_selected(); }, "brick_delete.png");
|
||||||
} else {
|
|
||||||
|
sidebar->obj_list()->append_menu_item_export_stl(menu);
|
||||||
|
}
|
||||||
|
else {
|
||||||
wxMenuItem* item_increase = append_menu_item(menu, wxID_ANY, _(L("Increase copies")) + "\t+", _(L("Place one more copy of the selected object")),
|
wxMenuItem* item_increase = append_menu_item(menu, wxID_ANY, _(L("Increase copies")) + "\t+", _(L("Place one more copy of the selected object")),
|
||||||
[this](wxCommandEvent&) { q->increase_instances(); }, "add.png");
|
[this](wxCommandEvent&) { q->increase_instances(); }, "add.png");
|
||||||
wxMenuItem* item_decrease = append_menu_item(menu, wxID_ANY, _(L("Decrease copies")) + "\t-", _(L("Remove one copy of the selected object")),
|
wxMenuItem* item_decrease = append_menu_item(menu, wxID_ANY, _(L("Decrease copies")) + "\t-", _(L("Remove one copy of the selected object")),
|
||||||
@ -2745,10 +2864,11 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
|
|||||||
append_menu_item(menu, wxID_ANY, _(L("Reload from Disk")), _(L("Reload the selected file from Disk")),
|
append_menu_item(menu, wxID_ANY, _(L("Reload from Disk")), _(L("Reload the selected file from Disk")),
|
||||||
[this](wxCommandEvent&) { reload_from_disk(); });
|
[this](wxCommandEvent&) { reload_from_disk(); });
|
||||||
|
|
||||||
append_menu_item(menu, wxID_ANY, _(L("Export object as STL")) + dots, _(L("Export this single object as STL file")),
|
append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, _(L("Export the selected object as STL file")),
|
||||||
[this](wxCommandEvent&) { q->export_stl(true); });
|
[this](wxCommandEvent&) { q->export_stl(true); });
|
||||||
}
|
|
||||||
menu->AppendSeparator();
|
menu->AppendSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
sidebar->obj_list()->append_menu_item_fix_through_netfabb(menu);
|
sidebar->obj_list()->append_menu_item_fix_through_netfabb(menu);
|
||||||
|
|
||||||
@ -2907,6 +3027,9 @@ bool Plater::priv::can_split() const
|
|||||||
|
|
||||||
bool Plater::priv::layers_height_allowed() const
|
bool Plater::priv::layers_height_allowed() const
|
||||||
{
|
{
|
||||||
|
if (printer_technology != ptFFF)
|
||||||
|
return false;
|
||||||
|
|
||||||
int obj_idx = get_selected_object_idx();
|
int obj_idx = get_selected_object_idx();
|
||||||
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed();
|
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed();
|
||||||
}
|
}
|
||||||
@ -3243,11 +3366,8 @@ void Plater::export_stl(bool selection_only)
|
|||||||
{
|
{
|
||||||
if (p->model.objects.empty()) { return; }
|
if (p->model.objects.empty()) { return; }
|
||||||
|
|
||||||
auto dialog = p->get_export_file(FT_STL);
|
wxString path = p->get_export_file(FT_STL);
|
||||||
if (! dialog) { return; }
|
if (path.empty()) { return; }
|
||||||
|
|
||||||
// Store a binary STL
|
|
||||||
const wxString path = dialog->GetPath();
|
|
||||||
const std::string path_u8 = into_u8(path);
|
const std::string path_u8 = into_u8(path);
|
||||||
|
|
||||||
wxBusyCursor wait;
|
wxBusyCursor wait;
|
||||||
@ -3259,10 +3379,25 @@ void Plater::export_stl(bool selection_only)
|
|||||||
|
|
||||||
const auto obj_idx = selection.get_object_idx();
|
const auto obj_idx = selection.get_object_idx();
|
||||||
if (obj_idx == -1) { return; }
|
if (obj_idx == -1) { return; }
|
||||||
mesh = p->model.objects[obj_idx]->mesh();
|
|
||||||
} else {
|
const ModelObject* model_object = p->model.objects[obj_idx];
|
||||||
mesh = p->model.mesh();
|
if (selection.get_mode() == Selection::Instance)
|
||||||
|
{
|
||||||
|
if (selection.is_single_full_object())
|
||||||
|
mesh = model_object->mesh();
|
||||||
|
else
|
||||||
|
mesh = model_object->full_raw_mesh();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||||
|
mesh = model_object->volumes[volume->volume_idx()]->mesh;
|
||||||
|
mesh.transform(volume->get_volume_transformation().get_matrix());
|
||||||
|
mesh.translate(-model_object->origin_translation.cast<float>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mesh = p->model.mesh();
|
||||||
|
|
||||||
Slic3r::store_stl(path_u8.c_str(), &mesh, true);
|
Slic3r::store_stl(path_u8.c_str(), &mesh, true);
|
||||||
p->statusbar()->set_status_text(wxString::Format(_(L("STL file exported to %s")), path));
|
p->statusbar()->set_status_text(wxString::Format(_(L("STL file exported to %s")), path));
|
||||||
@ -3272,15 +3407,14 @@ void Plater::export_amf()
|
|||||||
{
|
{
|
||||||
if (p->model.objects.empty()) { return; }
|
if (p->model.objects.empty()) { return; }
|
||||||
|
|
||||||
auto dialog = p->get_export_file(FT_AMF);
|
wxString path = p->get_export_file(FT_AMF);
|
||||||
if (! dialog) { return; }
|
if (path.empty()) { return; }
|
||||||
|
|
||||||
const wxString path = dialog->GetPath();
|
|
||||||
const std::string path_u8 = into_u8(path);
|
const std::string path_u8 = into_u8(path);
|
||||||
|
|
||||||
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
|
|
||||||
wxBusyCursor wait;
|
wxBusyCursor wait;
|
||||||
if (Slic3r::store_amf(path_u8.c_str(), &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) {
|
bool export_config = true;
|
||||||
|
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
|
||||||
|
if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) {
|
||||||
// Success
|
// Success
|
||||||
p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path));
|
p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path));
|
||||||
} else {
|
} else {
|
||||||
@ -3297,10 +3431,8 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
|
|||||||
bool export_config = true;
|
bool export_config = true;
|
||||||
if (output_path.empty())
|
if (output_path.empty())
|
||||||
{
|
{
|
||||||
auto dialog = p->get_export_file(FT_3MF);
|
path = p->get_export_file(FT_3MF);
|
||||||
if (!dialog) { return; }
|
if (path.empty()) { return; }
|
||||||
path = dialog->GetPath();
|
|
||||||
export_config = dialog->get_checkbox_value();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
path = from_path(output_path);
|
path = from_path(output_path);
|
||||||
|
@ -133,10 +133,12 @@ private:
|
|||||||
const Transform3d& get_instance_full_matrix() const { return m_instance.full_matrix; }
|
const Transform3d& get_instance_full_matrix() const { return m_instance.full_matrix; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
typedef std::map<unsigned int, VolumeCache> VolumesCache;
|
typedef std::map<unsigned int, VolumeCache> VolumesCache;
|
||||||
typedef std::set<int> InstanceIdxsList;
|
typedef std::set<int> InstanceIdxsList;
|
||||||
typedef std::map<int, InstanceIdxsList> ObjectIdxsToInstanceIdxsMap;
|
typedef std::map<int, InstanceIdxsList> ObjectIdxsToInstanceIdxsMap;
|
||||||
|
|
||||||
|
private:
|
||||||
struct Cache
|
struct Cache
|
||||||
{
|
{
|
||||||
// Cache of GLVolume derived transformation matrices, valid during mouse dragging.
|
// Cache of GLVolume derived transformation matrices, valid during mouse dragging.
|
||||||
|
@ -1446,7 +1446,7 @@ void TabFilament::build()
|
|||||||
line.append_option(optgroup->get_option("bed_temperature"));
|
line.append_option(optgroup->get_option("bed_temperature"));
|
||||||
optgroup->append_line(line);
|
optgroup->append_line(line);
|
||||||
|
|
||||||
page = add_options_page(_(L("Cooling")), "hourglass.png");
|
page = add_options_page(_(L("Cooling")), "cooling");
|
||||||
optgroup = page->new_optgroup(_(L("Enable")));
|
optgroup = page->new_optgroup(_(L("Enable")));
|
||||||
optgroup->append_single_option_line("fan_always_on");
|
optgroup->append_single_option_line("fan_always_on");
|
||||||
optgroup->append_single_option_line("cooling");
|
optgroup->append_single_option_line("cooling");
|
||||||
@ -1638,7 +1638,8 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto printhost_browse = [=](wxWindow* parent) {
|
auto printhost_browse = [=](wxWindow* parent) {
|
||||||
auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse ")) + dots,
|
||||||
|
wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
|
||||||
btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
||||||
btn->SetBitmap(create_scaled_bitmap("zoom.png"));
|
btn->SetBitmap(create_scaled_bitmap("zoom.png"));
|
||||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
#include <wx/dcclient.h>
|
#include <wx/dcclient.h>
|
||||||
#include <wx/numformatter.h>
|
#include <wx/numformatter.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
|
||||||
#include "BitmapCache.hpp"
|
#include "BitmapCache.hpp"
|
||||||
#include "GUI.hpp"
|
#include "GUI.hpp"
|
||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
@ -421,19 +423,21 @@ void PrusaCollapsiblePaneMSW::Collapse(bool collapse)
|
|||||||
// PrusaObjectDataViewModelNode
|
// PrusaObjectDataViewModelNode
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
wxBitmap create_scaled_bitmap(const std::string& bmp_name)
|
// If an icon has horizontal orientation (width > height) call this function with is_horizontal = true
|
||||||
|
wxBitmap create_scaled_bitmap(const std::string& bmp_name_in, const bool is_horizontal /* = false*/)
|
||||||
{
|
{
|
||||||
const double scale_f = Slic3r::GUI::wxGetApp().em_unit()* 0.1;//GetContentScaleFactor();
|
static Slic3r::GUI::BitmapCache cache;
|
||||||
if (scale_f == 1.0)
|
|
||||||
return wxBitmap(Slic3r::GUI::from_u8(Slic3r::var(bmp_name)), wxBITMAP_TYPE_PNG);
|
|
||||||
// else if (scale_f == 2.0) // use biger icon
|
|
||||||
// return wxBitmap(Slic3r::GUI::from_u8(Slic3r::var(bmp_name_X2)), wxBITMAP_TYPE_PNG);
|
|
||||||
|
|
||||||
wxImage img = wxImage(Slic3r::GUI::from_u8(Slic3r::var(bmp_name)), wxBITMAP_TYPE_PNG);
|
unsigned int height, width = height = 0;
|
||||||
const int sz_w = int(img.GetWidth()*scale_f);
|
unsigned int& scale_base = is_horizontal ? width : height;
|
||||||
const int sz_h = int(img.GetHeight()*scale_f);
|
scale_base = (unsigned int)(Slic3r::GUI::wxGetApp().em_unit() * 1.6f + 0.5f);
|
||||||
img.Rescale(sz_w, sz_h, wxIMAGE_QUALITY_BILINEAR);
|
|
||||||
return wxBitmap(img);
|
std::string bmp_name = bmp_name_in;
|
||||||
|
boost::replace_last(bmp_name, ".png", "");
|
||||||
|
wxBitmap *bmp = cache.load_svg(bmp_name, height, width);
|
||||||
|
if (bmp == nullptr)
|
||||||
|
bmp = cache.load_png(bmp_name, height, width);
|
||||||
|
return *bmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrusaObjectDataViewModelNode::set_object_action_icon() {
|
void PrusaObjectDataViewModelNode::set_object_action_icon() {
|
||||||
@ -1239,6 +1243,32 @@ unsigned int PrusaObjectDataViewModel::GetChildren(const wxDataViewItem &parent,
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PrusaObjectDataViewModel::GetAllChildren(const wxDataViewItem &parent, wxDataViewItemArray &array) const
|
||||||
|
{
|
||||||
|
PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)parent.GetID();
|
||||||
|
if (!node) {
|
||||||
|
for (auto object : m_objects)
|
||||||
|
array.Add(wxDataViewItem((void*)object));
|
||||||
|
}
|
||||||
|
else if (node->GetChildCount() == 0)
|
||||||
|
return;
|
||||||
|
else {
|
||||||
|
const size_t count = node->GetChildren().GetCount();
|
||||||
|
for (size_t pos = 0; pos < count; pos++) {
|
||||||
|
PrusaObjectDataViewModelNode *child = node->GetChildren().Item(pos);
|
||||||
|
array.Add(wxDataViewItem((void*)child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wxDataViewItemArray new_array = array;
|
||||||
|
for (const auto item : new_array)
|
||||||
|
{
|
||||||
|
wxDataViewItemArray children;
|
||||||
|
GetAllChildren(item, children);
|
||||||
|
WX_APPEND_ARRAY(array, children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ItemType PrusaObjectDataViewModel::GetItemType(const wxDataViewItem &item) const
|
ItemType PrusaObjectDataViewModel::GetItemType(const wxDataViewItem &item) const
|
||||||
{
|
{
|
||||||
if (!item.IsOk())
|
if (!item.IsOk())
|
||||||
@ -1456,8 +1486,8 @@ PrusaDoubleSlider::PrusaDoubleSlider(wxWindow *parent,
|
|||||||
if (!is_osx)
|
if (!is_osx)
|
||||||
SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX
|
SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX
|
||||||
|
|
||||||
m_bmp_thumb_higher = wxBitmap(create_scaled_bitmap(style == wxSL_HORIZONTAL ? "right_half_circle.png" : "up_half_circle.png"));
|
m_bmp_thumb_higher = wxBitmap(style == wxSL_HORIZONTAL ? create_scaled_bitmap("right_half_circle.png") : create_scaled_bitmap("up_half_circle.png", true));
|
||||||
m_bmp_thumb_lower = wxBitmap(create_scaled_bitmap(style == wxSL_HORIZONTAL ? "left_half_circle.png" : "down_half_circle.png"));
|
m_bmp_thumb_lower = wxBitmap(style == wxSL_HORIZONTAL ? create_scaled_bitmap("left_half_circle.png" ) : create_scaled_bitmap("down_half_circle.png", true));
|
||||||
m_thumb_size = m_bmp_thumb_lower.GetSize();
|
m_thumb_size = m_bmp_thumb_lower.GetSize();
|
||||||
|
|
||||||
m_bmp_add_tick_on = create_scaled_bitmap("colorchange_add_on.png");
|
m_bmp_add_tick_on = create_scaled_bitmap("colorchange_add_on.png");
|
||||||
|
@ -31,7 +31,7 @@ wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxStrin
|
|||||||
wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
|
wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
|
||||||
std::function<void(wxCommandEvent& event)> cb, wxEvtHandler* event_handler);
|
std::function<void(wxCommandEvent& event)> cb, wxEvtHandler* event_handler);
|
||||||
|
|
||||||
wxBitmap create_scaled_bitmap(const std::string& bmp_name);
|
wxBitmap create_scaled_bitmap(const std::string& bmp_name, const bool is_horizontal = false);
|
||||||
|
|
||||||
class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup
|
class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup
|
||||||
{
|
{
|
||||||
@ -511,7 +511,7 @@ public:
|
|||||||
virtual bool IsContainer(const wxDataViewItem &item) const override;
|
virtual bool IsContainer(const wxDataViewItem &item) const override;
|
||||||
virtual unsigned int GetChildren(const wxDataViewItem &parent,
|
virtual unsigned int GetChildren(const wxDataViewItem &parent,
|
||||||
wxDataViewItemArray &array) const override;
|
wxDataViewItemArray &array) const override;
|
||||||
|
void GetAllChildren(const wxDataViewItem &parent,wxDataViewItemArray &array) const;
|
||||||
// Is the container just a header or an item with all columns
|
// Is the container just a header or an item with all columns
|
||||||
// In our case it is an item with all columns
|
// In our case it is an item with all columns
|
||||||
virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
|
virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
|
||||||
|
@ -212,7 +212,12 @@ int wmain(int argc, wchar_t **argv)
|
|||||||
argv_extended.emplace_back(nullptr);
|
argv_extended.emplace_back(nullptr);
|
||||||
|
|
||||||
OpenGLVersionCheck opengl_version_check;
|
OpenGLVersionCheck opengl_version_check;
|
||||||
bool load_mesa = ! opengl_version_check.load_opengl_dll() || ! opengl_version_check.is_version_greater_or_equal_to(2, 0);
|
bool load_mesa =
|
||||||
|
// Running over a rempote desktop, and the RemoteFX is not enabled, therefore Windows will only provide SW OpenGL 1.1 context.
|
||||||
|
// In that case, use Mesa.
|
||||||
|
::GetSystemMetrics(SM_REMOTESESSION) ||
|
||||||
|
// Try to load the default OpenGL driver and test its context version.
|
||||||
|
! opengl_version_check.load_opengl_dll() || ! opengl_version_check.is_version_greater_or_equal_to(2, 0);
|
||||||
|
|
||||||
wchar_t path_to_exe[MAX_PATH + 1] = { 0 };
|
wchar_t path_to_exe[MAX_PATH + 1] = { 0 };
|
||||||
::GetModuleFileNameW(nullptr, path_to_exe, MAX_PATH);
|
::GetModuleFileNameW(nullptr, path_to_exe, MAX_PATH);
|
||||||
|
Loading…
Reference in New Issue
Block a user