mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-08-04 04:24:04 -06:00
Merge remote-tracking branch 'origin/master' into ys_msw_dpi
This commit is contained in:
commit
df7ada0199
104 changed files with 2162 additions and 410 deletions
|
@ -43,11 +43,21 @@ typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> stl_normal;
|
|||
static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect");
|
||||
static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect");
|
||||
|
||||
typedef struct {
|
||||
struct stl_facet {
|
||||
stl_normal normal;
|
||||
stl_vertex vertex[3];
|
||||
char extra[2];
|
||||
} stl_facet;
|
||||
|
||||
stl_facet rotated(const Eigen::Quaternion<float, Eigen::DontAlign> &rot) {
|
||||
stl_facet out;
|
||||
out.normal = rot * this->normal;
|
||||
out.vertex[0] = rot * this->vertex[0];
|
||||
out.vertex[1] = rot * this->vertex[1];
|
||||
out.vertex[2] = rot * this->vertex[2];
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
#define SIZEOF_STL_FACET 50
|
||||
|
||||
static_assert(offsetof(stl_facet, normal) == 0, "stl_facet.normal has correct offset");
|
||||
|
|
|
@ -41,10 +41,12 @@ stl_open(stl_file *stl, const char *file) {
|
|||
stl_count_facets(stl, file);
|
||||
stl_allocate(stl);
|
||||
stl_read(stl, 0, true);
|
||||
if (!stl->error) fclose(stl->fp);
|
||||
if (stl->fp != nullptr) {
|
||||
fclose(stl->fp);
|
||||
stl->fp = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
stl_initialize(stl_file *stl) {
|
||||
memset(stl, 0, sizeof(stl_file));
|
||||
|
@ -118,7 +120,7 @@ stl_count_facets(stl_file *stl, const char *file) {
|
|||
}
|
||||
|
||||
/* Read the int following the header. This should contain # of facets */
|
||||
bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, stl->fp);
|
||||
bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, stl->fp) != 0;
|
||||
#ifndef BOOST_LITTLE_ENDIAN
|
||||
// Convert from little endian to big endian.
|
||||
stl_internal_reverse_quads((char*)&header_num_facets, 4);
|
||||
|
@ -257,7 +259,6 @@ stl_reallocate(stl_file *stl) {
|
|||
time running this for the stl and therefore we should reset our max and min stats. */
|
||||
void stl_read(stl_file *stl, int first_facet, bool first) {
|
||||
stl_facet facet;
|
||||
int i;
|
||||
|
||||
if (stl->error) return;
|
||||
|
||||
|
@ -268,7 +269,7 @@ void stl_read(stl_file *stl, int first_facet, bool first) {
|
|||
}
|
||||
|
||||
char normal_buf[3][32];
|
||||
for(i = first_facet; i < stl->stats.number_of_facets; i++) {
|
||||
for(uint32_t i = first_facet; i < stl->stats.number_of_facets; i++) {
|
||||
if(stl->stats.type == binary)
|
||||
/* Read a single facet from a binary .STL file */
|
||||
{
|
||||
|
@ -366,17 +367,19 @@ void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
stl_close(stl_file *stl) {
|
||||
if (stl->error) return;
|
||||
void stl_close(stl_file *stl)
|
||||
{
|
||||
assert(stl->fp == nullptr);
|
||||
assert(stl->heads == nullptr);
|
||||
assert(stl->tail == nullptr);
|
||||
|
||||
if(stl->neighbors_start != NULL)
|
||||
free(stl->neighbors_start);
|
||||
if(stl->facet_start != NULL)
|
||||
free(stl->facet_start);
|
||||
if(stl->v_indices != NULL)
|
||||
free(stl->v_indices);
|
||||
if(stl->v_shared != NULL)
|
||||
free(stl->v_shared);
|
||||
if (stl->facet_start != NULL)
|
||||
free(stl->facet_start);
|
||||
if (stl->neighbors_start != NULL)
|
||||
free(stl->neighbors_start);
|
||||
if (stl->v_indices != NULL)
|
||||
free(stl->v_indices);
|
||||
if (stl->v_shared != NULL)
|
||||
free(stl->v_shared);
|
||||
memset(stl, 0, sizeof(stl_file));
|
||||
}
|
||||
|
||||
|
|
|
@ -189,7 +189,6 @@ target_link_libraries(libslic3r
|
|||
clipper
|
||||
nowide
|
||||
${EXPAT_LIBRARIES}
|
||||
${GLEW_LIBRARIES}
|
||||
glu-libtess
|
||||
polypartition
|
||||
poly2tri
|
||||
|
|
|
@ -593,6 +593,8 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
|
|||
this->origin_translation = rhs.origin_translation;
|
||||
m_bounding_box = rhs.m_bounding_box;
|
||||
m_bounding_box_valid = rhs.m_bounding_box_valid;
|
||||
m_raw_bounding_box = rhs.m_raw_bounding_box;
|
||||
m_raw_bounding_box_valid = rhs.m_raw_bounding_box_valid;
|
||||
m_raw_mesh_bounding_box = rhs.m_raw_mesh_bounding_box;
|
||||
m_raw_mesh_bounding_box_valid = rhs.m_raw_mesh_bounding_box_valid;
|
||||
|
||||
|
@ -627,6 +629,8 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
|
|||
this->origin_translation = std::move(rhs.origin_translation);
|
||||
m_bounding_box = std::move(rhs.m_bounding_box);
|
||||
m_bounding_box_valid = std::move(rhs.m_bounding_box_valid);
|
||||
m_raw_bounding_box = rhs.m_raw_bounding_box;
|
||||
m_raw_bounding_box_valid = rhs.m_raw_bounding_box_valid;
|
||||
m_raw_mesh_bounding_box = rhs.m_raw_mesh_bounding_box;
|
||||
m_raw_mesh_bounding_box_valid = rhs.m_raw_mesh_bounding_box_valid;
|
||||
|
||||
|
@ -859,7 +863,7 @@ TriangleMesh ModelObject::full_raw_mesh() const
|
|||
return mesh;
|
||||
}
|
||||
|
||||
BoundingBoxf3 ModelObject::raw_mesh_bounding_box() const
|
||||
const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const
|
||||
{
|
||||
if (! m_raw_mesh_bounding_box_valid) {
|
||||
m_raw_mesh_bounding_box_valid = true;
|
||||
|
@ -880,33 +884,36 @@ BoundingBoxf3 ModelObject::full_raw_mesh_bounding_box() const
|
|||
}
|
||||
|
||||
// A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
|
||||
// This bounding box is only used for the actual slicing.
|
||||
BoundingBoxf3 ModelObject::raw_bounding_box() const
|
||||
// This bounding box is only used for the actual slicing and for layer editing UI to calculate the layers.
|
||||
const BoundingBoxf3& ModelObject::raw_bounding_box() const
|
||||
{
|
||||
BoundingBoxf3 bb;
|
||||
#if ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
if (this->instances.empty())
|
||||
throw std::invalid_argument("Can't call raw_bounding_box() with no instances");
|
||||
if (! m_raw_bounding_box_valid) {
|
||||
m_raw_bounding_box_valid = true;
|
||||
m_raw_bounding_box.reset();
|
||||
#if ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
if (this->instances.empty())
|
||||
throw std::invalid_argument("Can't call raw_bounding_box() with no instances");
|
||||
|
||||
const Transform3d& inst_matrix = this->instances.front()->get_transformation().get_matrix(true);
|
||||
#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part()) {
|
||||
#if !ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
if (this->instances.empty())
|
||||
throw std::invalid_argument("Can't call raw_bounding_box() with no instances");
|
||||
#endif // !ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
const Transform3d& inst_matrix = this->instances.front()->get_transformation().get_matrix(true);
|
||||
#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part()) {
|
||||
#if !ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
if (this->instances.empty())
|
||||
throw std::invalid_argument("Can't call raw_bounding_box() with no instances");
|
||||
#endif // !ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
|
||||
TriangleMesh vol_mesh(v->mesh);
|
||||
#if ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
vol_mesh.transform(inst_matrix * v->get_matrix());
|
||||
bb.merge(vol_mesh.bounding_box());
|
||||
#else
|
||||
vol_mesh.transform(v->get_matrix());
|
||||
bb.merge(this->instances.front()->transform_mesh_bounding_box(vol_mesh, true));
|
||||
#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
}
|
||||
return bb;
|
||||
#if ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
m_raw_bounding_box.merge(v->mesh.transformed_bounding_box(inst_matrix * v->get_matrix()));
|
||||
#else
|
||||
// unmaintaned
|
||||
assert(false);
|
||||
// vol_mesh.transform(v->get_matrix());
|
||||
// m_raw_bounding_box_valid.merge(this->instances.front()->transform_mesh_bounding_box(vol_mesh, true));
|
||||
#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
}
|
||||
}
|
||||
return m_raw_bounding_box;
|
||||
}
|
||||
|
||||
// This returns an accurate snug bounding box of the transformed object instance, without the translation applied.
|
||||
|
@ -920,13 +927,13 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
|
|||
{
|
||||
if (v->is_model_part())
|
||||
{
|
||||
TriangleMesh mesh(v->mesh);
|
||||
#if ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
mesh.transform(inst_matrix * v->get_matrix());
|
||||
bb.merge(mesh.bounding_box());
|
||||
bb.merge(v->mesh.transformed_bounding_box(inst_matrix * v->get_matrix()));
|
||||
#else
|
||||
mesh.transform(v->get_matrix());
|
||||
bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(mesh, dont_translate));
|
||||
// not maintained
|
||||
assert(false);
|
||||
//mesh.transform(v->get_matrix());
|
||||
//bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(mesh, dont_translate));
|
||||
#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
}
|
||||
}
|
||||
|
|
|
@ -212,7 +212,7 @@ public:
|
|||
// This bounding box is approximate and not snug.
|
||||
// This bounding box is being cached.
|
||||
const BoundingBoxf3& bounding_box() const;
|
||||
void invalidate_bounding_box() { m_bounding_box_valid = false; m_raw_mesh_bounding_box_valid = false; }
|
||||
void invalidate_bounding_box() { m_bounding_box_valid = false; m_raw_bounding_box_valid = false; m_raw_mesh_bounding_box_valid = false; }
|
||||
|
||||
// A mesh containing all transformed instances of this object.
|
||||
TriangleMesh mesh() const;
|
||||
|
@ -223,11 +223,11 @@ public:
|
|||
TriangleMesh full_raw_mesh() const;
|
||||
// A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
|
||||
// This bounding box is only used for the actual slicing.
|
||||
BoundingBoxf3 raw_bounding_box() const;
|
||||
const BoundingBoxf3& raw_bounding_box() const;
|
||||
// A snug bounding box around the transformed non-modifier object volumes.
|
||||
BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const;
|
||||
// A snug bounding box of non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
|
||||
BoundingBoxf3 raw_mesh_bounding_box() const;
|
||||
const BoundingBoxf3& raw_mesh_bounding_box() const;
|
||||
// A snug bounding box of non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
|
||||
BoundingBoxf3 full_raw_mesh_bounding_box() const;
|
||||
|
||||
|
@ -285,7 +285,7 @@ protected:
|
|||
|
||||
private:
|
||||
ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()),
|
||||
m_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {}
|
||||
m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {}
|
||||
~ModelObject();
|
||||
|
||||
/* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
|
||||
|
@ -304,6 +304,8 @@ private:
|
|||
// Bounding box, cached.
|
||||
mutable BoundingBoxf3 m_bounding_box;
|
||||
mutable bool m_bounding_box_valid;
|
||||
mutable BoundingBoxf3 m_raw_bounding_box;
|
||||
mutable bool m_raw_bounding_box_valid;
|
||||
mutable BoundingBoxf3 m_raw_mesh_bounding_box;
|
||||
mutable bool m_raw_mesh_bounding_box_valid;
|
||||
};
|
||||
|
|
|
@ -132,7 +132,7 @@ public:
|
|||
// The slicing parameters are dependent on various configuration values
|
||||
// (layer height, first layer height, raft settings, print nozzle diameter etc).
|
||||
const SlicingParameters& slicing_parameters() const { return m_slicing_params; }
|
||||
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object);
|
||||
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z);
|
||||
|
||||
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
|
||||
std::vector<unsigned int> object_extruders() const;
|
||||
|
|
|
@ -2528,14 +2528,17 @@ void PrintConfigDef::init_sla_params()
|
|||
|
||||
def = this->add("pad_wall_height", coFloat);
|
||||
def->label = L("Pad wall height");
|
||||
def->tooltip = L("Defines the cavity depth. Set to zero to disable the cavity.");
|
||||
def->tooltip = L("Defines the pad cavity depth. Set to zero to disable the cavity. "
|
||||
"Be careful when enabling this feature, as some resins may "
|
||||
"produce an extreme suction effect inside the cavity, "
|
||||
"which makes pealing the print off the vat foil difficult.");
|
||||
def->category = L("Pad");
|
||||
// def->tooltip = L("");
|
||||
def->sidetext = L("mm");
|
||||
def->min = 0;
|
||||
def->max = 30;
|
||||
def->mode = comSimple;
|
||||
def->default_value = new ConfigOptionFloat(5.0);
|
||||
def->mode = comExpert;
|
||||
def->default_value = new ConfigOptionFloat(0.);
|
||||
|
||||
def = this->add("pad_max_merge_distance", coFloat);
|
||||
def->label = L("Max merge distance");
|
||||
|
@ -3114,6 +3117,13 @@ CLIMiscConfigDef::CLIMiscConfigDef()
|
|||
def->label = L("Logging level");
|
||||
def->tooltip = L("Messages with severity lower or eqal to the loglevel will be printed out. 0:trace, 1:debug, 2:info, 3:warning, 4:error, 5:fatal");
|
||||
def->min = 0;
|
||||
|
||||
#if defined(_MSC_VER) && defined(SLIC3R_GUI)
|
||||
def = this->add("sw_renderer", coBool);
|
||||
def->label = L("Render with a software renderer");
|
||||
def->tooltip = L("Render with a software renderer. The bundled MESA software renderer is loaded instead of the default OpenGL driver.");
|
||||
def->min = 0;
|
||||
#endif /* _MSC_VER */
|
||||
}
|
||||
|
||||
const CLIActionsConfigDef cli_actions_config_def;
|
||||
|
|
|
@ -1370,7 +1370,7 @@ void PrintObject::update_slicing_parameters()
|
|||
this->print()->config(), m_config, unscale<double>(this->size(2)), this->object_extruders());
|
||||
}
|
||||
|
||||
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object)
|
||||
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z)
|
||||
{
|
||||
PrintConfig print_config;
|
||||
PrintObjectConfig object_config;
|
||||
|
@ -1390,7 +1390,9 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full
|
|||
object_extruders);
|
||||
sort_remove_duplicates(object_extruders);
|
||||
|
||||
return SlicingParameters::create_from_config(print_config, object_config, model_object.bounding_box().max.z(), object_extruders);
|
||||
if (object_max_z <= 0.f)
|
||||
object_max_z = model_object.raw_bounding_box().size().z();
|
||||
return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
|
||||
}
|
||||
|
||||
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
|
||||
|
|
|
@ -1572,10 +1572,8 @@ public:
|
|||
auto hit = bridge_mesh_intersect(headjp, n, r);
|
||||
|
||||
if(std::isinf(hit.distance())) ground_head_indices.emplace_back(i);
|
||||
else {
|
||||
if(m_cfg.ground_facing_only) head.invalidate();
|
||||
m_iheads_onmodel.emplace_back(std::make_pair(i, hit));
|
||||
}
|
||||
else if(m_cfg.ground_facing_only) head.invalidate();
|
||||
else m_iheads_onmodel.emplace_back(std::make_pair(i, hit));
|
||||
}
|
||||
|
||||
// We want to search for clusters of points that are far enough
|
||||
|
@ -1872,7 +1870,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void cascade_pillars() {
|
||||
void interconnect_pillars() {
|
||||
// Now comes the algorithm that connects pillars with each other.
|
||||
// Ideally every pillar should be connected with at least one of its
|
||||
// neighbors if that neighbor is within max_pillar_link_distance
|
||||
|
@ -2121,7 +2119,7 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
|
|||
|
||||
std::bind(&Algorithm::routing_to_model, &alg),
|
||||
|
||||
std::bind(&Algorithm::cascade_pillars, &alg),
|
||||
std::bind(&Algorithm::interconnect_pillars, &alg),
|
||||
|
||||
std::bind(&Algorithm::routing_headless, &alg),
|
||||
|
||||
|
@ -2150,16 +2148,16 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
|
|||
// Let's define a simple automaton that will run our program.
|
||||
auto progress = [&ctl, &pc] () {
|
||||
static const std::array<std::string, NUM_STEPS> stepstr {
|
||||
L("Starting"),
|
||||
L("Filtering"),
|
||||
L("Generate pinheads"),
|
||||
L("Classification"),
|
||||
L("Routing to ground"),
|
||||
L("Routing supports to model surface"),
|
||||
L("Cascading pillars"),
|
||||
L("Processing small holes"),
|
||||
L("Done"),
|
||||
L("Abort")
|
||||
"Starting",
|
||||
"Filtering",
|
||||
"Generate pinheads",
|
||||
"Classification",
|
||||
"Routing to ground",
|
||||
"Routing supports to model surface",
|
||||
"Interconnecting pillars",
|
||||
"Processing small holes",
|
||||
"Done",
|
||||
"Abort"
|
||||
};
|
||||
|
||||
static const std::array<unsigned, NUM_STEPS> stepstate {
|
||||
|
|
|
@ -693,6 +693,16 @@ void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TriangleMeshSlicer::set_up_direction(const Vec3f& up)
|
||||
{
|
||||
m_quaternion.setFromTwoVectors(up, Vec3f::UnitZ());
|
||||
m_use_quaternion = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice";
|
||||
|
@ -795,7 +805,7 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
|
|||
void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex,
|
||||
const std::vector<float> &z) const
|
||||
{
|
||||
const stl_facet &facet = this->mesh->stl.facet_start[facet_idx];
|
||||
const stl_facet &facet = m_use_quaternion ? this->mesh->stl.facet_start[facet_idx].rotated(m_quaternion) : this->mesh->stl.facet_start[facet_idx];
|
||||
|
||||
// find facet extents
|
||||
const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2)));
|
||||
|
@ -860,26 +870,43 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
|
|||
IntersectionPoint points[3];
|
||||
size_t num_points = 0;
|
||||
size_t point_on_layer = size_t(-1);
|
||||
|
||||
|
||||
// Reorder vertices so that the first one is the one with lowest Z.
|
||||
// This is needed to get all intersection lines in a consistent order
|
||||
// (external on the right of the line)
|
||||
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
|
||||
int i = (facet.vertex[1].z() == min_z) ? 1 : ((facet.vertex[2].z() == min_z) ? 2 : 0);
|
||||
|
||||
// These are used only if the cut plane is tilted:
|
||||
stl_vertex rotated_a;
|
||||
stl_vertex rotated_b;
|
||||
|
||||
for (int j = i; j - i < 3; ++j) { // loop through facet edges
|
||||
int edge_id = this->facets_edges[facet_idx * 3 + (j % 3)];
|
||||
int a_id = vertices[j % 3];
|
||||
int b_id = vertices[(j+1) % 3];
|
||||
const stl_vertex *a = &this->v_scaled_shared[a_id];
|
||||
const stl_vertex *b = &this->v_scaled_shared[b_id];
|
||||
|
||||
const stl_vertex *a;
|
||||
const stl_vertex *b;
|
||||
if (m_use_quaternion) {
|
||||
rotated_a = m_quaternion * this->v_scaled_shared[a_id];
|
||||
rotated_b = m_quaternion * this->v_scaled_shared[b_id];
|
||||
a = &rotated_a;
|
||||
b = &rotated_b;
|
||||
}
|
||||
else {
|
||||
a = &this->v_scaled_shared[a_id];
|
||||
b = &this->v_scaled_shared[b_id];
|
||||
}
|
||||
|
||||
// Is edge or face aligned with the cutting plane?
|
||||
if (a->z() == slice_z && b->z() == slice_z) {
|
||||
// Edge is horizontal and belongs to the current layer.
|
||||
const stl_vertex &v0 = this->v_scaled_shared[vertices[0]];
|
||||
const stl_vertex &v1 = this->v_scaled_shared[vertices[1]];
|
||||
const stl_vertex &v2 = this->v_scaled_shared[vertices[2]];
|
||||
const stl_normal &normal = this->mesh->stl.facet_start[facet_idx].normal;
|
||||
// The following rotation of the three vertices may not be efficient, but this branch happens rarely.
|
||||
const stl_vertex &v0 = m_use_quaternion ? stl_vertex(m_quaternion * this->v_scaled_shared[vertices[0]]) : this->v_scaled_shared[vertices[0]];
|
||||
const stl_vertex &v1 = m_use_quaternion ? stl_vertex(m_quaternion * this->v_scaled_shared[vertices[1]]) : this->v_scaled_shared[vertices[1]];
|
||||
const stl_vertex &v2 = m_use_quaternion ? stl_vertex(m_quaternion * this->v_scaled_shared[vertices[2]]) : this->v_scaled_shared[vertices[2]];
|
||||
const stl_normal &normal = facet.normal;
|
||||
// We may ignore this edge for slicing purposes, but we may still use it for object cutting.
|
||||
FacetSliceType result = Slicing;
|
||||
if (min_z == max_z) {
|
||||
|
@ -995,7 +1022,9 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
|
|||
if (i == line_out->a_id || i == line_out->b_id)
|
||||
i = vertices[2];
|
||||
assert(i != line_out->a_id && i != line_out->b_id);
|
||||
line_out->edge_type = (this->v_scaled_shared[i].z() < slice_z) ? feTop : feBottom;
|
||||
line_out->edge_type = ((m_use_quaternion ?
|
||||
(m_quaternion * this->v_scaled_shared[i]).z()
|
||||
: this->v_scaled_shared[i].z()) < slice_z) ? feTop : feBottom;
|
||||
}
|
||||
#endif
|
||||
return Slicing;
|
||||
|
|
|
@ -25,9 +25,10 @@ public:
|
|||
TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd> &facets);
|
||||
TriangleMesh(const TriangleMesh &other) : repaired(false) { stl_initialize(&this->stl); *this = other; }
|
||||
TriangleMesh(TriangleMesh &&other) : repaired(false) { stl_initialize(&this->stl); this->swap(other); }
|
||||
~TriangleMesh() { stl_close(&this->stl); }
|
||||
~TriangleMesh() { clear(); }
|
||||
TriangleMesh& operator=(const TriangleMesh &other);
|
||||
TriangleMesh& operator=(TriangleMesh &&other) { this->swap(other); return *this; }
|
||||
void clear() { stl_close(&this->stl); this->repaired = false; }
|
||||
void swap(TriangleMesh &other) { std::swap(this->stl, other.stl); std::swap(this->repaired, other.repaired); }
|
||||
void ReadSTLFile(const char* input_file) { stl_open(&stl, input_file); }
|
||||
void write_ascii(const char* output_file) { stl_write_ascii(&this->stl, output_file, ""); }
|
||||
|
@ -171,6 +172,7 @@ public:
|
|||
FacetSliceType slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
|
||||
const float min_z, const float max_z, IntersectionLine *line_out) const;
|
||||
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
|
||||
void set_up_direction(const Vec3f& up);
|
||||
|
||||
private:
|
||||
const TriangleMesh *mesh;
|
||||
|
@ -178,6 +180,10 @@ private:
|
|||
std::vector<int> facets_edges;
|
||||
// Scaled copy of this->mesh->stl.v_shared
|
||||
std::vector<stl_vertex> v_scaled_shared;
|
||||
// Quaternion that will be used to rotate every facet before the slicing
|
||||
Eigen::Quaternion<float, Eigen::DontAlign> m_quaternion;
|
||||
// Whether or not the above quaterion should be used
|
||||
bool m_use_quaternion = false;
|
||||
|
||||
void _slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex, const std::vector<float> &z) const;
|
||||
void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const;
|
||||
|
|
|
@ -6,10 +6,12 @@
|
|||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
#include <wchar.h>
|
||||
// Let the NVIDIA and AMD know we want to use their graphics card
|
||||
// on a dual graphics card system.
|
||||
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
|
||||
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
#ifdef SLIC3R_GUI
|
||||
// Let the NVIDIA and AMD know we want to use their graphics card
|
||||
// on a dual graphics card system.
|
||||
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
|
||||
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
#endif /* SLIC3R_GUI */
|
||||
#endif /* WIN32 */
|
||||
|
||||
#include <cstdio>
|
||||
|
@ -38,8 +40,11 @@
|
|||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
#include "slic3r.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#endif /* SLIC3R_GUI */
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
|
@ -448,7 +453,7 @@ int CLI::run(int argc, char **argv)
|
|||
}
|
||||
|
||||
if (start_gui) {
|
||||
#if 1
|
||||
#ifdef SLIC3R_GUI
|
||||
// #ifdef USE_WX
|
||||
GUI::GUI_App *gui = new GUI::GUI_App();
|
||||
// gui->autosave = m_config.opt_string("autosave");
|
||||
|
@ -477,12 +482,12 @@ int CLI::run(int argc, char **argv)
|
|||
gui->mainframe->load_config(m_extra_config);
|
||||
});
|
||||
return wxEntry(argc, argv);
|
||||
#else
|
||||
#else /* SLIC3R_GUI */
|
||||
// No GUI support. Just print out a help.
|
||||
this->print_help(false);
|
||||
// If started without a parameter, consider it to be OK, otherwise report an error code (no action etc).
|
||||
return (argc == 0) ? 0 : 1;
|
||||
#endif
|
||||
#endif /* SLIC3R_GUI */
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -563,7 +568,13 @@ bool CLI::setup(int argc, char **argv)
|
|||
void CLI::print_help(bool include_print_options, PrinterTechnology printer_technology) const
|
||||
{
|
||||
boost::nowide::cout
|
||||
<< "Slic3r Prusa Edition " << SLIC3R_BUILD << std::endl
|
||||
<< "Slic3r Prusa Edition " << SLIC3R_BUILD
|
||||
#ifdef SLIC3R_GUI
|
||||
<< " (with GUI support)"
|
||||
#else /* SLIC3R_GUI */
|
||||
<< " (without GUI support)"
|
||||
#endif /* SLIC3R_GUI */
|
||||
<< std::endl
|
||||
<< "https://github.com/prusa3d/Slic3r" << std::endl << std::endl
|
||||
<< "Usage: slic3r [ ACTIONS ] [ TRANSFORM ] [ OPTIONS ] [ file.stl ... ]" << std::endl
|
||||
<< std::endl
|
||||
|
|
|
@ -154,7 +154,7 @@ endif ()
|
|||
|
||||
add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
|
||||
|
||||
target_link_libraries(libslic3r_gui libslic3r avrdude imgui)
|
||||
target_link_libraries(libslic3r_gui libslic3r avrdude imgui ${GLEW_LIBRARIES})
|
||||
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
|
||||
endif ()
|
||||
|
|
|
@ -721,32 +721,37 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
|||
return int(this->volumes.size() - 1);
|
||||
}
|
||||
|
||||
typedef std::pair<GLVolume*, double> GLVolumeWithZ;
|
||||
typedef std::vector<GLVolumeWithZ> GLVolumesWithZList;
|
||||
static GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func)
|
||||
GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func)
|
||||
{
|
||||
GLVolumesWithZList list;
|
||||
GLVolumeWithIdAndZList list;
|
||||
list.reserve(volumes.size());
|
||||
|
||||
for (GLVolume* volume : volumes)
|
||||
for (unsigned int i = 0; i < (unsigned int)volumes.size(); ++i)
|
||||
{
|
||||
GLVolume* volume = volumes[i];
|
||||
bool is_transparent = (volume->render_color[3] < 1.0f);
|
||||
if ((((type == GLVolumeCollection::Opaque) && !is_transparent) ||
|
||||
((type == GLVolumeCollection::Transparent) && is_transparent) ||
|
||||
(type == GLVolumeCollection::All)) &&
|
||||
(! filter_func || filter_func(*volume)))
|
||||
list.emplace_back(std::make_pair(volume, 0.0));
|
||||
list.emplace_back(std::make_pair(volume, std::make_pair(i, 0.0)));
|
||||
}
|
||||
|
||||
if ((type == GLVolumeCollection::Transparent) && (list.size() > 1))
|
||||
{
|
||||
for (GLVolumeWithZ& volume : list)
|
||||
for (GLVolumeWithIdAndZ& volume : list)
|
||||
{
|
||||
volume.second = volume.first->bounding_box.transformed(view_matrix * volume.first->world_matrix()).max(2);
|
||||
volume.second.second = volume.first->bounding_box.transformed(view_matrix * volume.first->world_matrix()).max(2);
|
||||
}
|
||||
|
||||
std::sort(list.begin(), list.end(),
|
||||
[](const GLVolumeWithZ& v1, const GLVolumeWithZ& v2) -> bool { return v1.second < v2.second; }
|
||||
[](const GLVolumeWithIdAndZ& v1, const GLVolumeWithIdAndZ& v2) -> bool { return v1.second.second < v2.second.second; }
|
||||
);
|
||||
}
|
||||
else if ((type == GLVolumeCollection::Opaque) && (list.size() > 1))
|
||||
{
|
||||
std::sort(list.begin(), list.end(),
|
||||
[](const GLVolumeWithIdAndZ& v1, const GLVolumeWithIdAndZ& v2) -> bool { return v1.first->selected && !v2.first->selected; }
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -769,6 +774,7 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool
|
|||
glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id));
|
||||
GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1;
|
||||
GLint z_range_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "z_range") : -1;
|
||||
GLint clipping_plane_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "clipping_plane") : -1;
|
||||
GLint print_box_min_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.min") : -1;
|
||||
GLint print_box_max_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.max") : -1;
|
||||
GLint print_box_detection_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1;
|
||||
|
@ -784,8 +790,11 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool
|
|||
if (z_range_id != -1)
|
||||
glsafe(::glUniform2fv(z_range_id, 1, (const GLfloat*)z_range));
|
||||
|
||||
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
|
||||
for (GLVolumeWithZ& volume : to_render) {
|
||||
if (clipping_plane_id != -1)
|
||||
glsafe(::glUniform4fv(clipping_plane_id, 1, (const GLfloat*)clipping_plane));
|
||||
|
||||
GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
|
||||
for (GLVolumeWithIdAndZ& volume : to_render) {
|
||||
volume.first->set_render_color();
|
||||
volume.first->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id);
|
||||
}
|
||||
|
@ -814,8 +823,8 @@ void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface,
|
|||
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
|
||||
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
|
||||
|
||||
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
|
||||
for (GLVolumeWithZ& volume : to_render)
|
||||
GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
|
||||
for (GLVolumeWithIdAndZ& volume : to_render)
|
||||
{
|
||||
volume.first->set_render_color();
|
||||
volume.first->render_legacy();
|
||||
|
|
|
@ -412,6 +412,8 @@ public:
|
|||
};
|
||||
|
||||
typedef std::vector<GLVolume*> GLVolumePtrs;
|
||||
typedef std::pair<GLVolume*, std::pair<unsigned int, double>> GLVolumeWithIdAndZ;
|
||||
typedef std::vector<GLVolumeWithIdAndZ> GLVolumeWithIdAndZList;
|
||||
|
||||
class GLVolumeCollection
|
||||
{
|
||||
|
@ -431,6 +433,9 @@ private:
|
|||
// z range for clipping in shaders
|
||||
float z_range[2];
|
||||
|
||||
// plane coeffs for clipping in shaders
|
||||
float clipping_plane[4];
|
||||
|
||||
public:
|
||||
GLVolumePtrs volumes;
|
||||
|
||||
|
@ -489,6 +494,7 @@ public:
|
|||
}
|
||||
|
||||
void set_z_range(float min_z, float max_z) { z_range[0] = min_z; z_range[1] = max_z; }
|
||||
void set_clipping_plane(const double* coeffs) { clipping_plane[0] = coeffs[0]; clipping_plane[1] = coeffs[1]; clipping_plane[2] = coeffs[2]; clipping_plane[3] = coeffs[3]; }
|
||||
|
||||
// returns true if all the volumes are completely contained in the print volume
|
||||
// returns the containment state in the given out_state, if non-null
|
||||
|
@ -505,6 +511,8 @@ private:
|
|||
GLVolumeCollection& operator=(const GLVolumeCollection &);
|
||||
};
|
||||
|
||||
GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = nullptr);
|
||||
|
||||
class GLModel
|
||||
{
|
||||
protected:
|
||||
|
|
|
@ -255,16 +255,21 @@ void GLCanvas3D::LayersEditing::set_config(const DynamicPrintConfig* config)
|
|||
void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id)
|
||||
{
|
||||
const ModelObject *model_object_new = (object_id >= 0) ? model.objects[object_id] : nullptr;
|
||||
if (model_object_new == nullptr || this->last_object_id != object_id || m_model_object != model_object_new || m_model_object->id() != model_object_new->id()) {
|
||||
// Maximum height of an object changes when the object gets rotated or scaled.
|
||||
// Changing maximum height of an object will invalidate the layer heigth editing profile.
|
||||
// m_model_object->raw_bounding_box() is cached, therefore it is cheap even if this method is called frequently.
|
||||
float new_max_z = (m_model_object == nullptr) ? 0.f : m_model_object->raw_bounding_box().size().z();
|
||||
if (m_model_object != model_object_new || this->last_object_id != object_id || m_object_max_z != new_max_z ||
|
||||
(model_object_new != nullptr && m_model_object->id() != model_object_new->id())) {
|
||||
m_layer_height_profile.clear();
|
||||
m_layer_height_profile_modified = false;
|
||||
delete m_slicing_parameters;
|
||||
m_slicing_parameters = nullptr;
|
||||
m_slicing_parameters = nullptr;
|
||||
m_layers_texture.valid = false;
|
||||
this->last_object_id = object_id;
|
||||
m_model_object = model_object_new;
|
||||
m_object_max_z = new_max_z;
|
||||
}
|
||||
this->last_object_id = object_id;
|
||||
m_model_object = model_object_new;
|
||||
m_object_max_z = (m_model_object == nullptr) ? 0.f : m_model_object->bounding_box().max.z();
|
||||
}
|
||||
|
||||
bool GLCanvas3D::LayersEditing::is_allowed() const
|
||||
|
@ -623,7 +628,7 @@ void GLCanvas3D::LayersEditing::update_slicing_parameters()
|
|||
{
|
||||
if (m_slicing_parameters == nullptr) {
|
||||
m_slicing_parameters = new SlicingParameters();
|
||||
*m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object);
|
||||
*m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2204,6 +2209,9 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
|||
|
||||
void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||
{
|
||||
if (!m_initialized)
|
||||
return;
|
||||
|
||||
// see include/wx/defs.h enum wxKeyCode
|
||||
int keyCode = evt.GetKeyCode();
|
||||
int ctrlMask = wxMOD_CONTROL;
|
||||
|
@ -2222,9 +2230,12 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
//#endif /* __APPLE__ */
|
||||
if ((evt.GetModifiers() & ctrlMask) != 0) {
|
||||
switch (keyCode) {
|
||||
#ifdef __APPLE__
|
||||
case 'a':
|
||||
case 'A':
|
||||
#else /* __APPLE__ */
|
||||
case WXK_CONTROL_A:
|
||||
#endif /* __APPLE__ */
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_SELECT_ALL));
|
||||
break;
|
||||
#ifdef __APPLE__
|
||||
|
@ -3134,6 +3145,11 @@ Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos)
|
|||
return Linef3(_mouse_to_3d(mouse_pos, &z0), _mouse_to_3d(mouse_pos, &z1));
|
||||
}
|
||||
|
||||
double GLCanvas3D::get_size_proportional_to_max_bed_size(double factor) const
|
||||
{
|
||||
return factor * m_bed.get_bounding_box().max_size();
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_is_shown_on_screen() const
|
||||
{
|
||||
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
|
||||
|
@ -3226,12 +3242,37 @@ bool GLCanvas3D::_init_toolbar()
|
|||
if (!m_toolbar.add_separator())
|
||||
return false;
|
||||
|
||||
item.name = "copy";
|
||||
#if ENABLE_SVG_ICONS
|
||||
item.icon_filename = "copy.svg";
|
||||
#endif // ENABLE_SVG_ICONS
|
||||
item.tooltip = GUI::L_str("Copy") + " [" + GUI::shortkey_ctrl_prefix() + "C]";
|
||||
item.sprite_id = 4;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_copy(); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
item.name = "paste";
|
||||
#if ENABLE_SVG_ICONS
|
||||
item.icon_filename = "paste.svg";
|
||||
#endif // ENABLE_SVG_ICONS
|
||||
item.tooltip = GUI::L_str("Paste") + " [" + GUI::shortkey_ctrl_prefix() + "V]";
|
||||
item.sprite_id = 5;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_paste(); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
if (!m_toolbar.add_separator())
|
||||
return false;
|
||||
|
||||
item.name = "more";
|
||||
#if ENABLE_SVG_ICONS
|
||||
item.icon_filename = "instance_add.svg";
|
||||
#endif // ENABLE_SVG_ICONS
|
||||
item.tooltip = GUI::L_str("Add instance [+]");
|
||||
item.sprite_id = 4;
|
||||
item.sprite_id = 6;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); };
|
||||
item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_increase_instances(); };
|
||||
|
@ -3243,7 +3284,7 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.icon_filename = "instance_remove.svg";
|
||||
#endif // ENABLE_SVG_ICONS
|
||||
item.tooltip = GUI::L_str("Remove instance [-]");
|
||||
item.sprite_id = 5;
|
||||
item.sprite_id = 7;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); };
|
||||
item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_decrease_instances(); };
|
||||
|
@ -3258,7 +3299,7 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.icon_filename = "split_objects.svg";
|
||||
#endif // ENABLE_SVG_ICONS
|
||||
item.tooltip = GUI::L_str("Split to objects");
|
||||
item.sprite_id = 6;
|
||||
item.sprite_id = 8;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); };
|
||||
item.visibility_callback = GLToolbarItem::Default_Visibility_Callback;
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_split_to_objects(); };
|
||||
|
@ -3270,7 +3311,7 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.icon_filename = "split_parts.svg";
|
||||
#endif // ENABLE_SVG_ICONS
|
||||
item.tooltip = GUI::L_str("Split to parts");
|
||||
item.sprite_id = 7;
|
||||
item.sprite_id = 9;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); };
|
||||
item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_split_to_volumes(); };
|
||||
|
@ -3285,7 +3326,7 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.icon_filename = "layers.svg";
|
||||
#endif // ENABLE_SVG_ICONS
|
||||
item.tooltip = GUI::L_str("Layers editing");
|
||||
item.sprite_id = 8;
|
||||
item.sprite_id = 10;
|
||||
item.is_toggable = true;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); };
|
||||
item.visibility_callback = [this]()->bool { return m_process->current_printer_technology() == ptFFF; };
|
||||
|
@ -3490,7 +3531,15 @@ void GLCanvas3D::_picking_pass() const
|
|||
|
||||
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
||||
|
||||
_render_volumes(true);
|
||||
m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane();
|
||||
if (m_camera_clipping_plane.is_active()) {
|
||||
::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data());
|
||||
::glEnable(GL_CLIP_PLANE0);
|
||||
}
|
||||
_render_volumes_for_picking();
|
||||
if (m_camera_clipping_plane.is_active())
|
||||
::glDisable(GL_CLIP_PLANE0);
|
||||
|
||||
m_gizmos.render_current_gizmo_for_picking_pass(m_selection);
|
||||
|
||||
if (m_multisample_allowed)
|
||||
|
@ -3571,6 +3620,8 @@ void GLCanvas3D::_render_axes() const
|
|||
m_bed.render_axes();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLCanvas3D::_render_objects() const
|
||||
{
|
||||
if (m_volumes.empty())
|
||||
|
@ -3579,6 +3630,8 @@ void GLCanvas3D::_render_objects() const
|
|||
glsafe(::glEnable(GL_LIGHTING));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
||||
m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane();
|
||||
|
||||
if (m_use_VBOs)
|
||||
{
|
||||
if (m_picking_enabled)
|
||||
|
@ -3599,6 +3652,8 @@ void GLCanvas3D::_render_objects() const
|
|||
else
|
||||
m_volumes.set_z_range(-FLT_MAX, FLT_MAX);
|
||||
|
||||
m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data());
|
||||
|
||||
m_shader.start_using();
|
||||
if (m_picking_enabled && m_layers_editing.is_enabled() && m_layers_editing.last_object_id != -1) {
|
||||
int object_id = m_layers_editing.last_object_id;
|
||||
|
@ -3619,13 +3674,17 @@ void GLCanvas3D::_render_objects() const
|
|||
}
|
||||
else
|
||||
{
|
||||
::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data());
|
||||
::glEnable(GL_CLIP_PLANE0);
|
||||
|
||||
if (m_use_clipping_planes)
|
||||
{
|
||||
glsafe(::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_clipping_planes[0].get_data()));
|
||||
glsafe(::glEnable(GL_CLIP_PLANE0));
|
||||
glsafe(::glClipPlane(GL_CLIP_PLANE1, (GLdouble*)m_clipping_planes[1].get_data()));
|
||||
glsafe(::glClipPlane(GL_CLIP_PLANE1, (GLdouble*)m_clipping_planes[0].get_data()));
|
||||
glsafe(::glEnable(GL_CLIP_PLANE1));
|
||||
glsafe(::glClipPlane(GL_CLIP_PLANE2, (GLdouble*)m_clipping_planes[1].get_data()));
|
||||
glsafe(::glEnable(GL_CLIP_PLANE2));
|
||||
}
|
||||
|
||||
|
||||
// do not cull backfaces to show broken geometry, if any
|
||||
m_volumes.render_legacy(GLVolumeCollection::Opaque, m_picking_enabled, m_camera.get_view_matrix(), [this](const GLVolume& volume) {
|
||||
|
@ -3633,13 +3692,16 @@ void GLCanvas3D::_render_objects() const
|
|||
});
|
||||
m_volumes.render_legacy(GLVolumeCollection::Transparent, false, m_camera.get_view_matrix());
|
||||
|
||||
::glDisable(GL_CLIP_PLANE0);
|
||||
|
||||
if (m_use_clipping_planes)
|
||||
{
|
||||
glsafe(::glDisable(GL_CLIP_PLANE0));
|
||||
glsafe(::glDisable(GL_CLIP_PLANE1));
|
||||
glsafe(::glDisable(GL_CLIP_PLANE2));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_camera_clipping_plane = ClippingPlane::ClipsNothing();
|
||||
glsafe(::glDisable(GL_LIGHTING));
|
||||
}
|
||||
|
||||
|
@ -3675,13 +3737,10 @@ void GLCanvas3D::_render_legend_texture() const
|
|||
m_legend_texture.render(*this);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_volumes(bool fake_colors) const
|
||||
void GLCanvas3D::_render_volumes_for_picking() const
|
||||
{
|
||||
static const GLfloat INV_255 = 1.0f / 255.0f;
|
||||
|
||||
if (!fake_colors)
|
||||
glsafe(::glEnable(GL_LIGHTING));
|
||||
|
||||
// do not cull backfaces to show broken geometry, if any
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
|
||||
|
@ -3691,27 +3750,31 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
|
|||
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
|
||||
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
|
||||
|
||||
unsigned int volume_id = 0;
|
||||
for (GLVolume* vol : m_volumes.volumes)
|
||||
const Transform3d& view_matrix = m_camera.get_view_matrix();
|
||||
GLVolumeWithIdAndZList to_render = volumes_to_render(m_volumes.volumes, GLVolumeCollection::Opaque, view_matrix);
|
||||
for (const GLVolumeWithIdAndZ& volume : to_render)
|
||||
{
|
||||
if (fake_colors)
|
||||
{
|
||||
// Object picking mode. Render the object with a color encoding the object index.
|
||||
unsigned int r = (volume_id & 0x000000FF) >> 0;
|
||||
unsigned int g = (volume_id & 0x0000FF00) >> 8;
|
||||
unsigned int b = (volume_id & 0x00FF0000) >> 16;
|
||||
glsafe(::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255));
|
||||
}
|
||||
else
|
||||
{
|
||||
vol->set_render_color();
|
||||
glsafe(::glColor4fv(vol->render_color));
|
||||
}
|
||||
// Object picking mode. Render the object with a color encoding the object index.
|
||||
unsigned int r = (volume.second.first & 0x000000FF) >> 0;
|
||||
unsigned int g = (volume.second.first & 0x0000FF00) >> 8;
|
||||
unsigned int b = (volume.second.first & 0x00FF0000) >> 16;
|
||||
glsafe(::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255));
|
||||
|
||||
if ((!fake_colors || !vol->disabled) && (vol->composite_id.volume_id >= 0 || m_render_sla_auxiliaries))
|
||||
vol->render();
|
||||
if (!volume.first->disabled && ((volume.first->composite_id.volume_id >= 0) || m_render_sla_auxiliaries))
|
||||
volume.first->render();
|
||||
}
|
||||
|
||||
++volume_id;
|
||||
to_render = volumes_to_render(m_volumes.volumes, GLVolumeCollection::Transparent, view_matrix);
|
||||
for (const GLVolumeWithIdAndZ& volume : to_render)
|
||||
{
|
||||
// Object picking mode. Render the object with a color encoding the object index.
|
||||
unsigned int r = (volume.second.first & 0x000000FF) >> 0;
|
||||
unsigned int g = (volume.second.first & 0x0000FF00) >> 8;
|
||||
unsigned int b = (volume.second.first & 0x00FF0000) >> 16;
|
||||
glsafe(::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255));
|
||||
|
||||
if (!volume.first->disabled && ((volume.first->composite_id.volume_id >= 0) || m_render_sla_auxiliaries))
|
||||
volume.first->render();
|
||||
}
|
||||
|
||||
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
|
||||
|
@ -3719,9 +3782,6 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
|
|||
glsafe(::glDisable(GL_BLEND));
|
||||
|
||||
glsafe(::glEnable(GL_CULL_FACE));
|
||||
|
||||
if (!fake_colors)
|
||||
glsafe(::glDisable(GL_LIGHTING));
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_current_gizmo() const
|
||||
|
@ -3999,15 +4059,9 @@ void GLCanvas3D::_update_volumes_hover_state() const
|
|||
return;
|
||||
|
||||
GLVolume* volume = m_volumes.volumes[m_hover_volume_id];
|
||||
|
||||
switch (m_selection.get_mode())
|
||||
{
|
||||
case Selection::Volume:
|
||||
{
|
||||
if (volume->is_modifier)
|
||||
volume->hover = true;
|
||||
break;
|
||||
}
|
||||
case Selection::Instance:
|
||||
else
|
||||
{
|
||||
int object_idx = volume->object_idx();
|
||||
int instance_idx = volume->instance_idx();
|
||||
|
@ -4017,9 +4071,6 @@ void GLCanvas3D::_update_volumes_hover_state() const
|
|||
if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx))
|
||||
v->hover = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,37 @@ public:
|
|||
void set_scale_factor(int height);
|
||||
};
|
||||
|
||||
|
||||
class ClippingPlane
|
||||
{
|
||||
double m_data[4];
|
||||
|
||||
public:
|
||||
ClippingPlane()
|
||||
{
|
||||
m_data[0] = 0.0;
|
||||
m_data[1] = 0.0;
|
||||
m_data[2] = 1.0;
|
||||
m_data[3] = 0.0;
|
||||
}
|
||||
|
||||
ClippingPlane(const Vec3d& direction, double offset)
|
||||
{
|
||||
Vec3d norm_dir = direction.normalized();
|
||||
m_data[0] = norm_dir(0);
|
||||
m_data[1] = norm_dir(1);
|
||||
m_data[2] = norm_dir(2);
|
||||
m_data[3] = offset;
|
||||
}
|
||||
|
||||
bool is_active() const { return m_data[3] != DBL_MAX; }
|
||||
|
||||
static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); }
|
||||
|
||||
const double* get_data() const { return m_data; }
|
||||
};
|
||||
|
||||
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
|
||||
|
||||
using Vec2dEvent = Event<Vec2d>;
|
||||
|
@ -288,32 +319,6 @@ class GLCanvas3D
|
|||
}
|
||||
};
|
||||
|
||||
public:
|
||||
class ClippingPlane
|
||||
{
|
||||
double m_data[4];
|
||||
|
||||
public:
|
||||
ClippingPlane()
|
||||
{
|
||||
m_data[0] = 0.0;
|
||||
m_data[1] = 0.0;
|
||||
m_data[2] = 1.0;
|
||||
m_data[3] = 0.0;
|
||||
}
|
||||
|
||||
ClippingPlane(const Vec3d& direction, double offset)
|
||||
{
|
||||
Vec3d norm_dir = direction.normalized();
|
||||
m_data[0] = norm_dir(0);
|
||||
m_data[1] = norm_dir(1);
|
||||
m_data[2] = norm_dir(2);
|
||||
m_data[3] = offset;
|
||||
}
|
||||
|
||||
const double* get_data() const { return m_data; }
|
||||
};
|
||||
|
||||
private:
|
||||
struct SlaCap
|
||||
{
|
||||
|
@ -405,6 +410,7 @@ private:
|
|||
mutable GLGizmosManager m_gizmos;
|
||||
mutable GLToolbar m_toolbar;
|
||||
ClippingPlane m_clipping_planes[2];
|
||||
mutable ClippingPlane m_camera_clipping_plane;
|
||||
bool m_use_clipping_planes;
|
||||
mutable SlaCap m_sla_caps[2];
|
||||
std::string m_sidebar_field;
|
||||
|
@ -579,6 +585,8 @@ public:
|
|||
void refresh_camera_scene_box() { m_camera.set_scene_box(scene_bounding_box()); }
|
||||
bool is_mouse_dragging() const { return m_mouse.dragging; }
|
||||
|
||||
double get_size_proportional_to_max_bed_size(double factor) const;
|
||||
|
||||
private:
|
||||
bool _is_shown_on_screen() const;
|
||||
|
||||
|
@ -605,7 +613,7 @@ private:
|
|||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
void _render_warning_texture() const;
|
||||
void _render_legend_texture() const;
|
||||
void _render_volumes(bool fake_colors) const;
|
||||
void _render_volumes_for_picking() const;
|
||||
void _render_current_gizmo() const;
|
||||
void _render_gizmos_overlay() const;
|
||||
void _render_toolbar() const;
|
||||
|
|
|
@ -21,6 +21,8 @@ wxDEFINE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent);
|
|||
wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE_ALL, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLTOOLBAR_ARRANGE, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLTOOLBAR_COPY, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLTOOLBAR_PASTE, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLTOOLBAR_MORE, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLTOOLBAR_FEWER, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLTOOLBAR_SPLIT_OBJECTS, SimpleEvent);
|
||||
|
|
|
@ -20,6 +20,8 @@ wxDECLARE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent);
|
|||
wxDECLARE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_DELETE_ALL, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_ARRANGE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_COPY, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_PASTE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_MORE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_FEWER, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_SPLIT_OBJECTS, SimpleEvent);
|
||||
|
|
|
@ -61,19 +61,22 @@ ObjectList::ObjectList(wxWindow* parent) :
|
|||
{
|
||||
// Fill CATEGORY_ICON
|
||||
{
|
||||
// Note: `this` isn't passed to create_scaled_bitmap() here because of bugs in the widget,
|
||||
// see note in PresetBundle::load_compatible_bitmaps()
|
||||
|
||||
// ptFFF
|
||||
CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap(this, "layers");
|
||||
CATEGORY_ICON[L("Infill")] = create_scaled_bitmap(this, "infill");
|
||||
CATEGORY_ICON[L("Support material")] = create_scaled_bitmap(this, "support");
|
||||
CATEGORY_ICON[L("Speed")] = create_scaled_bitmap(this, "time");
|
||||
CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap(this, "funnel");
|
||||
CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap(this, "funnel");
|
||||
// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap(this, "skirt+brim");
|
||||
// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap(this, "time");
|
||||
CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap(this, "wrench");
|
||||
// ptSLA
|
||||
CATEGORY_ICON[L("Supports")] = create_scaled_bitmap(this, "sla_supports");
|
||||
CATEGORY_ICON[L("Pad")] = create_scaled_bitmap(this, "brick.png");
|
||||
CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap(nullptr, "layers");
|
||||
CATEGORY_ICON[L("Infill")] = create_scaled_bitmap(nullptr, "infill");
|
||||
CATEGORY_ICON[L("Support material")] = create_scaled_bitmap(nullptr, "support");
|
||||
CATEGORY_ICON[L("Speed")] = create_scaled_bitmap(nullptr, "time");
|
||||
CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap(nullptr, "funnel");
|
||||
CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap(nullptr, "funnel");
|
||||
// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap(nullptr, "skirt+brim");
|
||||
// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap(nullptr, "time");
|
||||
CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap(nullptr, "wrench");
|
||||
// ptSLA
|
||||
CATEGORY_ICON[L("Supports")] = create_scaled_bitmap(nullptr, "support"/*"sla_supports"*/);
|
||||
CATEGORY_ICON[L("Pad")] = create_scaled_bitmap(nullptr, "pad");
|
||||
}
|
||||
|
||||
// create control
|
||||
|
@ -392,10 +395,10 @@ void ObjectList::update_name_in_model(const wxDataViewItem& item) const
|
|||
|
||||
void ObjectList::init_icons()
|
||||
{
|
||||
m_bmp_modifiermesh = create_scaled_bitmap(this, "lambda.png");
|
||||
m_bmp_solidmesh = create_scaled_bitmap(this, "object.png");
|
||||
m_bmp_support_enforcer = create_scaled_bitmap(this, "support_enforcer_.png");
|
||||
m_bmp_support_blocker = create_scaled_bitmap(this, "support_blocker_.png");
|
||||
m_bmp_modifiermesh = create_scaled_bitmap(nullptr, "add_modifier");
|
||||
m_bmp_solidmesh = create_scaled_bitmap(nullptr, "add_part");
|
||||
m_bmp_support_enforcer = create_scaled_bitmap(nullptr, "support_enforcer");
|
||||
m_bmp_support_blocker = create_scaled_bitmap(nullptr, "support_blocker");
|
||||
|
||||
|
||||
m_bmp_vector.reserve(4); // bitmaps for different types of parts
|
||||
|
@ -406,13 +409,13 @@ void ObjectList::init_icons()
|
|||
m_objects_model->SetVolumeBitmaps(m_bmp_vector);
|
||||
|
||||
// init icon for manifold warning
|
||||
m_bmp_manifold_warning = create_scaled_bitmap(this, "exclamation_mark_.png");
|
||||
m_bmp_manifold_warning = create_scaled_bitmap(nullptr, "exclamation");
|
||||
|
||||
// init bitmap for "Split to sub-objects" context menu
|
||||
m_bmp_split = create_scaled_bitmap(this, "split_parts");
|
||||
m_bmp_split = create_scaled_bitmap(nullptr, "split_parts_SMALL");
|
||||
|
||||
// init bitmap for "Add Settings" context menu
|
||||
m_bmp_cog = create_scaled_bitmap(this, "cog");
|
||||
m_bmp_cog = create_scaled_bitmap(nullptr, "cog");
|
||||
}
|
||||
|
||||
|
||||
|
@ -436,6 +439,66 @@ void ObjectList::selection_changed()
|
|||
part_selection_changed();
|
||||
}
|
||||
|
||||
void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes)
|
||||
{
|
||||
if ((obj_idx < 0) || ((int)m_objects->size() <= obj_idx))
|
||||
return;
|
||||
|
||||
if (volumes.empty())
|
||||
return;
|
||||
|
||||
ModelObject& model_object = *(*m_objects)[obj_idx];
|
||||
const auto object_item = m_objects_model->GetItemById(obj_idx);
|
||||
|
||||
wxDataViewItemArray items;
|
||||
|
||||
for (const ModelVolume* volume : volumes)
|
||||
{
|
||||
auto vol_item = m_objects_model->AddVolumeChild(object_item, volume->name, volume->type(),
|
||||
volume->config.has("extruder") ? volume->config.option<ConfigOptionInt>("extruder")->value : 0);
|
||||
auto opt_keys = volume->config.keys();
|
||||
if (!opt_keys.empty() && !((opt_keys.size() == 1) && (opt_keys[0] == "extruder")))
|
||||
select_item(m_objects_model->AddSettingsChild(vol_item));
|
||||
|
||||
items.Add(vol_item);
|
||||
}
|
||||
|
||||
m_parts_changed = true;
|
||||
parts_changed(obj_idx);
|
||||
|
||||
if (items.size() > 1)
|
||||
{
|
||||
m_selection_mode = smVolume;
|
||||
m_last_selected_item = wxDataViewItem(0);
|
||||
}
|
||||
|
||||
select_items(items);
|
||||
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
selection_changed();
|
||||
#endif //no __WXOSX__ //__WXMSW__
|
||||
}
|
||||
|
||||
void ObjectList::paste_objects_into_list(const std::vector<size_t>& object_idxs)
|
||||
{
|
||||
if (object_idxs.empty())
|
||||
return;
|
||||
|
||||
wxDataViewItemArray items;
|
||||
for (const size_t object : object_idxs)
|
||||
{
|
||||
add_object_to_list(object);
|
||||
m_parts_changed = true;
|
||||
parts_changed(object);
|
||||
|
||||
items.Add(m_objects_model->GetItemById(object));
|
||||
}
|
||||
|
||||
select_items(items);
|
||||
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
selection_changed();
|
||||
#endif //no __WXOSX__ //__WXMSW__
|
||||
}
|
||||
|
||||
void ObjectList::OnChar(wxKeyEvent& event)
|
||||
{
|
||||
if (event.GetKeyCode() == WXK_BACK){
|
||||
|
@ -1374,9 +1437,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
|
|||
const wxString name = _(L("Generic")) + "-" + _(type_name);
|
||||
TriangleMesh mesh;
|
||||
|
||||
auto& bed_shape = printer_config().option<ConfigOptionPoints>("bed_shape")->values;
|
||||
const auto& sz = BoundingBoxf(bed_shape).size();
|
||||
const auto side = 0.1 * std::max(sz(0), sz(1));
|
||||
double side = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.1);
|
||||
|
||||
if (type_name == "Box")
|
||||
// Sitting on the print bed, left front front corner at (0, 0).
|
||||
|
@ -1573,11 +1634,11 @@ void ObjectList::split()
|
|||
|
||||
for (auto id = 0; id < model_object->volumes.size(); id++) {
|
||||
const auto vol_item = m_objects_model->AddVolumeChild(parent, from_u8(model_object->volumes[id]->name),
|
||||
model_object->volumes[id]->is_modifier() ?
|
||||
ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART,
|
||||
model_object->volumes[id]->config.has("extruder") ?
|
||||
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0,
|
||||
false);
|
||||
model_object->volumes[id]->is_modifier() ?
|
||||
ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART,
|
||||
model_object->volumes[id]->config.has("extruder") ?
|
||||
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0,
|
||||
false);
|
||||
// add settings to the part, if it has those
|
||||
auto opt_keys = model_object->volumes[id]->config.keys();
|
||||
if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) {
|
||||
|
@ -2132,6 +2193,7 @@ void ObjectList::update_selections_on_canvas()
|
|||
add_to_selection(item, selection, instance_idx, false);
|
||||
|
||||
wxGetApp().plater()->canvas3D()->update_gizmos_on_off_state();
|
||||
wxGetApp().plater()->canvas3D()->render();
|
||||
}
|
||||
|
||||
void ObjectList::select_item(const wxDataViewItem& item)
|
||||
|
|
|
@ -31,6 +31,8 @@ typedef std::map<std::string, std::vector<std::string>> FreqSettingsBundle;
|
|||
// category -> vector ( option ; label )
|
||||
typedef std::map< std::string, std::vector< std::pair<std::string, std::string> > > settings_menu_hierarchy;
|
||||
|
||||
typedef std::vector<ModelVolume*> ModelVolumePtrs;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
wxDECLARE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
|
||||
|
@ -286,6 +288,9 @@ public:
|
|||
void fix_through_netfabb() const;
|
||||
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
|
||||
|
||||
void paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes);
|
||||
void paste_objects_into_list(const std::vector<size_t>& object_idxs);
|
||||
|
||||
void rescale();
|
||||
|
||||
private:
|
||||
|
|
|
@ -818,8 +818,8 @@ void Preview::on_sliders_scroll_changed(wxEvent& event)
|
|||
}
|
||||
else if (tech == ptSLA)
|
||||
{
|
||||
m_canvas->set_clipping_plane(0, GLCanvas3D::ClippingPlane(Vec3d::UnitZ(), -m_slider->GetLowerValueD()));
|
||||
m_canvas->set_clipping_plane(1, GLCanvas3D::ClippingPlane(-Vec3d::UnitZ(), m_slider->GetHigherValueD()));
|
||||
m_canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -m_slider->GetLowerValueD()));
|
||||
m_canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), m_slider->GetHigherValueD()));
|
||||
m_canvas->set_use_clipping_planes(m_slider->GetHigherValue() != 0);
|
||||
m_canvas_widget->Refresh();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "slic3r/GUI/GUI_ObjectSettings.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
||||
#include "slic3r/GUI/PresetBundle.hpp"
|
||||
#include "libslic3r/Tesselate.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -47,24 +48,24 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S
|
|||
{
|
||||
if (selection.is_empty()) {
|
||||
m_model_object = nullptr;
|
||||
m_old_model_object = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
m_old_model_object = m_model_object;
|
||||
m_model_object = model_object;
|
||||
m_active_instance = selection.get_instance_idx();
|
||||
|
||||
if (model_object && selection.is_from_single_instance())
|
||||
{
|
||||
// Cache the bb - it's needed for dealing with the clipping plane quite often
|
||||
// It could be done inside update_mesh but one has to account for scaling of the instance.
|
||||
//FIXME calling ModelObject::instance_bounding_box() is expensive!
|
||||
m_active_instance_bb_radius = m_model_object->instance_bounding_box(m_active_instance).radius();
|
||||
|
||||
if (is_mesh_update_necessary()) {
|
||||
update_mesh();
|
||||
editing_mode_reload_cache();
|
||||
}
|
||||
|
||||
if (m_model_object != m_old_model_object)
|
||||
m_editing_mode = false;
|
||||
|
||||
if (m_editing_mode_cache.empty() && m_model_object->sla_points_status != sla::PointsStatus::UserModified)
|
||||
get_data_from_backend();
|
||||
|
||||
|
@ -88,12 +89,75 @@ void GLGizmoSlaSupports::on_render(const Selection& selection) const
|
|||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
||||
render_points(selection, false);
|
||||
// we'll recover current look direction from the modelview matrix (in world coords):
|
||||
Eigen::Matrix<double, 4, 4, Eigen::DontAlign> modelview_matrix;
|
||||
::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data());
|
||||
Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]);
|
||||
m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z();
|
||||
|
||||
if (m_quadric != nullptr && selection.is_from_single_instance())
|
||||
render_points(selection, direction_to_camera, false);
|
||||
|
||||
render_selection_rectangle();
|
||||
render_clipping_plane(selection, direction_to_camera);
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection, const Vec3d& direction_to_camera) const
|
||||
{
|
||||
if (m_clipping_plane_distance == 0.f)
|
||||
return;
|
||||
|
||||
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
Transform3f instance_matrix = vol->get_instance_transformation().get_matrix().cast<float>();
|
||||
Transform3f instance_matrix_no_translation_no_scaling = vol->get_instance_transformation().get_matrix(true,false,true).cast<float>();
|
||||
Vec3f scaling = vol->get_instance_scaling_factor().cast<float>();
|
||||
|
||||
Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * direction_to_camera.cast<float>();
|
||||
Vec3f up = Vec3f(up_noscale(0)*scaling(0), up_noscale(1)*scaling(1), up_noscale(2)*scaling(2));
|
||||
float height_mesh = (m_active_instance_bb_radius - m_clipping_plane_distance * 2*m_active_instance_bb_radius) * (up_noscale.norm()/up.norm());
|
||||
|
||||
if (m_clipping_plane_distance != m_old_clipping_plane_distance
|
||||
|| m_old_direction_to_camera != direction_to_camera) {
|
||||
|
||||
std::vector<ExPolygons> list_of_expolys;
|
||||
if (! m_tms) {
|
||||
m_tms.reset(new TriangleMeshSlicer);
|
||||
m_tms->init(const_cast<TriangleMesh*>(&m_mesh), [](){});
|
||||
}
|
||||
|
||||
m_tms->set_up_direction(up);
|
||||
m_tms->slice(std::vector<float>{height_mesh}, 0.f, &list_of_expolys, [](){});
|
||||
m_triangles = triangulate_expolygons_2f(list_of_expolys[0]);
|
||||
|
||||
m_old_direction_to_camera = direction_to_camera;
|
||||
m_old_clipping_plane_distance = m_clipping_plane_distance;
|
||||
}
|
||||
|
||||
if (! m_triangles.empty()) {
|
||||
::glPushMatrix();
|
||||
::glTranslated(0.0, 0.0, m_z_shift);
|
||||
::glMultMatrixf(instance_matrix.data());
|
||||
Eigen::Quaternionf q;
|
||||
q.setFromTwoVectors(Vec3f::UnitZ(), up);
|
||||
Eigen::AngleAxisf aa(q);
|
||||
::glRotatef(aa.angle() * (180./M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2));
|
||||
::glTranslatef(0.f, 0.f, -0.001f); // to make sure the cut is safely beyond the near clipping plane
|
||||
::glColor3f(1.0f, 0.37f, 0.0f);
|
||||
::glBegin(GL_TRIANGLES);
|
||||
::glColor3f(1.0f, 0.37f, 0.0f);
|
||||
for (const Vec2f& point : m_triangles)
|
||||
::glVertex3f(point(0), point(1), height_mesh);
|
||||
::glEnd();
|
||||
::glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::render_selection_rectangle() const
|
||||
{
|
||||
if (m_selection_rectangle_status == srOff)
|
||||
|
@ -143,24 +207,25 @@ void GLGizmoSlaSupports::on_render_for_picking(const Selection& selection) const
|
|||
{
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
|
||||
render_points(selection, true);
|
||||
// we'll recover current look direction from the modelview matrix (in world coords):
|
||||
Eigen::Matrix<double, 4, 4, Eigen::DontAlign> modelview_matrix;
|
||||
::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data());
|
||||
Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]);
|
||||
|
||||
render_points(selection, direction_to_camera, true);
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const
|
||||
void GLGizmoSlaSupports::render_points(const Selection& selection, const Vec3d& direction_to_camera, bool picking) const
|
||||
{
|
||||
if (m_quadric == nullptr || !selection.is_from_single_instance())
|
||||
return;
|
||||
|
||||
if (!picking)
|
||||
glsafe(::glEnable(GL_LIGHTING));
|
||||
|
||||
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
double z_shift = vol->get_sla_shift_z();
|
||||
const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse();
|
||||
const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix();
|
||||
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(0.0, 0.0, z_shift));
|
||||
glsafe(::glTranslated(0.0, 0.0, m_z_shift));
|
||||
glsafe(::glMultMatrixd(instance_matrix.data()));
|
||||
|
||||
float render_color[3];
|
||||
|
@ -169,6 +234,9 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
const sla::SupportPoint& support_point = m_editing_mode_cache[i].support_point;
|
||||
const bool& point_selected = m_editing_mode_cache[i].selected;
|
||||
|
||||
if (is_point_clipped(support_point.pos.cast<double>(), direction_to_camera))
|
||||
continue;
|
||||
|
||||
// First decide about the color of the point.
|
||||
if (picking) {
|
||||
std::array<float, 3> color = picking_color_component(i);
|
||||
|
@ -238,13 +306,25 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
glsafe(::glPopMatrix());
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point, const Vec3d& direction_to_camera) const
|
||||
{
|
||||
if (m_clipping_plane_distance == 0.f)
|
||||
return false;
|
||||
|
||||
Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point;
|
||||
transformed_point(2) += m_z_shift;
|
||||
return direction_to_camera.dot(m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift)) + m_active_instance_bb_radius
|
||||
- m_clipping_plane_distance * 2*m_active_instance_bb_radius < direction_to_camera.dot(transformed_point);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool GLGizmoSlaSupports::is_mesh_update_necessary() const
|
||||
{
|
||||
return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty())
|
||||
&& ((m_model_object != m_old_model_object) || m_V.size()==0);
|
||||
|
||||
//if (m_state != On || !m_model_object || m_model_object->instances.empty() || ! m_instance_matrix.isApprox(m_source_data.matrix))
|
||||
// return false;
|
||||
&& ((m_model_object->id() != m_current_mesh_model_id) || m_V.size()==0);
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::update_mesh()
|
||||
|
@ -252,10 +332,9 @@ void GLGizmoSlaSupports::update_mesh()
|
|||
wxBusyCursor wait;
|
||||
Eigen::MatrixXf& V = m_V;
|
||||
Eigen::MatrixXi& F = m_F;
|
||||
// Composite mesh of all instances in the world coordinate system.
|
||||
// This mesh does not account for the possible Z up SLA offset.
|
||||
TriangleMesh mesh = m_model_object->raw_mesh();
|
||||
const stl_file& stl = mesh.stl;
|
||||
m_mesh = m_model_object->raw_mesh();
|
||||
const stl_file& stl = m_mesh.stl;
|
||||
V.resize(3 * stl.stats.number_of_facets, 3);
|
||||
F.resize(stl.stats.number_of_facets, 3);
|
||||
for (unsigned int i=0; i<stl.stats.number_of_facets; ++i) {
|
||||
|
@ -267,11 +346,15 @@ void GLGizmoSlaSupports::update_mesh()
|
|||
F(i, 1) = 3*i+1;
|
||||
F(i, 2) = 3*i+2;
|
||||
}
|
||||
m_current_mesh_model_id = m_model_object->id();
|
||||
m_editing_mode = false;
|
||||
|
||||
m_AABB = igl::AABB<Eigen::MatrixXf,3>();
|
||||
m_AABB.init(m_V, m_F);
|
||||
}
|
||||
|
||||
// Unprojects the mouse position on the mesh and return the hit point and normal of the facet.
|
||||
// The function throws if no intersection if found.
|
||||
std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
|
||||
{
|
||||
// if the gizmo doesn't have the V, F structures for igl, calculate them first:
|
||||
|
@ -288,31 +371,53 @@ std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
|
|||
::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 0.f, modelview_matrix.data(), projection_matrix.data(), viewport.data(), &point1(0), &point1(1), &point1(2));
|
||||
::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 1.f, modelview_matrix.data(), projection_matrix.data(), viewport.data(), &point2(0), &point2(1), &point2(2));
|
||||
|
||||
igl::Hit hit;
|
||||
std::vector<igl::Hit> hits;
|
||||
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
double z_offset = volume->get_sla_shift_z();
|
||||
|
||||
point1(2) -= z_offset;
|
||||
point2(2) -= z_offset;
|
||||
// we'll recover current look direction from the modelview matrix (in world coords):
|
||||
Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]);
|
||||
|
||||
point1(2) -= m_z_shift;
|
||||
point2(2) -= m_z_shift;
|
||||
|
||||
Transform3d inv = volume->get_instance_transformation().get_matrix().inverse();
|
||||
|
||||
point1 = inv * point1;
|
||||
point2 = inv * point2;
|
||||
|
||||
if (!m_AABB.intersect_ray(m_V, m_F, point1.cast<float>(), (point2-point1).cast<float>(), hit))
|
||||
if (!m_AABB.intersect_ray(m_V, m_F, point1.cast<float>(), (point2-point1).cast<float>(), hits))
|
||||
throw std::invalid_argument("unproject_on_mesh(): No intersection found.");
|
||||
|
||||
int fid = hit.id; // facet id
|
||||
Vec3f bc(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
|
||||
Vec3f a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0)));
|
||||
Vec3f b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0)));
|
||||
std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; });
|
||||
|
||||
// Now let's iterate through the points and find the first that is not clipped:
|
||||
unsigned int i=0;
|
||||
Vec3f bc;
|
||||
Vec3f a;
|
||||
Vec3f b;
|
||||
Vec3f result;
|
||||
for (i=0; i<hits.size(); ++i) {
|
||||
igl::Hit& hit = hits[i];
|
||||
int fid = hit.id; // facet id
|
||||
bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
|
||||
a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0)));
|
||||
b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0)));
|
||||
result = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2));
|
||||
if (m_clipping_plane_distance == 0.f || !is_point_clipped(result.cast<double>(), direction_to_camera))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i==hits.size() || (hits.size()-i) % 2 != 0) {
|
||||
// All hits are either clipped, or there is an odd number of unclipped
|
||||
// hits - meaning the nearest must be from inside the mesh.
|
||||
throw std::invalid_argument("unproject_on_mesh(): No intersection found.");
|
||||
}
|
||||
|
||||
// Calculate and return both the point and the facet normal.
|
||||
return std::make_pair(
|
||||
bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2)),
|
||||
result,
|
||||
a.cross(b)
|
||||
);
|
||||
}
|
||||
|
@ -383,36 +488,64 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
double z_offset = volume->get_sla_shift_z();
|
||||
|
||||
// bounding box created from the rectangle corners - will take care of order of the corners
|
||||
BoundingBox rectangle(Points{Point(m_selection_rectangle_start_corner.cast<int>()), Point(m_selection_rectangle_end_corner.cast<int>())});
|
||||
|
||||
const Transform3d& instance_matrix_no_translation = volume->get_instance_transformation().get_matrix(true);
|
||||
const Transform3d& instance_matrix_no_translation_no_scaling = volume->get_instance_transformation().get_matrix(true,false,true);
|
||||
|
||||
// we'll recover current look direction from the modelview matrix (in world coords)...
|
||||
Vec3f direction_to_camera = camera.get_dir_forward().cast<float>();
|
||||
// ...and transform it to model coords.
|
||||
direction_to_camera = (instance_matrix_no_translation.inverse().cast<float>() * direction_to_camera).normalized().eval();
|
||||
Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast<float>() * direction_to_camera).normalized().eval();
|
||||
Vec3f scaling = volume->get_instance_scaling_factor().cast<float>();
|
||||
direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2));
|
||||
|
||||
// Iterate over all points, check if they're in the rectangle and if so, check that they are not obscured by the mesh:
|
||||
for (unsigned int i=0; i<m_editing_mode_cache.size(); ++i) {
|
||||
const sla::SupportPoint &support_point = m_editing_mode_cache[i].support_point;
|
||||
Vec3f pos = instance_matrix.cast<float>() * support_point.pos;
|
||||
pos(2) += z_offset;
|
||||
pos(2) += m_z_shift;
|
||||
GLdouble out_x, out_y, out_z;
|
||||
::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), (GLdouble*)modelview_matrix.data(), (GLdouble*)projection_matrix.data(), (GLint*)viewport.data(), &out_x, &out_y, &out_z);
|
||||
out_y = m_canvas_height - out_y;
|
||||
|
||||
if (rectangle.contains(Point(out_x, out_y))) {
|
||||
if (rectangle.contains(Point(out_x, out_y)) && !is_point_clipped(support_point.pos.cast<double>(), direction_to_camera.cast<double>())) {
|
||||
bool is_obscured = false;
|
||||
// Cast a ray in the direction of the camera and look for intersection with the mesh:
|
||||
std::vector<igl::Hit> hits;
|
||||
// Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies.
|
||||
if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera * (support_point.head_front_radius + EPSILON), direction_to_camera, hits))
|
||||
if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera_mesh * (support_point.head_front_radius + EPSILON), direction_to_camera_mesh, hits)) {
|
||||
std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; });
|
||||
|
||||
if (m_clipping_plane_distance != 0.f) {
|
||||
// If the closest hit facet normal points in the same direction as the ray,
|
||||
// we are looking through the mesh and should therefore discard the point:
|
||||
int fid = hits.front().id; // facet id
|
||||
Vec3f a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0)));
|
||||
Vec3f b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0)));
|
||||
if ((a.cross(b)).dot(direction_to_camera_mesh) > 0.f)
|
||||
is_obscured = true;
|
||||
|
||||
// Eradicate all hits that are on clipped surfaces:
|
||||
for (unsigned int j=0; j<hits.size(); ++j) {
|
||||
const igl::Hit& hit = hits[j];
|
||||
int fid = hit.id; // facet id
|
||||
|
||||
Vec3f bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
|
||||
Vec3f hit_pos = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2));
|
||||
if (is_point_clipped(hit_pos.cast<double>(), direction_to_camera.cast<double>())) {
|
||||
hits.erase(hits.begin()+j);
|
||||
--j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction.
|
||||
// Also, the threshold is in mesh coordinates, not in actual dimensions.
|
||||
if (hits.size() > 1 || hits.front().t > 0.001f)
|
||||
if (!hits.empty())
|
||||
is_obscured = true;
|
||||
}
|
||||
|
||||
if (!is_obscured) {
|
||||
if (m_selection_rectangle_status == srDeselect)
|
||||
|
@ -570,6 +703,64 @@ void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const
|
|||
|
||||
|
||||
|
||||
|
||||
ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const
|
||||
{
|
||||
if (!m_model_object || m_state == Off)
|
||||
return ClippingPlane::ClipsNothing();
|
||||
|
||||
Eigen::Matrix<GLdouble, 4, 4, Eigen::DontAlign> modelview_matrix;
|
||||
::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data());
|
||||
|
||||
// we'll recover current look direction from the modelview matrix (in world coords):
|
||||
Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]);
|
||||
float dist = direction_to_camera.dot(m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift));
|
||||
|
||||
return ClippingPlane(-direction_to_camera.normalized(),(dist - (-m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_active_instance_bb_radius));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
void GLGizmoSlaSupports::find_intersecting_facets(const igl::AABB<Eigen::MatrixXf, 3>* aabb, const Vec3f& normal, double offset, std::vector<unsigned int>& idxs) const
|
||||
{
|
||||
if (aabb->is_leaf()) { // this is a facet
|
||||
// corner.dot(normal) - offset
|
||||
idxs.push_back(aabb->m_primitive);
|
||||
}
|
||||
else { // not a leaf
|
||||
using CornerType = Eigen::AlignedBox<float, 3>::CornerType;
|
||||
bool sign = std::signbit(offset - normal.dot(aabb->m_box.corner(CornerType(0))));
|
||||
for (unsigned int i=1; i<8; ++i)
|
||||
if (std::signbit(offset - normal.dot(aabb->m_box.corner(CornerType(i)))) != sign) {
|
||||
find_intersecting_facets(aabb->m_left, normal, offset, idxs);
|
||||
find_intersecting_facets(aabb->m_right, normal, offset, idxs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::make_line_segments() const
|
||||
{
|
||||
TriangleMeshSlicer tms(&m_model_object->volumes.front()->mesh);
|
||||
Vec3f normal(0.f, 1.f, 1.f);
|
||||
double d = 0.;
|
||||
|
||||
std::vector<IntersectionLine> lines;
|
||||
find_intersections(&m_AABB, normal, d, lines);
|
||||
ExPolygons expolys;
|
||||
tms.make_expolygons_simple(lines, &expolys);
|
||||
|
||||
SVG svg("slice_loops.svg", get_extents(expolys));
|
||||
svg.draw(expolys);
|
||||
//for (const IntersectionLine &l : lines[i])
|
||||
// svg.draw(l, "red", 0);
|
||||
//svg.draw_outline(expolygons, "black", "blue", 0);
|
||||
svg.Close();
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection)
|
||||
{
|
||||
if (!m_model_object)
|
||||
|
@ -580,7 +771,7 @@ void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_l
|
|||
RENDER_AGAIN:
|
||||
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
|
||||
|
||||
const ImVec2 window_size(m_imgui->scaled(17.f, 18.f));
|
||||
const ImVec2 window_size(m_imgui->scaled(17.f, 20.f));
|
||||
ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) ));
|
||||
ImGui::SetNextWindowSize(ImVec2(window_size));
|
||||
|
||||
|
@ -687,6 +878,13 @@ RENDER_AGAIN:
|
|||
(m_model_object->sla_points_status == sla::PointsStatus::Generating ? "Generation in progress..." : "UNKNOWN STATUS"))));
|
||||
}
|
||||
|
||||
|
||||
// Following is rendered in both editing and non-editing mode:
|
||||
m_imgui->text("Clipping of view: ");
|
||||
ImGui::SameLine();
|
||||
ImGui::PushItemWidth(150.0f);
|
||||
bool value_changed = ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f");
|
||||
|
||||
m_imgui->end();
|
||||
|
||||
if (m_editing_mode != m_old_editing_state) { // user toggled between editing/non-editing mode
|
||||
|
@ -740,12 +938,7 @@ std::string GLGizmoSlaSupports::on_get_name() const
|
|||
|
||||
void GLGizmoSlaSupports::on_set_state()
|
||||
{
|
||||
// Following is called through CallAfter, because otherwise there was a problem
|
||||
// on OSX with the wxMessageDialog being shown several times when clicked into.
|
||||
|
||||
wxGetApp().CallAfter([this]() {
|
||||
if (m_state == On && m_old_state != On) { // the gizmo was just turned on
|
||||
|
||||
if (is_mesh_update_necessary())
|
||||
update_mesh();
|
||||
|
||||
|
@ -762,23 +955,32 @@ void GLGizmoSlaSupports::on_set_state()
|
|||
m_new_point_head_diameter = static_cast<const ConfigOptionFloat*>(cfg.option("support_head_front_diameter"))->value;
|
||||
}
|
||||
if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
|
||||
if (m_model_object) {
|
||||
if (m_unsaved_changes) {
|
||||
wxMessageDialog dlg(GUI::wxGetApp().mainframe, _(L("Do you want to save your manually edited support points ?\n")),
|
||||
_(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
|
||||
if (dlg.ShowModal() == wxID_YES)
|
||||
editing_mode_apply_changes();
|
||||
else
|
||||
editing_mode_discard_changes();
|
||||
wxGetApp().CallAfter([this]() {
|
||||
// Following is called through CallAfter, because otherwise there was a problem
|
||||
// on OSX with the wxMessageDialog being shown several times when clicked into.
|
||||
if (m_model_object) {
|
||||
if (m_unsaved_changes) {
|
||||
wxMessageDialog dlg(GUI::wxGetApp().mainframe, _(L("Do you want to save your manually edited support points ?\n")),
|
||||
_(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
|
||||
if (dlg.ShowModal() == wxID_YES)
|
||||
editing_mode_apply_changes();
|
||||
else
|
||||
editing_mode_discard_changes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_parent.toggle_model_objects_visibility(true);
|
||||
m_editing_mode = false; // so it is not active next time the gizmo opens
|
||||
m_editing_mode_cache.clear();
|
||||
m_parent.toggle_model_objects_visibility(true);
|
||||
m_editing_mode = false; // so it is not active next time the gizmo opens
|
||||
m_editing_mode_cache.clear();
|
||||
m_clipping_plane_distance = 0.f;
|
||||
// Release copy of the mesh, triangle slicer and the AABB spatial search structure.
|
||||
m_mesh.clear();
|
||||
m_AABB.deinit();
|
||||
m_V = Eigen::MatrixXf();
|
||||
m_F = Eigen::MatrixXi();
|
||||
m_tms.reset(nullptr);
|
||||
});
|
||||
}
|
||||
m_old_state = m_state;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -827,11 +1029,19 @@ void GLGizmoSlaSupports::unselect_point(int i)
|
|||
|
||||
void GLGizmoSlaSupports::editing_mode_discard_changes()
|
||||
{
|
||||
m_editing_mode_cache.clear();
|
||||
for (const sla::SupportPoint& point : m_model_object->sla_support_points)
|
||||
m_editing_mode_cache.emplace_back(point, false);
|
||||
m_editing_mode = false;
|
||||
m_unsaved_changes = false;
|
||||
// If the points were autogenerated, they may not be on the ModelObject yet.
|
||||
// Because the user probably messed with the cache, we will get the data
|
||||
// from the backend again.
|
||||
|
||||
if (m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated)
|
||||
get_data_from_backend();
|
||||
else {
|
||||
m_editing_mode_cache.clear();
|
||||
for (const sla::SupportPoint& point : m_model_object->sla_support_points)
|
||||
m_editing_mode_cache.emplace_back(point, false);
|
||||
}
|
||||
m_editing_mode = false;
|
||||
m_unsaved_changes = false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -17,12 +17,17 @@ namespace Slic3r {
|
|||
namespace GUI {
|
||||
|
||||
|
||||
class ClippingPlane;
|
||||
|
||||
|
||||
class GLGizmoSlaSupports : public GLGizmoBase
|
||||
{
|
||||
private:
|
||||
ModelObject* m_model_object = nullptr;
|
||||
ModelObject* m_old_model_object = nullptr;
|
||||
ModelID m_current_mesh_model_id = 0;
|
||||
int m_active_instance = -1;
|
||||
float m_active_instance_bb_radius; // to cache the bb
|
||||
mutable float m_z_shift = 0.f;
|
||||
std::pair<Vec3f, Vec3f> unproject_on_mesh(const Vec2d& mouse_pos);
|
||||
|
||||
const float RenderPointScale = 1.f;
|
||||
|
@ -31,6 +36,8 @@ private:
|
|||
Eigen::MatrixXf m_V; // vertices
|
||||
Eigen::MatrixXi m_F; // facets indices
|
||||
igl::AABB<Eigen::MatrixXf,3> m_AABB;
|
||||
TriangleMesh m_mesh;
|
||||
mutable std::vector<Vec2f> m_triangles;
|
||||
|
||||
class CacheEntry {
|
||||
public:
|
||||
|
@ -52,7 +59,7 @@ public:
|
|||
void set_sla_support_data(ModelObject* model_object, const Selection& selection);
|
||||
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
|
||||
void delete_selected_points(bool force = false);
|
||||
std::pair<float, float> get_sla_clipping_plane() const;
|
||||
ClippingPlane get_sla_clipping_plane() const;
|
||||
|
||||
private:
|
||||
bool on_init();
|
||||
|
@ -61,7 +68,8 @@ private:
|
|||
virtual void on_render_for_picking(const Selection& selection) const;
|
||||
|
||||
void render_selection_rectangle() const;
|
||||
void render_points(const Selection& selection, bool picking = false) const;
|
||||
void render_points(const Selection& selection, const Vec3d& direction_to_camera, bool picking = false) const;
|
||||
void render_clipping_plane(const Selection& selection, const Vec3d& direction_to_camera) const;
|
||||
bool is_mesh_update_necessary() const;
|
||||
void update_mesh();
|
||||
void update_cache_entry_normal(unsigned int i) const;
|
||||
|
@ -74,6 +82,8 @@ private:
|
|||
float m_density = 100.f;
|
||||
mutable std::vector<CacheEntry> m_editing_mode_cache; // a support point and whether it is currently selected
|
||||
float m_clipping_plane_distance = 0.f;
|
||||
mutable float m_old_clipping_plane_distance = 0.f;
|
||||
mutable Vec3d m_old_direction_to_camera;
|
||||
|
||||
enum SelectionRectangleStatus {
|
||||
srOff = 0,
|
||||
|
@ -90,7 +100,11 @@ private:
|
|||
int m_canvas_width;
|
||||
int m_canvas_height;
|
||||
|
||||
mutable std::unique_ptr<TriangleMeshSlicer> m_tms;
|
||||
|
||||
std::vector<const ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
|
||||
bool is_point_clipped(const Vec3d& point, const Vec3d& direction_to_camera) const;
|
||||
void find_intersecting_facets(const igl::AABB<Eigen::MatrixXf, 3>* aabb, const Vec3f& normal, double offset, std::vector<unsigned int>& out) const;
|
||||
|
||||
// Methods that do the model_object and editing cache synchronization,
|
||||
// editing mode selection, etc:
|
||||
|
|
|
@ -467,6 +467,19 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
|
|||
return false;
|
||||
}
|
||||
|
||||
ClippingPlane GLGizmosManager::get_sla_clipping_plane() const
|
||||
{
|
||||
if (!m_enabled || m_current != SlaSupports)
|
||||
return ClippingPlane::ClipsNothing();
|
||||
|
||||
GizmosMap::const_iterator it = m_gizmos.find(SlaSupports);
|
||||
if (it != m_gizmos.end())
|
||||
return reinterpret_cast<GLGizmoSlaSupports*>(it->second)->get_sla_clipping_plane();
|
||||
|
||||
return ClippingPlane::ClipsNothing();
|
||||
}
|
||||
|
||||
|
||||
void GLGizmosManager::render_current_gizmo(const Selection& selection) const
|
||||
{
|
||||
if (!m_enabled)
|
||||
|
@ -713,7 +726,12 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt, GLCanvas3D& canvas)
|
|||
{
|
||||
switch (keyCode)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
case 'a':
|
||||
case 'A':
|
||||
#else /* __APPLE__ */
|
||||
case WXK_CONTROL_A:
|
||||
#endif /* __APPLE__ */
|
||||
{
|
||||
// Sla gizmo selects all support points
|
||||
if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::SelectAll))
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace GUI {
|
|||
class Selection;
|
||||
class GLGizmoBase;
|
||||
class GLCanvas3D;
|
||||
class ClippingPlane;
|
||||
|
||||
class Rect
|
||||
{
|
||||
|
@ -146,7 +147,7 @@ public:
|
|||
|
||||
void set_sla_support_data(ModelObject* model_object, const Selection& selection);
|
||||
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false, bool alt_down = false, bool control_down = false);
|
||||
|
||||
ClippingPlane get_sla_clipping_plane() const;
|
||||
|
||||
void render_current_gizmo(const Selection& selection) const;
|
||||
void render_current_gizmo_for_picking_pass(const Selection& selection) const;
|
||||
|
|
|
@ -321,7 +321,7 @@ void MainFrame::init_menubar()
|
|||
wxMenu* fileMenu = new wxMenu;
|
||||
{
|
||||
wxMenuItem* item_open = append_menu_item(fileMenu, wxID_ANY, _(L("&Open Project")) + dots + "\tCtrl+O", _(L("Open a project file")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "brick_add.png");
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "open");
|
||||
wxMenuItem* item_save = append_menu_item(fileMenu, wxID_ANY, _(L("&Save Project")) + "\tCtrl+S", _(L("Save current project file")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename())); }, "save");
|
||||
wxMenuItem* item_save_as = append_menu_item(fileMenu, wxID_ANY, _(L("Save Project &as")) + dots + "\tCtrl+Alt+S", _(L("Save current project file as")),
|
||||
|
@ -331,30 +331,30 @@ void MainFrame::init_menubar()
|
|||
|
||||
wxMenu* import_menu = new wxMenu();
|
||||
wxMenuItem* item_import_model = append_menu_item(import_menu, wxID_ANY, _(L("Import STL/OBJ/AM&F/3MF")) + dots + "\tCtrl+I", _(L("Load a model")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, "brick_add.png");
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, "import_plater");
|
||||
import_menu->AppendSeparator();
|
||||
append_menu_item(import_menu, wxID_ANY, _(L("Import &Config")) + dots + "\tCtrl+L", _(L("Load exported configuration file")),
|
||||
[this](wxCommandEvent&) { load_config_file(); }, "plugin_add.png");
|
||||
[this](wxCommandEvent&) { load_config_file(); }, "import_config");
|
||||
append_menu_item(import_menu, wxID_ANY, _(L("Import Config from &project")) + dots +"\tCtrl+Alt+L", _(L("Load configuration from project file")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->extract_config_from_project(); }, "plugin_add.png");
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->extract_config_from_project(); }, "import_config");
|
||||
import_menu->AppendSeparator();
|
||||
append_menu_item(import_menu, wxID_ANY, _(L("Import Config &Bundle")) + dots, _(L("Load presets from a bundle")),
|
||||
[this](wxCommandEvent&) { load_configbundle(); }, "lorry_add.png");
|
||||
[this](wxCommandEvent&) { load_configbundle(); }, "import_config_bundle");
|
||||
append_submenu(fileMenu, import_menu, wxID_ANY, _(L("&Import")), "");
|
||||
|
||||
wxMenu* export_menu = new wxMenu();
|
||||
wxMenuItem* item_export_gcode = append_menu_item(export_menu, wxID_ANY, _(L("Export &G-code")) + dots +"\tCtrl+G", _(L("Export current plate as G-code")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(); }, "cog_go.png");
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(); }, "export_gcode");
|
||||
export_menu->AppendSeparator();
|
||||
wxMenuItem* item_export_stl = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &STL")) + dots, _(L("Export current plate as STL")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, "brick_go.png");
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, "export_plater");
|
||||
wxMenuItem* item_export_amf = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &AMF")) + dots, _(L("Export current plate as AMF")),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, "brick_go.png");
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, "export_plater");
|
||||
export_menu->AppendSeparator();
|
||||
append_menu_item(export_menu, wxID_ANY, _(L("Export &Config")) +dots +"\tCtrl+E", _(L("Export current configuration to file")),
|
||||
[this](wxCommandEvent&) { export_config(); }, "plugin_go.png");
|
||||
[this](wxCommandEvent&) { export_config(); }, "export_config");
|
||||
append_menu_item(export_menu, wxID_ANY, _(L("Export Config &Bundle")) + dots, _(L("Export all presets to file")),
|
||||
[this](wxCommandEvent&) { export_configbundle(); }, "lorry_go.png");
|
||||
[this](wxCommandEvent&) { export_configbundle(); }, "export_config_bundle");
|
||||
append_submenu(fileMenu, export_menu, wxID_ANY, _(L("&Export")), "");
|
||||
|
||||
fileMenu->AppendSeparator();
|
||||
|
@ -382,10 +382,10 @@ void MainFrame::init_menubar()
|
|||
fileMenu->AppendSeparator();
|
||||
#endif
|
||||
m_menu_item_reslice_now = append_menu_item(fileMenu, wxID_ANY, _(L("(Re)Slice &Now")) + "\tCtrl+R", _(L("Start new slicing process")),
|
||||
[this](wxCommandEvent&) { reslice_now(); }, "shape_handles.png");
|
||||
[this](wxCommandEvent&) { reslice_now(); }, "re_slice");
|
||||
fileMenu->AppendSeparator();
|
||||
append_menu_item(fileMenu, wxID_ANY, _(L("&Repair STL file")) + dots, _(L("Automatically repair an STL file")),
|
||||
[this](wxCommandEvent&) { repair_stl(); }, "wrench.png");
|
||||
[this](wxCommandEvent&) { repair_stl(); }, "wrench");
|
||||
fileMenu->AppendSeparator();
|
||||
append_menu_item(fileMenu, wxID_EXIT, _(L("&Quit")), _(L("Quit Slic3r")),
|
||||
[this](wxCommandEvent&) { Close(false); });
|
||||
|
@ -425,13 +425,22 @@ void MainFrame::init_menubar()
|
|||
[this](wxCommandEvent&) { m_plater->select_all(); }, "");
|
||||
editMenu->AppendSeparator();
|
||||
wxMenuItem* item_delete_sel = append_menu_item(editMenu, wxID_ANY, _(L("&Delete selected")) + sep + hotkey_delete, _(L("Deletes the current selection")),
|
||||
[this](wxCommandEvent&) { m_plater->remove_selected(); }, "");
|
||||
[this](wxCommandEvent&) { m_plater->remove_selected(); }, "remove_menu");
|
||||
wxMenuItem* item_delete_all = append_menu_item(editMenu, wxID_ANY, _(L("Delete &all")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + hotkey_delete, _(L("Deletes all objects")),
|
||||
[this](wxCommandEvent&) { m_plater->reset(); }, "");
|
||||
[this](wxCommandEvent&) { m_plater->reset(); }, "delete_all_menu");
|
||||
|
||||
editMenu->AppendSeparator();
|
||||
|
||||
wxMenuItem* item_copy = append_menu_item(editMenu, wxID_ANY, _(L("&Copy")) + "\tCtrl+C", _(L("Copy selection to clipboard")),
|
||||
[this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); }, "copy_menu");
|
||||
wxMenuItem* item_paste = append_menu_item(editMenu, wxID_ANY, _(L("&Paste")) + "\tCtrl+V", _(L("Paste clipboard")),
|
||||
[this](wxCommandEvent&) { m_plater->paste_from_clipboard(); }, "paste_menu");
|
||||
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_select()); }, item_select_all->GetId());
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_delete()); }, item_delete_sel->GetId());
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_delete_all()); }, item_delete_all->GetId());
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_plater->can_copy()); }, item_copy->GetId());
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_plater->can_paste()); }, item_paste->GetId());
|
||||
}
|
||||
|
||||
// Window menu
|
||||
|
@ -440,7 +449,7 @@ void MainFrame::init_menubar()
|
|||
size_t tab_offset = 0;
|
||||
if (m_plater) {
|
||||
append_menu_item(windowMenu, wxID_HIGHEST + 1, _(L("&Plater Tab")) + "\tCtrl+1", _(L("Show the plater")),
|
||||
[this](wxCommandEvent&) { select_tab(0); }, "application_view_tile.png");
|
||||
[this](wxCommandEvent&) { select_tab(0); }, "plater");
|
||||
tab_offset += 1;
|
||||
}
|
||||
if (tab_offset > 0) {
|
||||
|
@ -455,9 +464,9 @@ void MainFrame::init_menubar()
|
|||
if (m_plater) {
|
||||
windowMenu->AppendSeparator();
|
||||
wxMenuItem* item_3d = append_menu_item(windowMenu, wxID_HIGHEST + 5, _(L("3&D")) + "\tCtrl+5", _(L("Show the 3D editing view")),
|
||||
[this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, "");
|
||||
[this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, "editor_menu");
|
||||
wxMenuItem* item_preview = append_menu_item(windowMenu, wxID_HIGHEST + 6, _(L("Pre&view")) + "\tCtrl+6", _(L("Show the 3D slices preview")),
|
||||
[this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, "");
|
||||
[this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, "preview_menu");
|
||||
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_3d->GetId());
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_preview->GetId());
|
||||
|
@ -478,7 +487,7 @@ void MainFrame::init_menubar()
|
|||
|
||||
windowMenu->AppendSeparator();
|
||||
append_menu_item(windowMenu, wxID_ANY, _(L("Print &Host Upload Queue")) + "\tCtrl+J", _(L("Display the Print Host Upload Queue window")),
|
||||
[this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "arrow_up.png");
|
||||
[this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue");
|
||||
}
|
||||
|
||||
// View menu
|
||||
|
@ -590,7 +599,7 @@ void MainFrame::quick_slice(const int qs)
|
|||
dlg->ShowModal();
|
||||
return;
|
||||
}
|
||||
if (std::ifstream(m_qs_last_input_file.char_str())) {
|
||||
if (std::ifstream(m_qs_last_input_file.ToUTF8().data())) {
|
||||
auto dlg = new wxMessageDialog(this, _(L("Previously sliced file ("))+m_qs_last_input_file+_(L(") not found.")),
|
||||
_(L("File Not Found")), wxICON_ERROR | wxOK);
|
||||
dlg->ShowModal();
|
||||
|
@ -705,24 +714,23 @@ void MainFrame::repair_stl()
|
|||
dlg->Destroy();
|
||||
}
|
||||
|
||||
auto output_file = input_file;
|
||||
wxString output_file = input_file;
|
||||
{
|
||||
// output_file = ~s / \.[sS][tT][lL]$ / _fixed.obj / ;
|
||||
auto dlg = new wxFileDialog( this, L("Save OBJ file (less prone to coordinate errors than STL) as:"),
|
||||
get_dir_name(output_file), get_base_name(output_file),
|
||||
get_dir_name(output_file), get_base_name(output_file, ".obj"),
|
||||
file_wildcards(FT_OBJ), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
if (dlg->ShowModal() != wxID_OK) {
|
||||
dlg->Destroy();
|
||||
return /*undef*/;
|
||||
return;
|
||||
}
|
||||
output_file = dlg->GetPath();
|
||||
dlg->Destroy();
|
||||
}
|
||||
|
||||
auto tmesh = new Slic3r::TriangleMesh();
|
||||
tmesh->ReadSTLFile(input_file.char_str());
|
||||
tmesh->ReadSTLFile(input_file.ToUTF8().data());
|
||||
tmesh->repair();
|
||||
tmesh->WriteOBJFile(output_file.char_str());
|
||||
tmesh->WriteOBJFile(output_file.ToUTF8().data());
|
||||
Slic3r::GUI::show_info(this, L("Your file was repaired."), L("Repair"));
|
||||
}
|
||||
|
||||
|
@ -962,9 +970,12 @@ void MainFrame::update_ui_from_settings()
|
|||
tab->update_ui_from_settings();
|
||||
}
|
||||
|
||||
std::string MainFrame::get_base_name(const wxString &full_name) const
|
||||
std::string MainFrame::get_base_name(const wxString &full_name, const char *extension) const
|
||||
{
|
||||
return boost::filesystem::path(full_name.wx_str()).filename().string();
|
||||
boost::filesystem::path filename = boost::filesystem::path(full_name.wx_str()).filename();
|
||||
if (extension != nullptr)
|
||||
filename = filename.replace_extension(extension);
|
||||
return filename.string();
|
||||
}
|
||||
|
||||
std::string MainFrame::get_dir_name(const wxString &full_name) const
|
||||
|
|
|
@ -54,7 +54,7 @@ class MainFrame : public DPIFrame
|
|||
|
||||
PrintHostQueueDialog *m_printhost_queue_dlg;
|
||||
|
||||
std::string get_base_name(const wxString &full_name) const;
|
||||
std::string get_base_name(const wxString &full_name, const char *extension = nullptr) const;
|
||||
std::string get_dir_name(const wxString &full_name) const;
|
||||
|
||||
void on_presets_changed(SimpleEvent&);
|
||||
|
|
|
@ -144,8 +144,7 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
|
|||
info_manifold_text->SetFont(wxGetApp().small_font());
|
||||
info_manifold = new wxStaticText(parent, wxID_ANY, "");
|
||||
info_manifold->SetFont(wxGetApp().small_font());
|
||||
wxBitmap bitmap(GUI::from_u8(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
|
||||
manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, bitmap);
|
||||
manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap(parent, "exclamation"));
|
||||
auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer_manifold->Add(info_manifold_text, 0);
|
||||
sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2);
|
||||
|
@ -1477,6 +1476,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); });
|
||||
view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE_ALL, [this](SimpleEvent&) { reset(); });
|
||||
view3D_canvas->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { arrange(); });
|
||||
view3D_canvas->Bind(EVT_GLTOOLBAR_COPY, [q](SimpleEvent&) { q->copy_selection_to_clipboard(); });
|
||||
view3D_canvas->Bind(EVT_GLTOOLBAR_PASTE, [q](SimpleEvent&) { q->paste_from_clipboard(); });
|
||||
view3D_canvas->Bind(EVT_GLTOOLBAR_MORE, [q](SimpleEvent&) { q->increase_instances(); });
|
||||
view3D_canvas->Bind(EVT_GLTOOLBAR_FEWER, [q](SimpleEvent&) { q->decrease_instances(); });
|
||||
view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_OBJECTS, &priv::on_action_split_objects, this);
|
||||
|
@ -1719,11 +1720,11 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
|
||||
if (advanced)
|
||||
{
|
||||
wxMessageDialog dlg(q, _(L("This file cannot be loaded in simple mode. Do you want to switch to expert mode?\n")),
|
||||
wxMessageDialog dlg(q, _(L("This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode?\n")),
|
||||
_(L("Detected advanced data")), wxICON_WARNING | wxYES | wxNO);
|
||||
if (dlg.ShowModal() == wxID_YES)
|
||||
{
|
||||
Slic3r::GUI::wxGetApp().save_mode(comExpert);
|
||||
Slic3r::GUI::wxGetApp().save_mode(comAdvanced);
|
||||
view3D->set_as_dirty();
|
||||
}
|
||||
else
|
||||
|
@ -2669,25 +2670,21 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
|
|||
this->statusbar()->set_progress(evt.status.percent);
|
||||
this->statusbar()->set_status_text(_(L(evt.status.text)) + wxString::FromUTF8("…"));
|
||||
}
|
||||
if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SCENE) {
|
||||
if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE || PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) {
|
||||
switch (this->printer_technology) {
|
||||
case ptFFF:
|
||||
this->update_fff_scene();
|
||||
break;
|
||||
case ptSLA:
|
||||
// If RELOAD_SLA_SUPPORT_POINTS, then the SLA gizmo is updated (reload_scene calls update_gizmos_data)
|
||||
if (view3D->is_dragging())
|
||||
delayed_scene_refresh = true;
|
||||
else
|
||||
this->update_sla_scene();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS) {
|
||||
// Update SLA gizmo (reload_scene calls update_gizmos_data)
|
||||
q->canvas3D()->reload_scene(true);
|
||||
}
|
||||
if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_PREVIEW) {
|
||||
// Update the SLA preview
|
||||
} else if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_PREVIEW) {
|
||||
// Update the SLA preview. Only called if not RELOAD_SLA_SUPPORT_POINTS, as the block above will refresh the preview anyways.
|
||||
this->preview->reload_print();
|
||||
}
|
||||
}
|
||||
|
@ -2895,17 +2892,17 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
|
|||
wxMenuItem* item_delete = nullptr;
|
||||
if (is_part) {
|
||||
item_delete = append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")),
|
||||
[this](wxCommandEvent&) { q->remove_selected(); }, "remove");
|
||||
[this](wxCommandEvent&) { q->remove_selected(); }, "delete");
|
||||
|
||||
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")),
|
||||
[this](wxCommandEvent&) { q->increase_instances(); }, "instance_add");
|
||||
[this](wxCommandEvent&) { q->increase_instances(); }, "add_copies");
|
||||
wxMenuItem* item_decrease = append_menu_item(menu, wxID_ANY, _(L("Decrease copies")) + "\t-", _(L("Remove one copy of the selected object")),
|
||||
[this](wxCommandEvent&) { q->decrease_instances(); }, "instance_remove");
|
||||
[this](wxCommandEvent&) { q->decrease_instances(); }, "remove_copies");
|
||||
wxMenuItem* item_set_number_of_copies = append_menu_item(menu, wxID_ANY, _(L("Set number of copies")) + dots, _(L("Change the number of copies of the selected object")),
|
||||
[this](wxCommandEvent&) { q->set_number_of_copies(); }, "textfield.png");
|
||||
[this](wxCommandEvent&) { q->set_number_of_copies(); }, "number_of_copies");
|
||||
|
||||
items_increase.push_back(item_increase);
|
||||
items_decrease.push_back(item_decrease);
|
||||
|
@ -2913,7 +2910,7 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
|
|||
|
||||
// Delete menu was moved to be after +/- instace to make it more difficult to be selected by mistake.
|
||||
item_delete = append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")),
|
||||
[this](wxCommandEvent&) { q->remove_selected(); }, "remove");
|
||||
[this](wxCommandEvent&) { q->remove_selected(); }, "delete");
|
||||
|
||||
menu->AppendSeparator();
|
||||
wxMenuItem* item_instance_to_object = sidebar->obj_list()->append_menu_item_instance_to_object(menu);
|
||||
|
@ -2943,11 +2940,11 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
|
|||
return false;
|
||||
|
||||
append_menu_item(mirror_menu, wxID_ANY, _(L("Along X axis")), _(L("Mirror the selected object along the X axis")),
|
||||
[this](wxCommandEvent&) { mirror(X); }, "bullet_red.png", menu);
|
||||
[this](wxCommandEvent&) { mirror(X); }, "mark_X", menu);
|
||||
append_menu_item(mirror_menu, wxID_ANY, _(L("Along Y axis")), _(L("Mirror the selected object along the Y axis")),
|
||||
[this](wxCommandEvent&) { mirror(Y); }, "bullet_green.png", menu);
|
||||
[this](wxCommandEvent&) { mirror(Y); }, "mark_Y", menu);
|
||||
append_menu_item(mirror_menu, wxID_ANY, _(L("Along Z axis")), _(L("Mirror the selected object along the Z axis")),
|
||||
[this](wxCommandEvent&) { mirror(Z); }, "bullet_blue.png", menu);
|
||||
[this](wxCommandEvent&) { mirror(Z); }, "mark_Z", menu);
|
||||
|
||||
wxMenuItem* item_mirror = append_submenu(menu, mirror_menu, wxID_ANY, _(L("Mirror")), _(L("Mirror the selected object")));
|
||||
|
||||
|
@ -2968,9 +2965,9 @@ bool Plater::priv::complit_init_object_menu()
|
|||
return false;
|
||||
|
||||
wxMenuItem* item_split_objects = append_menu_item(split_menu, wxID_ANY, _(L("To objects")), _(L("Split the selected object into individual objects")),
|
||||
[this](wxCommandEvent&) { split_object(); }, "split_objects.png", &object_menu);
|
||||
[this](wxCommandEvent&) { split_object(); }, "split_object_SMALL", &object_menu);
|
||||
wxMenuItem* item_split_volumes = append_menu_item(split_menu, wxID_ANY, _(L("To parts")), _(L("Split the selected object into individual sub-parts")),
|
||||
[this](wxCommandEvent&) { split_volume(); }, "split_parts.png", &object_menu);
|
||||
[this](wxCommandEvent&) { split_volume(); }, "split_parts_SMALL", &object_menu);
|
||||
|
||||
wxMenuItem* item_split = append_submenu(&object_menu, split_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object"))/*, "shape_ungroup.png"*/);
|
||||
object_menu.AppendSeparator();
|
||||
|
@ -2990,7 +2987,7 @@ bool Plater::priv::complit_init_object_menu()
|
|||
bool Plater::priv::complit_init_sla_object_menu()
|
||||
{
|
||||
wxMenuItem* item_split = append_menu_item(&sla_object_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object into individual objects")),
|
||||
[this](wxCommandEvent&) { split_object(); }, "shape_ungroup_o.png");
|
||||
[this](wxCommandEvent&) { split_object(); }, "split_object_SMALL");
|
||||
|
||||
sla_object_menu.AppendSeparator();
|
||||
|
||||
|
@ -3010,7 +3007,7 @@ bool Plater::priv::complit_init_sla_object_menu()
|
|||
bool Plater::priv::complit_init_part_menu()
|
||||
{
|
||||
wxMenuItem* item_split = append_menu_item(&part_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object into individual sub-parts")),
|
||||
[this](wxCommandEvent&) { split_volume(); }, "shape_ungroup_p.png");
|
||||
[this](wxCommandEvent&) { split_volume(); }, "split_parts_SMALL");
|
||||
|
||||
part_menu.AppendSeparator();
|
||||
|
||||
|
@ -3117,7 +3114,7 @@ void Plater::priv::set_bed_shape(const Pointfs& shape)
|
|||
|
||||
bool Plater::priv::can_delete() const
|
||||
{
|
||||
return !get_selection().is_empty();
|
||||
return !get_selection().is_empty() && !get_selection().is_wipe_tower();
|
||||
}
|
||||
|
||||
bool Plater::priv::can_delete_all() const
|
||||
|
@ -3299,8 +3296,9 @@ void Plater::increase_instances(size_t num)
|
|||
|
||||
bool was_one_instance = model_object->instances.size()==1;
|
||||
|
||||
float offset = 10.0;
|
||||
for (size_t i = 0; i < num; i++, offset += 10.0) {
|
||||
double offset_base = canvas3D()->get_size_proportional_to_max_bed_size(0.05);
|
||||
double offset = offset_base;
|
||||
for (size_t i = 0; i < num; i++, offset += offset_base) {
|
||||
Vec3d offset_vec = model_instance->get_offset() + Vec3d(offset, offset, 0.0);
|
||||
model_object->add_instance(offset_vec, model_instance->get_scaling_factor(), model_instance->get_rotation(), model_instance->get_mirror());
|
||||
// p->print.get_object(obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
|
||||
|
@ -3368,7 +3366,7 @@ void Plater::set_number_of_copies(/*size_t num*/)
|
|||
|
||||
bool Plater::is_selection_empty() const
|
||||
{
|
||||
return p->get_selection().is_empty();
|
||||
return p->get_selection().is_empty() || p->get_selection().is_wipe_tower();
|
||||
}
|
||||
|
||||
void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper, bool keep_lower, bool rotate_lower)
|
||||
|
@ -3771,10 +3769,43 @@ void Plater::changed_object(int obj_idx)
|
|||
this->p->schedule_background_process();
|
||||
}
|
||||
|
||||
void Plater::schedule_background_process()
|
||||
{
|
||||
this->p->schedule_background_process();
|
||||
}
|
||||
|
||||
void Plater::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) { p->fix_through_netfabb(obj_idx, vol_idx); }
|
||||
|
||||
void Plater::update_object_menu() { p->update_object_menu(); }
|
||||
|
||||
void Plater::copy_selection_to_clipboard()
|
||||
{
|
||||
p->view3D->get_canvas3d()->get_selection().copy_to_clipboard();
|
||||
}
|
||||
|
||||
void Plater::paste_from_clipboard()
|
||||
{
|
||||
p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
|
||||
}
|
||||
|
||||
bool Plater::can_paste_from_clipboard() const
|
||||
{
|
||||
const Selection& selection = p->view3D->get_canvas3d()->get_selection();
|
||||
const Selection::Clipboard& clipboard = selection.get_clipboard();
|
||||
Selection::EMode mode = clipboard.get_mode();
|
||||
|
||||
if (clipboard.is_empty())
|
||||
return false;
|
||||
|
||||
if ((mode == Selection::Volume) && !selection.is_from_single_instance())
|
||||
return false;
|
||||
|
||||
if ((mode == Selection::Instance) && (selection.get_mode() != Selection::Instance))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Plater::can_delete() const { return p->can_delete(); }
|
||||
bool Plater::can_delete_all() const { return p->can_delete_all(); }
|
||||
bool Plater::can_increase_instances() const { return p->can_increase_instances(); }
|
||||
|
@ -3783,5 +3814,7 @@ bool Plater::can_split_to_objects() const { return p->can_split_to_objects(); }
|
|||
bool Plater::can_split_to_volumes() const { return p->can_split_to_volumes(); }
|
||||
bool Plater::can_arrange() const { return p->can_arrange(); }
|
||||
bool Plater::can_layers_editing() const { return p->can_layers_editing(); }
|
||||
bool Plater::can_copy() const { return !is_selection_empty(); }
|
||||
bool Plater::can_paste() const { return can_paste_from_clipboard(); }
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
|
|
@ -167,6 +167,7 @@ public:
|
|||
void reslice();
|
||||
void reslice_SLA_supports(const ModelObject &object);
|
||||
void changed_object(int obj_idx);
|
||||
void schedule_background_process();
|
||||
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
|
||||
void send_gcode();
|
||||
|
||||
|
@ -187,6 +188,10 @@ public:
|
|||
PrinterTechnology printer_technology() const;
|
||||
void set_printer_technology(PrinterTechnology printer_technology);
|
||||
|
||||
void copy_selection_to_clipboard();
|
||||
void paste_from_clipboard();
|
||||
bool can_paste_from_clipboard() const;
|
||||
|
||||
bool can_delete() const;
|
||||
bool can_delete_all() const;
|
||||
bool can_increase_instances() const;
|
||||
|
@ -195,6 +200,8 @@ public:
|
|||
bool can_split_to_volumes() const;
|
||||
bool can_arrange() const;
|
||||
bool can_layers_editing() const;
|
||||
bool can_copy() const;
|
||||
bool can_paste() const;
|
||||
|
||||
private:
|
||||
struct priv;
|
||||
|
|
|
@ -801,12 +801,16 @@ bool PresetCollection::delete_current_preset()
|
|||
|
||||
void PresetCollection::load_bitmap_default(wxWindow *window, const std::string &file_name)
|
||||
{
|
||||
*m_bitmap_main_frame = create_scaled_bitmap(window, file_name);
|
||||
// XXX: See note in PresetBundle::load_compatible_bitmaps()
|
||||
(void)window;
|
||||
*m_bitmap_main_frame = create_scaled_bitmap(nullptr, file_name);
|
||||
}
|
||||
|
||||
void PresetCollection::load_bitmap_add(wxWindow *window, const std::string &file_name)
|
||||
{
|
||||
*m_bitmap_add = create_scaled_bitmap(window, file_name);
|
||||
// XXX: See note in PresetBundle::load_compatible_bitmaps()
|
||||
(void)window;
|
||||
*m_bitmap_add = create_scaled_bitmap(nullptr, file_name);
|
||||
}
|
||||
|
||||
const Preset* PresetCollection::get_selected_preset_parent() const
|
||||
|
|
|
@ -398,22 +398,27 @@ void PresetBundle::export_selections(AppConfig &config)
|
|||
|
||||
void PresetBundle::load_compatible_bitmaps(wxWindow *window)
|
||||
{
|
||||
*m_bitmapCompatible = create_scaled_bitmap(window, "flag_green");
|
||||
*m_bitmapIncompatible = create_scaled_bitmap(window, "flag_red");
|
||||
*m_bitmapLock = create_scaled_bitmap(window, "lock_closed");
|
||||
*m_bitmapLockOpen = create_scaled_bitmap(window, "sys_unlock.png");
|
||||
// We don't actually pass the window pointer here and instead generate
|
||||
// a low DPI bitmap, because the wxBitmapComboBox and wxDataViewCtrl don't support
|
||||
// high DPI bitmaps very well, they compute their dimensions wrong.
|
||||
// TODO: Update this when fixed in wxWidgets
|
||||
// See also PresetCollection::load_bitmap_default() and PresetCollection::load_bitmap_add()
|
||||
|
||||
(void)window;
|
||||
*m_bitmapCompatible = create_scaled_bitmap(nullptr, "flag_green");
|
||||
*m_bitmapIncompatible = create_scaled_bitmap(nullptr, "flag_red");
|
||||
*m_bitmapLock = create_scaled_bitmap(nullptr, "lock_closed");
|
||||
*m_bitmapLockOpen = create_scaled_bitmap(nullptr, "sys_unlock.png");
|
||||
|
||||
prints .set_bitmap_compatible(m_bitmapCompatible);
|
||||
filaments .set_bitmap_compatible(m_bitmapCompatible);
|
||||
sla_prints .set_bitmap_compatible(m_bitmapCompatible);
|
||||
sla_materials.set_bitmap_compatible(m_bitmapCompatible);
|
||||
printers .set_bitmap_compatible(m_bitmapCompatible);
|
||||
|
||||
prints .set_bitmap_incompatible(m_bitmapIncompatible);
|
||||
filaments .set_bitmap_incompatible(m_bitmapIncompatible);
|
||||
sla_prints .set_bitmap_incompatible(m_bitmapIncompatible);
|
||||
sla_materials.set_bitmap_incompatible(m_bitmapIncompatible);
|
||||
printers .set_bitmap_incompatible(m_bitmapIncompatible);
|
||||
|
||||
prints .set_bitmap_lock(m_bitmapLock);
|
||||
filaments .set_bitmap_lock(m_bitmapLock);
|
||||
|
|
|
@ -1022,6 +1022,77 @@ bool Selection::requires_local_axes() const
|
|||
return (m_mode == Volume) && is_from_single_instance();
|
||||
}
|
||||
|
||||
void Selection::copy_to_clipboard()
|
||||
{
|
||||
if (!m_valid)
|
||||
return;
|
||||
|
||||
m_clipboard.reset();
|
||||
|
||||
for (const ObjectIdxsToInstanceIdxsMap::value_type& object : m_cache.content)
|
||||
{
|
||||
ModelObject* src_object = m_model->objects[object.first];
|
||||
ModelObject* dst_object = m_clipboard.add_object();
|
||||
dst_object->name = src_object->name;
|
||||
dst_object->input_file = src_object->input_file;
|
||||
dst_object->config = src_object->config;
|
||||
dst_object->sla_support_points = src_object->sla_support_points;
|
||||
dst_object->sla_points_status = src_object->sla_points_status;
|
||||
dst_object->layer_height_ranges = src_object->layer_height_ranges;
|
||||
dst_object->layer_height_profile = src_object->layer_height_profile;
|
||||
dst_object->origin_translation = src_object->origin_translation;
|
||||
|
||||
for (int i : object.second)
|
||||
{
|
||||
dst_object->add_instance(*src_object->instances[i]);
|
||||
}
|
||||
|
||||
for (unsigned int i : m_list)
|
||||
{
|
||||
// Copy the ModelVolumes only for the selected GLVolumes of the 1st selected instance.
|
||||
const GLVolume* volume = (*m_volumes)[i];
|
||||
if ((volume->object_idx() == object.first) && (volume->instance_idx() == *object.second.begin()))
|
||||
{
|
||||
int volume_idx = volume->volume_idx();
|
||||
if ((0 <= volume_idx) && (volume_idx < (int)src_object->volumes.size()))
|
||||
{
|
||||
ModelVolume* src_volume = src_object->volumes[volume_idx];
|
||||
ModelVolume* dst_volume = dst_object->add_volume(*src_volume);
|
||||
dst_volume->set_new_unique_id();
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_clipboard.set_mode(m_mode);
|
||||
}
|
||||
|
||||
void Selection::paste_from_clipboard()
|
||||
{
|
||||
if (!m_valid || m_clipboard.is_empty())
|
||||
return;
|
||||
|
||||
switch (m_clipboard.get_mode())
|
||||
{
|
||||
case Volume:
|
||||
{
|
||||
if (is_from_single_instance())
|
||||
paste_volumes_from_clipboard();
|
||||
|
||||
break;
|
||||
}
|
||||
case Instance:
|
||||
{
|
||||
if (m_mode == Instance)
|
||||
paste_objects_from_clipboard();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Selection::update_valid()
|
||||
{
|
||||
m_valid = (m_volumes != nullptr) && (m_model != nullptr);
|
||||
|
@ -1697,5 +1768,44 @@ bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const
|
|||
return count == (unsigned int)m_model->objects[object_idx]->volumes.size();
|
||||
}
|
||||
|
||||
void Selection::paste_volumes_from_clipboard()
|
||||
{
|
||||
int obj_idx = get_object_idx();
|
||||
if ((obj_idx < 0) || ((int)m_model->objects.size() <= obj_idx))
|
||||
return;
|
||||
|
||||
ModelObject* src_object = m_clipboard.get_object(0);
|
||||
if (src_object != nullptr)
|
||||
{
|
||||
ModelObject* dst_object = m_model->objects[obj_idx];
|
||||
|
||||
ModelVolumePtrs volumes;
|
||||
for (ModelVolume* src_volume : src_object->volumes)
|
||||
{
|
||||
ModelVolume* dst_volume = dst_object->add_volume(*src_volume);
|
||||
dst_volume->set_new_unique_id();
|
||||
double offset = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.05);
|
||||
dst_volume->translate(offset, offset, 0.0);
|
||||
volumes.push_back(dst_volume);
|
||||
}
|
||||
wxGetApp().obj_list()->paste_volumes_into_list(obj_idx, volumes);
|
||||
}
|
||||
}
|
||||
|
||||
void Selection::paste_objects_from_clipboard()
|
||||
{
|
||||
std::vector<size_t> object_idxs;
|
||||
const ModelObjectPtrs& src_objects = m_clipboard.get_objects();
|
||||
for (const ModelObject* src_object : src_objects)
|
||||
{
|
||||
ModelObject* dst_object = m_model->add_object(*src_object);
|
||||
double offset = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.05);
|
||||
dst_object->translate(offset, offset, 0.0);
|
||||
object_idxs.push_back(m_model->objects.size() - 1);
|
||||
}
|
||||
|
||||
wxGetApp().obj_list()->paste_objects_into_list(object_idxs);
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -138,6 +138,23 @@ public:
|
|||
typedef std::set<int> InstanceIdxsList;
|
||||
typedef std::map<int, InstanceIdxsList> ObjectIdxsToInstanceIdxsMap;
|
||||
|
||||
class Clipboard
|
||||
{
|
||||
Model m_model;
|
||||
Selection::EMode m_mode;
|
||||
|
||||
public:
|
||||
void reset() { m_model.clear_objects(); }
|
||||
bool is_empty() const { return m_model.objects.empty(); }
|
||||
|
||||
ModelObject* add_object() { return m_model.add_object(); }
|
||||
ModelObject* get_object(unsigned int id) { return (id < (unsigned int)m_model.objects.size()) ? m_model.objects[id] : nullptr; }
|
||||
const ModelObjectPtrs& get_objects() const { return m_model.objects; }
|
||||
|
||||
Selection::EMode get_mode() const { return m_mode; }
|
||||
void set_mode(Selection::EMode mode) { m_mode = mode; }
|
||||
};
|
||||
|
||||
private:
|
||||
struct Cache
|
||||
{
|
||||
|
@ -163,6 +180,7 @@ private:
|
|||
// set of indices to m_volumes
|
||||
IndicesList m_list;
|
||||
Cache m_cache;
|
||||
Clipboard m_clipboard;
|
||||
mutable BoundingBoxf3 m_bounding_box;
|
||||
mutable bool m_bounding_box_dirty;
|
||||
|
||||
|
@ -267,6 +285,11 @@ public:
|
|||
|
||||
bool requires_local_axes() const;
|
||||
|
||||
void copy_to_clipboard();
|
||||
void paste_from_clipboard();
|
||||
|
||||
const Clipboard& get_clipboard() const { return m_clipboard; }
|
||||
|
||||
private:
|
||||
void update_valid();
|
||||
void update_type();
|
||||
|
@ -301,6 +324,9 @@ private:
|
|||
void synchronize_unselected_volumes();
|
||||
void ensure_on_bed();
|
||||
bool is_from_fully_selected_instance(unsigned int volume_idx) const;
|
||||
|
||||
void paste_volumes_from_clipboard();
|
||||
void paste_objects_from_clipboard();
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
@ -165,10 +165,13 @@ void Tab::create_preset_tab()
|
|||
add_scaled_bitmap(this, m_bmp_value_unlock, "lock_open");
|
||||
m_bmp_non_system = &m_bmp_white_bullet;
|
||||
// Bitmaps to be shown on the "Undo user changes" button next to each input field.
|
||||
// m_bmp_value_revert = create_scaled_bitmap(this, "undo");
|
||||
// m_bmp_white_bullet = create_scaled_bitmap(this, luma >= 128 ? "dot" : "dot_white"/*"bullet_white.png"*/);
|
||||
// m_bmp_question = create_scaled_bitmap(this, "question");
|
||||
// m_bmp_value_revert = create_scaled_bitmap(this, "undo");
|
||||
// m_bmp_white_bullet = create_scaled_bitmap(this, "bullet_white.png");
|
||||
add_scaled_bitmap(this, m_bmp_value_revert, "undo");
|
||||
add_scaled_bitmap(this, m_bmp_white_bullet, "bullet_white.png");
|
||||
add_scaled_bitmap(this, m_bmp_white_bullet, luma >= 128 ? "dot" : "dot_white"/*"bullet_white.png"*/);
|
||||
|
||||
fill_icon_descriptions();
|
||||
set_tooltips_text();
|
||||
|
@ -1163,7 +1166,7 @@ void TabPrint::build()
|
|||
optgroup = page->new_optgroup(_(L("Advanced")));
|
||||
optgroup->append_single_option_line("interface_shells");
|
||||
|
||||
page = add_options_page(_(L("Advanced")), "wrench.png");
|
||||
page = add_options_page(_(L("Advanced")), "wrench");
|
||||
optgroup = page->new_optgroup(_(L("Extrusion width")));
|
||||
optgroup->append_single_option_line("extrusion_width");
|
||||
optgroup->append_single_option_line("first_layer_extrusion_width");
|
||||
|
@ -1536,7 +1539,7 @@ void TabFilament::build()
|
|||
optgroup->append_single_option_line("slowdown_below_layer_time");
|
||||
optgroup->append_single_option_line("min_print_speed");
|
||||
|
||||
page = add_options_page(_(L("Advanced")), "wrench.png");
|
||||
page = add_options_page(_(L("Advanced")), "wrench");
|
||||
optgroup = page->new_optgroup(_(L("Filament properties")));
|
||||
optgroup->append_single_option_line("filament_type");
|
||||
optgroup->append_single_option_line("filament_soluble");
|
||||
|
@ -1702,12 +1705,17 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
|
|||
}
|
||||
|
||||
auto printhost_browse = [=](wxWindow* parent) {
|
||||
// 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->SetBitmap(create_scaled_bitmap(this, "browse"));
|
||||
// auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse ")) + dots,
|
||||
// wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
|
||||
// btn->SetBitmap(create_scaled_bitmap(this, "zoom.png"));
|
||||
add_scaled_button(parent, &m_printhost_browse_btn, "zoom.png", _(L(" Browse ")) + dots, wxBU_LEFT | wxBU_EXACTFIT);
|
||||
add_scaled_button(parent, &m_printhost_browse_btn, "browse", _(L(" Browse ")) + dots, wxBU_LEFT | wxBU_EXACTFIT);
|
||||
PrusaButton* btn = m_printhost_browse_btn;
|
||||
btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
||||
btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
||||
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn);
|
||||
|
||||
|
@ -1722,6 +1730,11 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
|
|||
return sizer;
|
||||
};
|
||||
|
||||
auto print_host_test = [this](wxWindow* parent) {
|
||||
auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
|
||||
wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
|
||||
btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
||||
btn->SetBitmap(create_scaled_bitmap(this, "test"));
|
||||
auto print_host_test = [this](wxWindow* parent) {
|
||||
// auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
|
||||
// wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
|
||||
|
@ -1765,7 +1778,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
|
|||
auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) {
|
||||
auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||
btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
||||
btn->SetBitmap(create_scaled_bitmap(this, "zoom.png"));
|
||||
btn->SetBitmap(create_scaled_bitmap(this, "browse"));
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn);
|
||||
|
||||
|
@ -3330,7 +3343,8 @@ void TabSLAMaterial::build()
|
|||
optgroup = page->new_optgroup(_(L("Corrections")));
|
||||
optgroup->label_width = 19;//190;
|
||||
std::vector<std::string> corrections = {"material_correction"};
|
||||
std::vector<std::string> axes{ "X", "Y", "Z" };
|
||||
// std::vector<std::string> axes{ "X", "Y", "Z" };
|
||||
std::vector<std::string> axes{ "XY", "Z" };
|
||||
for (auto& opt_key : corrections) {
|
||||
auto line = Line{ m_config->def()->get(opt_key)->full_label, "" };
|
||||
int id = 0;
|
||||
|
@ -3413,7 +3427,7 @@ void TabSLAPrint::build()
|
|||
optgroup->append_single_option_line("layer_height");
|
||||
optgroup->append_single_option_line("faded_layers");
|
||||
|
||||
page = add_options_page(_(L("Supports")), "sla_supports");
|
||||
page = add_options_page(_(L("Supports")), "support"/*"sla_supports"*/);
|
||||
optgroup = page->new_optgroup(_(L("Supports")));
|
||||
optgroup->append_single_option_line("supports_enable");
|
||||
|
||||
|
@ -3441,7 +3455,7 @@ void TabSLAPrint::build()
|
|||
optgroup->append_single_option_line("support_points_density_relative");
|
||||
optgroup->append_single_option_line("support_points_minimal_distance");
|
||||
|
||||
page = add_options_page(_(L("Pad")), "brick.png");
|
||||
page = add_options_page(_(L("Pad")), "pad");
|
||||
optgroup = page->new_optgroup(_(L("Pad")));
|
||||
optgroup->append_single_option_line("pad_enable");
|
||||
optgroup->append_single_option_line("pad_wall_thickness");
|
||||
|
|
|
@ -461,10 +461,10 @@ wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in, con
|
|||
// ----------------------------------------------------------------------------
|
||||
|
||||
void PrusaObjectDataViewModelNode::set_object_action_icon() {
|
||||
m_action_icon = create_scaled_bitmap(nullptr, "add_object.png"); // FIXME: pass window ptr
|
||||
m_action_icon = create_scaled_bitmap(nullptr, "advanced_plus"); // FIXME: pass window ptr
|
||||
}
|
||||
void PrusaObjectDataViewModelNode::set_part_action_icon() {
|
||||
m_action_icon = create_scaled_bitmap(nullptr, m_type == itVolume ? "cog.png" : "brick_go.png"); // FIXME: pass window ptr
|
||||
m_action_icon = create_scaled_bitmap(nullptr, m_type == itVolume ? "cog" : "set_separate_obj"); // FIXME: pass window ptr
|
||||
}
|
||||
|
||||
Slic3r::GUI::BitmapCache *m_bitmap_cache = nullptr;
|
||||
|
@ -529,10 +529,10 @@ wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name, const int ext
|
|||
}
|
||||
|
||||
wxDataViewItem PrusaObjectDataViewModel::AddVolumeChild(const wxDataViewItem &parent_item,
|
||||
const wxString &name,
|
||||
const Slic3r::ModelVolumeType volume_type,
|
||||
const int extruder/* = 0*/,
|
||||
const bool create_frst_child/* = true*/)
|
||||
const wxString &name,
|
||||
const Slic3r::ModelVolumeType volume_type,
|
||||
const int extruder/* = 0*/,
|
||||
const bool create_frst_child/* = true*/)
|
||||
{
|
||||
PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID();
|
||||
if (!root) return wxDataViewItem(0);
|
||||
|
@ -545,7 +545,7 @@ wxDataViewItem PrusaObjectDataViewModel::AddVolumeChild(const wxDataViewItem &pa
|
|||
insert_position = -1;
|
||||
|
||||
if (create_frst_child && root->m_volumes_cnt == 0)
|
||||
{
|
||||
{
|
||||
const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, *m_volume_bmps[0], extruder_str, 0);
|
||||
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
|
||||
// notify control
|
||||
|
|
|
@ -454,12 +454,12 @@ public:
|
|||
~PrusaObjectDataViewModel();
|
||||
|
||||
wxDataViewItem Add(const wxString &name, const int extruder);
|
||||
wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item,
|
||||
const wxString &name,
|
||||
const Slic3r::ModelVolumeType volume_type,
|
||||
const int extruder = 0,
|
||||
const bool create_frst_child = true);
|
||||
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
|
||||
wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item,
|
||||
const wxString &name,
|
||||
const Slic3r::ModelVolumeType volume_type,
|
||||
const int extruder = 0,
|
||||
const bool create_frst_child = true);
|
||||
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
|
||||
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num);
|
||||
wxDataViewItem Delete(const wxDataViewItem &item);
|
||||
wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num);
|
||||
|
|
|
@ -6,14 +6,20 @@
|
|||
#include <Windows.h>
|
||||
#include <shellapi.h>
|
||||
#include <wchar.h>
|
||||
// Let the NVIDIA and AMD know we want to use their graphics card
|
||||
// on a dual graphics card system.
|
||||
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
|
||||
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
// Let the NVIDIA and AMD know we want to use their graphics card
|
||||
// on a dual graphics card system.
|
||||
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
|
||||
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
#endif /* SLIC3R_GUI */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <GL/GL.h>
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
#include <GL/GL.h>
|
||||
#endif /* SLIC3R_GUI */
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -23,6 +29,7 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
class OpenGLVersionCheck
|
||||
{
|
||||
public:
|
||||
|
@ -188,6 +195,7 @@ protected:
|
|||
};
|
||||
|
||||
bool OpenGLVersionCheck::message_pump_exit = false;
|
||||
#endif /* SLIC3R_GUI */
|
||||
|
||||
extern "C" {
|
||||
typedef int (__stdcall *Slic3rMainFunc)(int argc, wchar_t **argv);
|
||||
|
@ -206,18 +214,33 @@ int wmain(int argc, wchar_t **argv)
|
|||
|
||||
std::vector<wchar_t*> argv_extended;
|
||||
argv_extended.emplace_back(argv[0]);
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
// Here one may push some additional parameters based on the wrapper type.
|
||||
for (int i = 1; i < argc; ++ i)
|
||||
bool force_mesa = false;
|
||||
#endif /* SLIC3R_GUI */
|
||||
for (int i = 1; i < argc; ++ i) {
|
||||
#ifdef SLIC3R_GUI
|
||||
if (wcscmp(argv[i], L"--sw-renderer") == 0)
|
||||
force_mesa = true;
|
||||
else if (wcscmp(argv[i], L"--no-sw-renderer") == 0)
|
||||
force_mesa = false;
|
||||
#endif /* SLIC3R_GUI */
|
||||
argv_extended.emplace_back(argv[i]);
|
||||
}
|
||||
argv_extended.emplace_back(nullptr);
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
OpenGLVersionCheck opengl_version_check;
|
||||
bool load_mesa =
|
||||
// Forced from the command line.
|
||||
force_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);
|
||||
#endif /* SLIC3R_GUI */
|
||||
|
||||
wchar_t path_to_exe[MAX_PATH + 1] = { 0 };
|
||||
::GetModuleFileNameW(nullptr, path_to_exe, MAX_PATH);
|
||||
|
@ -228,6 +251,7 @@ int wmain(int argc, wchar_t **argv)
|
|||
_wsplitpath(path_to_exe, drive, dir, fname, ext);
|
||||
_wmakepath(path_to_exe, drive, dir, nullptr, nullptr);
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
// https://wiki.qt.io/Cross_compiling_Mesa_for_Windows
|
||||
// http://download.qt.io/development_releases/prebuilt/llvmpipe/windows/
|
||||
if (load_mesa) {
|
||||
|
@ -242,6 +266,8 @@ int wmain(int argc, wchar_t **argv)
|
|||
} else
|
||||
printf("MESA OpenGL library was loaded sucessfully\n");
|
||||
}
|
||||
#endif /* SLIC3R_GUI */
|
||||
|
||||
|
||||
wchar_t path_to_slic3r[MAX_PATH + 1] = { 0 };
|
||||
wcscpy(path_to_slic3r, path_to_exe);
|
||||
|
@ -267,5 +293,5 @@ int wmain(int argc, wchar_t **argv)
|
|||
return -1;
|
||||
}
|
||||
// argc minus the trailing nullptr of the argv
|
||||
return slic3r_main(argv_extended.size() - 1, argv_extended.data());
|
||||
return slic3r_main((int)argv_extended.size() - 1, argv_extended.data());
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue