mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Fixed conflicts after merge with master
This commit is contained in:
		
						commit
						3e902ecfbd
					
				
					 20 changed files with 445 additions and 114 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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -897,13 +897,13 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const
 | 
			
		|||
                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());
 | 
			
		||||
            bb.merge(v->mesh.transformed_bounding_box(inst_matrix * v->get_matrix()));
 | 
			
		||||
#else
 | 
			
		||||
            vol_mesh.transform(v->get_matrix());
 | 
			
		||||
            bb.merge(this->instances.front()->transform_mesh_bounding_box(vol_mesh, true));
 | 
			
		||||
            // unmaintaned
 | 
			
		||||
            assert(false);
 | 
			
		||||
            // 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;
 | 
			
		||||
| 
						 | 
				
			
			@ -920,13 +920,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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -774,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;
 | 
			
		||||
| 
						 | 
				
			
			@ -789,6 +790,9 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool
 | 
			
		|||
    if (z_range_id != -1)
 | 
			
		||||
        glsafe(::glUniform2fv(z_range_id, 1, (const GLfloat*)z_range));
 | 
			
		||||
 | 
			
		||||
    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();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -433,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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -491,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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3521,7 +3521,15 @@ void GLCanvas3D::_picking_pass() const
 | 
			
		|||
 | 
			
		||||
        glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
 | 
			
		||||
 | 
			
		||||
        m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane();
 | 
			
		||||
        if (! m_use_VBOs) {
 | 
			
		||||
            ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data());
 | 
			
		||||
            ::glEnable(GL_CLIP_PLANE0);
 | 
			
		||||
        }
 | 
			
		||||
        _render_volumes_for_picking();
 | 
			
		||||
        if (! m_use_VBOs)
 | 
			
		||||
            ::glDisable(GL_CLIP_PLANE0);
 | 
			
		||||
 | 
			
		||||
        m_gizmos.render_current_gizmo_for_picking_pass(m_selection);
 | 
			
		||||
 | 
			
		||||
        if (m_multisample_allowed)
 | 
			
		||||
| 
						 | 
				
			
			@ -3602,6 +3610,8 @@ void GLCanvas3D::_render_axes() const
 | 
			
		|||
    m_bed.render_axes();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void GLCanvas3D::_render_objects() const
 | 
			
		||||
{
 | 
			
		||||
    if (m_volumes.empty())
 | 
			
		||||
| 
						 | 
				
			
			@ -3610,6 +3620,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)
 | 
			
		||||
| 
						 | 
				
			
			@ -3630,6 +3642,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;
 | 
			
		||||
| 
						 | 
				
			
			@ -3650,13 +3664,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) {
 | 
			
		||||
| 
						 | 
				
			
			@ -3664,13 +3682,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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,6 +65,35 @@ 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 +317,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 +408,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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			@ -55,6 +56,11 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S
 | 
			
		|||
 | 
			
		||||
    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();
 | 
			
		||||
| 
						 | 
				
			
			@ -83,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)
 | 
			
		||||
| 
						 | 
				
			
			@ -138,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];
 | 
			
		||||
| 
						 | 
				
			
			@ -164,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);
 | 
			
		||||
| 
						 | 
				
			
			@ -233,6 +306,21 @@ 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())
 | 
			
		||||
| 
						 | 
				
			
			@ -244,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) {
 | 
			
		||||
| 
						 | 
				
			
			@ -266,6 +353,8 @@ void GLGizmoSlaSupports::update_mesh()
 | 
			
		|||
    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:
 | 
			
		||||
| 
						 | 
				
			
			@ -282,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)
 | 
			
		||||
        );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -377,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)
 | 
			
		||||
| 
						 | 
				
			
			@ -564,6 +703,64 @@ void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const
 | 
			
		||||
{
 | 
			
		||||
    if (!m_model_object)
 | 
			
		||||
        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)
 | 
			
		||||
| 
						 | 
				
			
			@ -681,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
 | 
			
		||||
| 
						 | 
				
			
			@ -752,8 +956,8 @@ 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
 | 
			
		||||
        wxGetApp().CallAfter([this]() {
 | 
			
		||||
               // Following is called through CallAfter, because otherwise there was a problem
 | 
			
		||||
            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) {
 | 
			
		||||
| 
						 | 
				
			
			@ -765,10 +969,16 @@ void GLGizmoSlaSupports::on_set_state()
 | 
			
		|||
                            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_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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,12 +17,17 @@ namespace Slic3r {
 | 
			
		|||
namespace GUI {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ClippingPlane;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GLGizmoSlaSupports : public GLGizmoBase
 | 
			
		||||
{
 | 
			
		||||
private:
 | 
			
		||||
    ModelObject* m_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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3711,6 +3711,7 @@ 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();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1079,6 +1079,7 @@ void Selection::paste_from_clipboard()
 | 
			
		|||
    case Instance:
 | 
			
		||||
    {
 | 
			
		||||
        paste_objects_from_clipboard();
 | 
			
		||||
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3224,7 +3224,8 @@ void TabSLAMaterial::build()
 | 
			
		|||
    optgroup = page->new_optgroup(_(L("Corrections")));
 | 
			
		||||
    optgroup->label_width = 19 * m_em_unit;//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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue