mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Fixed conflicts after pulling from master
This commit is contained in:
		
						commit
						56e2f47ba6
					
				
					 29 changed files with 1743 additions and 565 deletions
				
			
		| 
						 | 
					@ -1,5 +1,19 @@
 | 
				
			||||||
#version 110
 | 
					#version 110
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define INTENSITY_CORRECTION 0.6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
 | 
				
			||||||
 | 
					const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
 | 
				
			||||||
 | 
					#define LIGHT_TOP_DIFFUSE    (0.8 * INTENSITY_CORRECTION)
 | 
				
			||||||
 | 
					#define LIGHT_TOP_SPECULAR   (0.125 * INTENSITY_CORRECTION)
 | 
				
			||||||
 | 
					#define LIGHT_TOP_SHININESS  20.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
 | 
				
			||||||
 | 
					const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
 | 
				
			||||||
 | 
					#define LIGHT_FRONT_DIFFUSE  (0.3 * INTENSITY_CORRECTION)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define INTENSITY_AMBIENT    0.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
 | 
					const vec3 ZERO = vec3(0.0, 0.0, 0.0);
 | 
				
			||||||
const vec3 GREEN = vec3(0.0, 0.7, 0.0);
 | 
					const vec3 GREEN = vec3(0.0, 0.7, 0.0);
 | 
				
			||||||
const vec3 YELLOW = vec3(0.5, 0.7, 0.0);
 | 
					const vec3 YELLOW = vec3(0.5, 0.7, 0.0);
 | 
				
			||||||
| 
						 | 
					@ -42,14 +56,42 @@ vec3 sinking_color(vec3 color)
 | 
				
			||||||
    return (mod(model_pos.x + model_pos.y + model_pos.z, BANDS_WIDTH) < (0.5 * BANDS_WIDTH)) ? mix(color, ZERO, 0.6666) : color;
 | 
					    return (mod(model_pos.x + model_pos.y + model_pos.z, BANDS_WIDTH) < (0.5 * BANDS_WIDTH)) ? mix(color, ZERO, 0.6666) : color;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform bool compute_triangle_normals_in_fs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void main()
 | 
					void main()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (any(lessThan(clipping_planes_dots, ZERO)))
 | 
					    if (any(lessThan(clipping_planes_dots, ZERO)))
 | 
				
			||||||
        discard;
 | 
					        discard;
 | 
				
			||||||
    vec3 color = uniform_color.rgb;
 | 
					    vec3  color = uniform_color.rgb;
 | 
				
			||||||
    float alpha = uniform_color.a;
 | 
					    float alpha = uniform_color.a;
 | 
				
			||||||
    if (slope.actived && world_normal_z < slope.normal_z - EPSILON)
 | 
					
 | 
				
			||||||
    {
 | 
					    vec2  intensity_fs      = intensity;
 | 
				
			||||||
 | 
					    vec3  eye_normal_fs     = eye_normal;
 | 
				
			||||||
 | 
					    float world_normal_z_fs = world_normal_z;
 | 
				
			||||||
 | 
					    if (compute_triangle_normals_in_fs) {
 | 
				
			||||||
 | 
					        vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // First transform the normal into camera space and normalize the result.
 | 
				
			||||||
 | 
					        eye_normal_fs = normalize(gl_NormalMatrix * triangle_normal);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
 | 
				
			||||||
 | 
					        // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
 | 
				
			||||||
 | 
					        float NdotL = max(dot(eye_normal_fs, LIGHT_TOP_DIR), 0.0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        intensity_fs = vec2(0.0, 0.0);
 | 
				
			||||||
 | 
					        intensity_fs.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
 | 
				
			||||||
 | 
					        vec3 position = (gl_ModelViewMatrix * model_pos).xyz;
 | 
				
			||||||
 | 
					        intensity_fs.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal_fs)), 0.0), LIGHT_TOP_SHININESS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Perform the same lighting calculation for the 2nd light source (no specular applied).
 | 
				
			||||||
 | 
					        NdotL = max(dot(eye_normal_fs, LIGHT_FRONT_DIR), 0.0);
 | 
				
			||||||
 | 
					        intensity_fs.x += NdotL * LIGHT_FRONT_DIFFUSE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // z component of normal vector in world coordinate used for slope shading
 | 
				
			||||||
 | 
					        world_normal_z_fs = slope.actived ? (normalize(slope.volume_world_normal_matrix * triangle_normal)).z : 0.0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (slope.actived && world_normal_z_fs < slope.normal_z - EPSILON) {
 | 
				
			||||||
        color = vec3(0.7, 0.7, 1.0);
 | 
					        color = vec3(0.7, 0.7, 1.0);
 | 
				
			||||||
        alpha = 1.0;
 | 
					        alpha = 1.0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -60,8 +102,8 @@ void main()
 | 
				
			||||||
        color = (abs(world_pos_z) < 0.05) ? WHITE : sinking_color(color);
 | 
					        color = (abs(world_pos_z) < 0.05) ? WHITE : sinking_color(color);
 | 
				
			||||||
#ifdef ENABLE_ENVIRONMENT_MAP
 | 
					#ifdef ENABLE_ENVIRONMENT_MAP
 | 
				
			||||||
    if (use_environment_tex)
 | 
					    if (use_environment_tex)
 | 
				
			||||||
        gl_FragColor = vec4(0.45 * texture2D(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color * intensity.x, alpha);
 | 
					        gl_FragColor = vec4(0.45 * texture2D(environment_tex, normalize(eye_normal_fs).xy * 0.5 + 0.5).xyz + 0.8 * color * intensity_fs.x, alpha);
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
        gl_FragColor = vec4(vec3(intensity.y) + color * intensity.x, alpha);
 | 
					        gl_FragColor = vec4(vec3(intensity_fs.y) + color * intensity_fs.x, alpha);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,22 +54,26 @@ varying float world_pos_z;
 | 
				
			||||||
varying float world_normal_z;
 | 
					varying float world_normal_z;
 | 
				
			||||||
varying vec3 eye_normal;
 | 
					varying vec3 eye_normal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uniform bool compute_triangle_normals_in_fs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void main()
 | 
					void main()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    // First transform the normal into camera space and normalize the result.
 | 
					    if (!compute_triangle_normals_in_fs) {
 | 
				
			||||||
    eye_normal = normalize(gl_NormalMatrix * gl_Normal);
 | 
					        // First transform the normal into camera space and normalize the result.
 | 
				
			||||||
 | 
					        eye_normal = normalize(gl_NormalMatrix * gl_Normal);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
 | 
					        // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
 | 
				
			||||||
    // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
 | 
					        // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
 | 
				
			||||||
    float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
 | 
					        float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
 | 
					        intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
 | 
				
			||||||
    vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz;
 | 
					        vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz;
 | 
				
			||||||
    intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
 | 
					        intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Perform the same lighting calculation for the 2nd light source (no specular applied).
 | 
					        // Perform the same lighting calculation for the 2nd light source (no specular applied).
 | 
				
			||||||
    NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
 | 
					        NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
 | 
				
			||||||
    intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
 | 
					        intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    model_pos = gl_Vertex;
 | 
					    model_pos = gl_Vertex;
 | 
				
			||||||
    // Point in homogenous coordinates.
 | 
					    // Point in homogenous coordinates.
 | 
				
			||||||
| 
						 | 
					@ -77,19 +81,17 @@ void main()
 | 
				
			||||||
    world_pos_z = world_pos.z;
 | 
					    world_pos_z = world_pos.z;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // compute deltas for out of print volume detection (world coordinates)
 | 
					    // compute deltas for out of print volume detection (world coordinates)
 | 
				
			||||||
    if (print_box.actived)
 | 
					    if (print_box.actived) {
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        delta_box_min = world_pos.xyz - print_box.min;
 | 
					        delta_box_min = world_pos.xyz - print_box.min;
 | 
				
			||||||
        delta_box_max = world_pos.xyz - print_box.max;
 | 
					        delta_box_max = world_pos.xyz - print_box.max;
 | 
				
			||||||
    }
 | 
					    } else {
 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        delta_box_min = ZERO;
 | 
					        delta_box_min = ZERO;
 | 
				
			||||||
        delta_box_max = ZERO;
 | 
					        delta_box_max = ZERO;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // z component of normal vector in world coordinate used for slope shading
 | 
					    // z component of normal vector in world coordinate used for slope shading
 | 
				
			||||||
	world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * gl_Normal)).z : 0.0;
 | 
					    if (!compute_triangle_normals_in_fs)
 | 
				
			||||||
 | 
					        world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * gl_Normal)).z : 0.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    gl_Position = ftransform();
 | 
					    gl_Position = ftransform();
 | 
				
			||||||
    // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.
 | 
					    // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -602,12 +602,12 @@ FaceNeighborIndex its_create_neighbors_index_8(const indexed_triangle_set &its)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::vector<Vec3crd> its_create_neighbors_index_9(const indexed_triangle_set &its)
 | 
					std::vector<Vec3crd> its_create_neighbors_index_9(const indexed_triangle_set &its)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return create_neighbors_index(ex_seq, its);
 | 
					    return create_face_neighbors_index(ex_seq, its);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::vector<Vec3i> its_create_neighbors_index_10(const indexed_triangle_set &its)
 | 
					std::vector<Vec3i> its_create_neighbors_index_10(const indexed_triangle_set &its)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return create_neighbors_index(ex_tbb, its);
 | 
					    return create_face_neighbors_index(ex_tbb, its);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace Slic3r
 | 
					} // namespace Slic3r
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64,10 +64,12 @@ public:
 | 
				
			||||||
	void			    set(const std::string §ion, const std::string &key, const std::string &value)
 | 
						void			    set(const std::string §ion, const std::string &key, const std::string &value)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
#ifndef NDEBUG
 | 
					#ifndef NDEBUG
 | 
				
			||||||
		std::string key_trimmed = key;
 | 
							{
 | 
				
			||||||
		boost::trim_all(key_trimmed);
 | 
								std::string key_trimmed = key;
 | 
				
			||||||
		assert(key_trimmed == key);
 | 
								boost::trim_all(key_trimmed);
 | 
				
			||||||
		assert(! key_trimmed.empty());
 | 
								assert(key_trimmed == key);
 | 
				
			||||||
 | 
								assert(! key_trimmed.empty());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
#endif // NDEBUG
 | 
					#endif // NDEBUG
 | 
				
			||||||
		std::string &old = m_storage[section][key];
 | 
							std::string &old = m_storage[section][key];
 | 
				
			||||||
		if (old != value) {
 | 
							if (old != value) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -201,21 +201,24 @@ void SeamPlacer::init(const Print& print)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::vector<ExPolygons> temp_enf;
 | 
					    std::vector<ExPolygons> temp_enf;
 | 
				
			||||||
    std::vector<ExPolygons> temp_blk;
 | 
					    std::vector<ExPolygons> temp_blk;
 | 
				
			||||||
 | 
					    std::vector<Polygons>   temp_polygons;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (const PrintObject* po : print.objects()) {
 | 
					    for (const PrintObject* po : print.objects()) {
 | 
				
			||||||
        temp_enf.clear();
 | 
					 | 
				
			||||||
        temp_blk.clear();
 | 
					 | 
				
			||||||
        po->project_and_append_custom_facets(true, EnforcerBlockerType::ENFORCER, temp_enf);
 | 
					 | 
				
			||||||
        po->project_and_append_custom_facets(true, EnforcerBlockerType::BLOCKER, temp_blk);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Offset the triangles out slightly.
 | 
					        auto merge_and_offset = [po, &temp_polygons, max_nozzle_dmr](EnforcerBlockerType type, std::vector<ExPolygons>& out) {
 | 
				
			||||||
        for (auto* custom_per_object : {&temp_enf, &temp_blk}) {
 | 
					            // Offset the triangles out slightly.
 | 
				
			||||||
 | 
					            temp_polygons.clear();
 | 
				
			||||||
 | 
					            po->project_and_append_custom_facets(true, type, temp_polygons);
 | 
				
			||||||
 | 
					            out.clear();
 | 
				
			||||||
 | 
					            out.reserve(temp_polygons.size());
 | 
				
			||||||
            float offset = max_nozzle_dmr + po->config().elefant_foot_compensation;
 | 
					            float offset = max_nozzle_dmr + po->config().elefant_foot_compensation;
 | 
				
			||||||
            for (ExPolygons& explgs : *custom_per_object) {
 | 
					            for (const Polygons &src : temp_polygons) {
 | 
				
			||||||
                explgs = Slic3r::offset_ex(explgs, scale_(offset));
 | 
					                out.emplace_back(Slic3r::offset_ex(src, scale_(offset)));
 | 
				
			||||||
                offset = max_nozzle_dmr;
 | 
					                offset = max_nozzle_dmr;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        };
 | 
				
			||||||
 | 
					        merge_and_offset(EnforcerBlockerType::BLOCKER, temp_blk);
 | 
				
			||||||
 | 
					        merge_and_offset(EnforcerBlockerType::ENFORCER, temp_enf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//     FIXME: Offsetting should be done somehow cheaper, but following does not work
 | 
					//     FIXME: Offsetting should be done somehow cheaper, but following does not work
 | 
				
			||||||
//        for (auto* custom_per_object : {&temp_enf, &temp_blk}) {
 | 
					//        for (auto* custom_per_object : {&temp_enf, &temp_blk}) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -201,6 +201,16 @@ protected:
 | 
				
			||||||
    virtual ~SupportLayer() = default;
 | 
					    virtual ~SupportLayer() = default;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename LayerContainer>
 | 
				
			||||||
 | 
					inline std::vector<float> zs_from_layers(const LayerContainer &layers)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    std::vector<float> zs;
 | 
				
			||||||
 | 
					    zs.reserve(layers.size());
 | 
				
			||||||
 | 
					    for (const Layer *l : layers)
 | 
				
			||||||
 | 
					        zs.emplace_back((float)l->slice_z);
 | 
				
			||||||
 | 
					    return zs;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@
 | 
				
			||||||
namespace Slic3r {
 | 
					namespace Slic3r {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template<class ExPolicy>
 | 
					template<class ExPolicy>
 | 
				
			||||||
std::vector<Vec3i> create_neighbors_index(ExPolicy &&ex, const indexed_triangle_set &its);
 | 
					std::vector<Vec3i> create_face_neighbors_index(ExPolicy &&ex, const indexed_triangle_set &its);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace meshsplit_detail {
 | 
					namespace meshsplit_detail {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,7 +24,7 @@ template<> struct ItsWithNeighborsIndex_<indexed_triangle_set> {
 | 
				
			||||||
    static const indexed_triangle_set &get_its(const indexed_triangle_set &its) noexcept { return its; }
 | 
					    static const indexed_triangle_set &get_its(const indexed_triangle_set &its) noexcept { return its; }
 | 
				
			||||||
    static Index get_index(const indexed_triangle_set &its) noexcept
 | 
					    static Index get_index(const indexed_triangle_set &its) noexcept
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return create_neighbors_index(ex_tbb, its);
 | 
					        return create_face_neighbors_index(ex_tbb, its);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -162,28 +162,14 @@ template<class Its> bool its_is_splittable(const Its &m)
 | 
				
			||||||
    return !faces.empty();
 | 
					    return !faces.empty();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline int get_vertex_index(size_t vertex_index, const stl_triangle_vertex_indices &triangle_indices) {
 | 
					 | 
				
			||||||
    if (int(vertex_index) == triangle_indices[0]) return 0;
 | 
					 | 
				
			||||||
    if (int(vertex_index) == triangle_indices[1]) return 1;
 | 
					 | 
				
			||||||
    if (int(vertex_index) == triangle_indices[2]) return 2;
 | 
					 | 
				
			||||||
    return -1;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
inline Vec2crd get_edge_indices(int edge_index, const stl_triangle_vertex_indices &triangle_indices)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    int next_edge_index = (edge_index == 2) ? 0 : edge_index + 1;
 | 
					 | 
				
			||||||
    int vi0             = triangle_indices[edge_index];
 | 
					 | 
				
			||||||
    int vi1             = triangle_indices[next_edge_index];
 | 
					 | 
				
			||||||
    return Vec2crd(vi0, vi1);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template<class ExPolicy>
 | 
					template<class ExPolicy>
 | 
				
			||||||
std::vector<Vec3i> create_neighbors_index(ExPolicy &&ex, const indexed_triangle_set &its)
 | 
					std::vector<Vec3i> create_face_neighbors_index(ExPolicy &&ex, const indexed_triangle_set &its)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    const std::vector<stl_triangle_vertex_indices> &indices = its.indices;
 | 
					    const std::vector<stl_triangle_vertex_indices> &indices = its.indices;
 | 
				
			||||||
    size_t vertices_size = its.vertices.size();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (indices.empty() || vertices_size == 0) return {};
 | 
					    if (indices.empty()) return {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert(! its.vertices.empty());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto               vertex_triangles = VertexFaceIndex{its};
 | 
					    auto               vertex_triangles = VertexFaceIndex{its};
 | 
				
			||||||
    static constexpr int no_value         = -1;
 | 
					    static constexpr int no_value         = -1;
 | 
				
			||||||
| 
						 | 
					@ -192,27 +178,28 @@ std::vector<Vec3i> create_neighbors_index(ExPolicy &&ex, const indexed_triangle_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //for (const stl_triangle_vertex_indices& triangle_indices : indices) {
 | 
					    //for (const stl_triangle_vertex_indices& triangle_indices : indices) {
 | 
				
			||||||
    execution::for_each(ex, size_t(0), indices.size(),
 | 
					    execution::for_each(ex, size_t(0), indices.size(),
 | 
				
			||||||
        [&neighbors, &indices, &vertex_triangles] (size_t index)
 | 
					        [&neighbors, &indices, &vertex_triangles] (size_t face_idx)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            Vec3i& neighbor = neighbors[index];
 | 
					            Vec3i& neighbor = neighbors[face_idx];
 | 
				
			||||||
            const stl_triangle_vertex_indices & triangle_indices = indices[index];
 | 
					            const stl_triangle_vertex_indices & triangle_indices = indices[face_idx];
 | 
				
			||||||
            for (int edge_index = 0; edge_index < 3; ++edge_index) {
 | 
					            for (int edge_index = 0; edge_index < 3; ++edge_index) {
 | 
				
			||||||
                // check if done
 | 
					                // check if done
 | 
				
			||||||
                int& neighbor_edge = neighbor[edge_index];
 | 
					                int& neighbor_edge = neighbor[edge_index];
 | 
				
			||||||
                if (neighbor_edge != no_value) continue;
 | 
					                if (neighbor_edge != no_value) 
 | 
				
			||||||
                Vec2crd edge_indices = get_edge_indices(edge_index, triangle_indices);
 | 
					                    // This edge already has a neighbor assigned.
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                Vec2i edge_indices = its_triangle_edge(triangle_indices, edge_index);
 | 
				
			||||||
                // IMPROVE: use same vector for 2 sides of triangle
 | 
					                // IMPROVE: use same vector for 2 sides of triangle
 | 
				
			||||||
                const auto &faces_range = vertex_triangles[edge_indices[0]];
 | 
					                for (const size_t other_face : vertex_triangles[edge_indices[0]]) {
 | 
				
			||||||
                for (const size_t &face : faces_range) {
 | 
					                    if (other_face <= face_idx) continue;
 | 
				
			||||||
                    if (face <= index) continue;
 | 
					                    const stl_triangle_vertex_indices &face_indices = indices[other_face];
 | 
				
			||||||
                    const stl_triangle_vertex_indices &face_indices = indices[face];
 | 
					                    int vertex_index = its_triangle_vertex_index(face_indices, edge_indices[1]);
 | 
				
			||||||
                    int vertex_index = get_vertex_index(edge_indices[1], face_indices);
 | 
					 | 
				
			||||||
                    // NOT Contain second vertex?
 | 
					                    // NOT Contain second vertex?
 | 
				
			||||||
                    if (vertex_index < 0) continue;
 | 
					                    if (vertex_index < 0) continue;
 | 
				
			||||||
                    // Has NOT oposit direction?
 | 
					                    // Has NOT oposite direction?
 | 
				
			||||||
                    if (edge_indices[0] != face_indices[(vertex_index + 1) % 3]) continue;
 | 
					                    if (edge_indices[0] != face_indices[(vertex_index + 1) % 3]) continue;
 | 
				
			||||||
                    neighbor_edge = face;
 | 
					                    neighbor_edge = other_face;
 | 
				
			||||||
                    neighbors[face][vertex_index] = index;
 | 
					                    neighbors[other_face][vertex_index] = face_idx;
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1959,8 +1959,19 @@ indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, Enforce
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    TriangleSelector selector(mv.mesh());
 | 
					    TriangleSelector selector(mv.mesh());
 | 
				
			||||||
    selector.deserialize(m_data);
 | 
					    selector.deserialize(m_data);
 | 
				
			||||||
    indexed_triangle_set out = selector.get_facets(type);
 | 
					    return selector.get_facets(type);
 | 
				
			||||||
    return out;
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					indexed_triangle_set FacetsAnnotation::get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    TriangleSelector selector(mv.mesh());
 | 
				
			||||||
 | 
					    selector.deserialize(m_data);
 | 
				
			||||||
 | 
					    return selector.get_facets_strict(type);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool FacetsAnnotation::has_facets(const ModelVolume& mv, EnforcerBlockerType type) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return TriangleSelector::has_facets(m_data, type);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool FacetsAnnotation::set(const TriangleSelector& selector)
 | 
					bool FacetsAnnotation::set(const TriangleSelector& selector)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -500,10 +500,26 @@ private:
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum class EnforcerBlockerType : int8_t {
 | 
					enum class EnforcerBlockerType : int8_t {
 | 
				
			||||||
    // Maximum is 3. The value is serialized in TriangleSelector into 2 bits!
 | 
					    // Maximum is 3. The value is serialized in TriangleSelector into 2 bits.
 | 
				
			||||||
    NONE      = 0,
 | 
					    NONE      = 0,
 | 
				
			||||||
    ENFORCER  = 1,
 | 
					    ENFORCER  = 1,
 | 
				
			||||||
    BLOCKER   = 2
 | 
					    BLOCKER   = 2,
 | 
				
			||||||
 | 
					    // Maximum is 15. The value is serialized in TriangleSelector into 6 bits using a 2 bit prefix code.
 | 
				
			||||||
 | 
					    Extruder1 = ENFORCER,
 | 
				
			||||||
 | 
					    Extruder2 = BLOCKER,
 | 
				
			||||||
 | 
					    Extruder3,
 | 
				
			||||||
 | 
					    Extruder4,
 | 
				
			||||||
 | 
					    Extruder5,
 | 
				
			||||||
 | 
					    Extruder6,
 | 
				
			||||||
 | 
					    Extruder7,
 | 
				
			||||||
 | 
					    Extruder8,
 | 
				
			||||||
 | 
					    Extruder9,
 | 
				
			||||||
 | 
					    Extruder10,
 | 
				
			||||||
 | 
					    Extruder11,
 | 
				
			||||||
 | 
					    Extruder12,
 | 
				
			||||||
 | 
					    Extruder13,
 | 
				
			||||||
 | 
					    Extruder14,
 | 
				
			||||||
 | 
					    Extruder15,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum class ConversionType : int {
 | 
					enum class ConversionType : int {
 | 
				
			||||||
| 
						 | 
					@ -521,6 +537,8 @@ public:
 | 
				
			||||||
    const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>>& get_data() const throw() { return m_data; }
 | 
					    const std::pair<std::vector<std::pair<int, int>>, std::vector<bool>>& get_data() const throw() { return m_data; }
 | 
				
			||||||
    bool set(const TriangleSelector& selector);
 | 
					    bool set(const TriangleSelector& selector);
 | 
				
			||||||
    indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
 | 
					    indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
 | 
				
			||||||
 | 
					    indexed_triangle_set get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const;
 | 
				
			||||||
 | 
					    bool has_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
 | 
				
			||||||
    bool empty() const { return m_data.first.empty(); }
 | 
					    bool empty() const { return m_data.first.empty(); }
 | 
				
			||||||
    void clear();
 | 
					    void clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1145,67 +1145,16 @@ static void cut_segmented_layers(const std::vector<ExPolygons>
 | 
				
			||||||
    BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - cutting segmented layers in parallel - end";
 | 
					    BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - cutting segmented layers in parallel - end";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// #define MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Returns MMU segmentation of top and bottom layers based on painting in MMU segmentation gizmo
 | 
					// Returns MMU segmentation of top and bottom layers based on painting in MMU segmentation gizmo
 | 
				
			||||||
static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bottom_layers(const PrintObject             &print_object,
 | 
					static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bottom_layers(const PrintObject             &print_object,
 | 
				
			||||||
                                                                                          const std::vector<ExPolygons> &input_expolygons,
 | 
					                                                                                          const std::vector<ExPolygons> &input_expolygons,
 | 
				
			||||||
                                                                                          const std::function<void()>   &throw_on_cancel_callback)
 | 
					                                                                                          const std::function<void()>   &throw_on_cancel_callback)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    const size_t num_extruders = print_object.print()->config().nozzle_diameter.size() + 1;
 | 
					    const size_t num_extruders = print_object.print()->config().nozzle_diameter.size() + 1;
 | 
				
			||||||
 | 
					    const size_t num_layers    = input_expolygons.size();
 | 
				
			||||||
    const ConstLayerPtrsAdaptor layers = print_object.layers();
 | 
					    const ConstLayerPtrsAdaptor layers = print_object.layers();
 | 
				
			||||||
    std::vector<std::vector<ExPolygons>> triangles_by_color(num_extruders);
 | 
					 | 
				
			||||||
    triangles_by_color.assign(num_extruders, std::vector<ExPolygons>(layers.size()));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - top and bottom layers - projection of painted triangles - begin";
 | 
					 | 
				
			||||||
    for (const ModelVolume *mv : print_object.model_object()->volumes) {
 | 
					 | 
				
			||||||
        for (size_t extruder_idx = 0; extruder_idx < num_extruders; ++extruder_idx) {
 | 
					 | 
				
			||||||
            throw_on_cancel_callback();
 | 
					 | 
				
			||||||
            const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx));
 | 
					 | 
				
			||||||
            if (!mv->is_model_part() || custom_facets.indices.empty())
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            const Transform3f tr = print_object.trafo().cast<float>() * mv->get_matrix().cast<float>();
 | 
					 | 
				
			||||||
            for (size_t facet_idx = 0; facet_idx < custom_facets.indices.size(); ++facet_idx) {
 | 
					 | 
				
			||||||
                float min_z = std::numeric_limits<float>::max();
 | 
					 | 
				
			||||||
                float max_z = std::numeric_limits<float>::lowest();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                std::array<Vec3f, 3> facet;
 | 
					 | 
				
			||||||
                Points               projected_facet(3);
 | 
					 | 
				
			||||||
                for (int p_idx = 0; p_idx < 3; ++p_idx) {
 | 
					 | 
				
			||||||
                    facet[p_idx] = tr * custom_facets.vertices[custom_facets.indices[facet_idx](p_idx)];
 | 
					 | 
				
			||||||
                    max_z        = std::max(max_z, facet[p_idx].z());
 | 
					 | 
				
			||||||
                    min_z        = std::min(min_z, facet[p_idx].z());
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Sort the vertices by z-axis for simplification of projected_facet on slices
 | 
					 | 
				
			||||||
                std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                for (int p_idx = 0; p_idx < 3; ++p_idx) {
 | 
					 | 
				
			||||||
                    projected_facet[p_idx] = Point(scale_(facet[p_idx].x()), scale_(facet[p_idx].y()));
 | 
					 | 
				
			||||||
                    projected_facet[p_idx] = projected_facet[p_idx] - print_object.center_offset();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                ExPolygon triangle = ExPolygon(projected_facet);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Find lowest slice not below the triangle.
 | 
					 | 
				
			||||||
                auto first_layer = std::upper_bound(layers.begin(), layers.end(), float(min_z - EPSILON),
 | 
					 | 
				
			||||||
                                                    [](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; });
 | 
					 | 
				
			||||||
                auto last_layer  = std::upper_bound(layers.begin(), layers.end(), float(max_z - EPSILON),
 | 
					 | 
				
			||||||
                                                   [](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (last_layer == layers.end())
 | 
					 | 
				
			||||||
                    --last_layer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (first_layer == layers.end() || (first_layer != layers.begin() && facet[0].z() < (*first_layer)->print_z - EPSILON))
 | 
					 | 
				
			||||||
                    --first_layer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                for (auto layer_it = first_layer; (layer_it != (last_layer + 1) && layer_it != layers.end()); ++layer_it) {
 | 
					 | 
				
			||||||
                    size_t layer_idx = layer_it - layers.begin();
 | 
					 | 
				
			||||||
                    triangles_by_color[extruder_idx][layer_idx].emplace_back(triangle);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - top and bottom layers - projection of painted triangles - end";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto get_extrusion_width = [&layers = std::as_const(layers)](const size_t layer_idx) -> float {
 | 
					    auto get_extrusion_width = [&layers = std::as_const(layers)](const size_t layer_idx) -> float {
 | 
				
			||||||
        auto extrusion_width_it = std::max_element(layers[layer_idx]->regions().begin(), layers[layer_idx]->regions().end(),
 | 
					        auto extrusion_width_it = std::max_element(layers[layer_idx]->regions().begin(), layers[layer_idx]->regions().end(),
 | 
				
			||||||
| 
						 | 
					@ -1235,9 +1184,10 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
 | 
				
			||||||
        return (*top_bottom_layer_it)->region().config().bottom_solid_layers;
 | 
					        return (*top_bottom_layer_it)->region().config().bottom_solid_layers;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::vector<ExPolygons> top_layers(input_expolygons.size());
 | 
					#if 0
 | 
				
			||||||
 | 
					    std::vector<ExPolygons> top_layers(num_layers);
 | 
				
			||||||
    top_layers.back() = input_expolygons.back();
 | 
					    top_layers.back() = input_expolygons.back();
 | 
				
			||||||
    tbb::parallel_for(tbb::blocked_range<size_t>(1, input_expolygons.size()), [&](const tbb::blocked_range<size_t> &range) {
 | 
					    tbb::parallel_for(tbb::blocked_range<size_t>(1, num_layers), [&](const tbb::blocked_range<size_t> &range) {
 | 
				
			||||||
        for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
 | 
					        for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
 | 
				
			||||||
            throw_on_cancel_callback();
 | 
					            throw_on_cancel_callback();
 | 
				
			||||||
            float extrusion_width     = 0.1f * float(scale_(get_extrusion_width(layer_idx)));
 | 
					            float extrusion_width     = 0.1f * float(scale_(get_extrusion_width(layer_idx)));
 | 
				
			||||||
| 
						 | 
					@ -1245,9 +1195,9 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }); // end of parallel_for
 | 
					    }); // end of parallel_for
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::vector<ExPolygons> bottom_layers(input_expolygons.size());
 | 
					    std::vector<ExPolygons> bottom_layers(num_layers);
 | 
				
			||||||
    bottom_layers.front() = input_expolygons.front();
 | 
					    bottom_layers.front() = input_expolygons.front();
 | 
				
			||||||
    tbb::parallel_for(tbb::blocked_range<size_t>(0, input_expolygons.size() - 1), [&](const tbb::blocked_range<size_t> &range) {
 | 
					    tbb::parallel_for(tbb::blocked_range<size_t>(0, num_layers - 1), [&](const tbb::blocked_range<size_t> &range) {
 | 
				
			||||||
        for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
 | 
					        for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
 | 
				
			||||||
            throw_on_cancel_callback();
 | 
					            throw_on_cancel_callback();
 | 
				
			||||||
            float extrusion_width        = 0.1f * float(scale_(get_extrusion_width(layer_idx)));
 | 
					            float extrusion_width        = 0.1f * float(scale_(get_extrusion_width(layer_idx)));
 | 
				
			||||||
| 
						 | 
					@ -1255,28 +1205,84 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }); // end of parallel_for
 | 
					    }); // end of parallel_for
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tbb::parallel_for(tbb::blocked_range<size_t>(0, input_expolygons.size()), [&](const tbb::blocked_range<size_t> &range) {
 | 
					    std::vector<std::vector<ClipperLib::Paths>> triangles_by_color_raw(num_extruders, std::vector<ClipperLib::Paths>(layers.size()));
 | 
				
			||||||
        for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
 | 
					    BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - top and bottom layers - projection of painted triangles - begin";
 | 
				
			||||||
            throw_on_cancel_callback();
 | 
					    {
 | 
				
			||||||
            float extrusion_width = 0.1f * float(scale_(get_extrusion_width(layer_idx)));
 | 
					        auto                delta = float(10 * SCALED_EPSILON);
 | 
				
			||||||
            for (std::vector<ExPolygons> &triangles : triangles_by_color) {
 | 
					        std::vector<float>  deltas { delta, delta, delta };
 | 
				
			||||||
                if (!triangles[layer_idx].empty() && (!top_layers[layer_idx].empty() || !bottom_layers[layer_idx].empty())) {
 | 
					        Points              projected_facet;
 | 
				
			||||||
                    ExPolygons connected = union_ex(offset_ex(triangles[layer_idx], float(10 * SCALED_EPSILON)));
 | 
					        for (const ModelVolume *mv : print_object.model_object()->volumes)
 | 
				
			||||||
                    triangles[layer_idx] = union_ex(offset_ex(offset_ex(connected, -extrusion_width / 1), extrusion_width / 1));
 | 
					            if (mv->is_model_part()) {
 | 
				
			||||||
                } else {
 | 
					                const Transform3f tr = print_object.trafo().cast<float>() * mv->get_matrix().cast<float>();
 | 
				
			||||||
                    triangles[layer_idx].clear();
 | 
					
 | 
				
			||||||
 | 
					                for (size_t extruder_idx = 0; extruder_idx < num_extruders; ++extruder_idx) {
 | 
				
			||||||
 | 
					                    const indexed_triangle_set custom_facets = mv->mmu_segmentation_facets.get_facets(*mv, EnforcerBlockerType(extruder_idx));
 | 
				
			||||||
 | 
					                    if (custom_facets.indices.empty())
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    throw_on_cancel_callback();
 | 
				
			||||||
 | 
					                    for (size_t facet_idx = 0; facet_idx < custom_facets.indices.size(); ++facet_idx) {
 | 
				
			||||||
 | 
					                        float min_z = std::numeric_limits<float>::max();
 | 
				
			||||||
 | 
					                        float max_z = std::numeric_limits<float>::lowest();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        std::array<Vec3f, 3> facet;
 | 
				
			||||||
 | 
					                        for (int p_idx = 0; p_idx < 3; ++p_idx) {
 | 
				
			||||||
 | 
					                            facet[p_idx] = tr * custom_facets.vertices[custom_facets.indices[facet_idx](p_idx)];
 | 
				
			||||||
 | 
					                            max_z        = std::max(max_z, facet[p_idx].z());
 | 
				
			||||||
 | 
					                            min_z        = std::min(min_z, facet[p_idx].z());
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Sort the vertices by z-axis for simplification of projected_facet on slices
 | 
				
			||||||
 | 
					                        std::sort(facet.begin(), facet.end(), [](const Vec3f &p1, const Vec3f &p2) { return p1.z() < p2.z(); });
 | 
				
			||||||
 | 
					                        projected_facet.clear();
 | 
				
			||||||
 | 
					                        projected_facet.reserve(3);
 | 
				
			||||||
 | 
					                        for (int p_idx = 0; p_idx < 3; ++p_idx)
 | 
				
			||||||
 | 
					                            projected_facet.emplace_back(Point(scale_(facet[p_idx].x()), scale_(facet[p_idx].y())) - print_object.center_offset());
 | 
				
			||||||
 | 
					                        if (cross2((projected_facet[1] - projected_facet[0]).cast<int64_t>(), (projected_facet[2] - projected_facet[1]).cast<int64_t>()) < 0)
 | 
				
			||||||
 | 
					                            // Make CCW.
 | 
				
			||||||
 | 
					                            std::swap(projected_facet[1], projected_facet[2]);
 | 
				
			||||||
 | 
					                        ClipperLib::Path offsetted = mittered_offset_path_scaled(projected_facet, deltas, 3.);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Find lowest slice not below the triangle.
 | 
				
			||||||
 | 
					                        auto first_layer = std::upper_bound(layers.begin(), layers.end(), float(min_z - EPSILON),
 | 
				
			||||||
 | 
					                                                            [](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; });
 | 
				
			||||||
 | 
					                        auto last_layer  = std::upper_bound(layers.begin(), layers.end(), float(max_z - EPSILON),
 | 
				
			||||||
 | 
					                                                           [](float z, const Layer *l1) { return z < l1->slice_z + l1->height * 0.5; });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (last_layer == layers.end())
 | 
				
			||||||
 | 
					                            --last_layer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (first_layer == layers.end() || (first_layer != layers.begin() && facet[0].z() < (*first_layer)->print_z - EPSILON))
 | 
				
			||||||
 | 
					                            --first_layer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        for (auto layer_it = first_layer; (layer_it != (last_layer + 1) && layer_it != layers.end()); ++layer_it)
 | 
				
			||||||
 | 
					                            if (size_t layer_idx = layer_it - layers.begin(); ! top_layers[layer_idx].empty() || ! bottom_layers[layer_idx].empty())
 | 
				
			||||||
 | 
					                                triangles_by_color_raw[extruder_idx][layer_idx].emplace_back(offsetted);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - top and bottom layers - projection of painted triangles - end";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<std::vector<ExPolygons>> triangles_by_color(num_extruders, std::vector<ExPolygons>(layers.size()));
 | 
				
			||||||
 | 
					    tbb::parallel_for(tbb::blocked_range<size_t>(0, num_layers), [&](const tbb::blocked_range<size_t> &range) {
 | 
				
			||||||
 | 
					        for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
 | 
				
			||||||
 | 
					            throw_on_cancel_callback();
 | 
				
			||||||
 | 
					            float offset_factor = 0.1f * float(scale_(get_extrusion_width(layer_idx)));
 | 
				
			||||||
 | 
					            for (size_t extruder_id = 0; extruder_id < num_extruders; ++ extruder_id)
 | 
				
			||||||
 | 
					                if (ClipperLib::Paths &src_paths = triangles_by_color_raw[extruder_id][layer_idx]; !src_paths.empty())
 | 
				
			||||||
 | 
					                    triangles_by_color[extruder_id][layer_idx] = offset_ex(offset_ex(ClipperPaths_to_Slic3rExPolygons(src_paths), -offset_factor), offset_factor);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }); // end of parallel_for
 | 
					    }); // end of parallel_for
 | 
				
			||||||
 | 
					    triangles_by_color_raw.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::vector<std::vector<ExPolygons>> triangles_by_color_bottom(num_extruders);
 | 
					    std::vector<std::vector<ExPolygons>> triangles_by_color_bottom(num_extruders);
 | 
				
			||||||
    std::vector<std::vector<ExPolygons>> triangles_by_color_top(num_extruders);
 | 
					    std::vector<std::vector<ExPolygons>> triangles_by_color_top(num_extruders);
 | 
				
			||||||
    triangles_by_color_bottom.assign(num_extruders, std::vector<ExPolygons>(input_expolygons.size()));
 | 
					    triangles_by_color_bottom.assign(num_extruders, std::vector<ExPolygons>(num_layers));
 | 
				
			||||||
    triangles_by_color_top.assign(num_extruders, std::vector<ExPolygons>(input_expolygons.size()));
 | 
					    triangles_by_color_top.assign(num_extruders, std::vector<ExPolygons>(num_layers));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of top layer - begin";
 | 
					    BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of top layer - begin";
 | 
				
			||||||
    for (size_t layer_idx = 0; layer_idx < input_expolygons.size(); ++layer_idx) {
 | 
					    for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
 | 
				
			||||||
        float      extrusion_width  = scale_(get_extrusion_width(layer_idx));
 | 
					        float      extrusion_width  = scale_(get_extrusion_width(layer_idx));
 | 
				
			||||||
        int        top_solid_layers = get_top_solid_layers(layer_idx);
 | 
					        int        top_solid_layers = get_top_solid_layers(layer_idx);
 | 
				
			||||||
        ExPolygons top_expolygon    = top_layers[layer_idx];
 | 
					        ExPolygons top_expolygon    = top_layers[layer_idx];
 | 
				
			||||||
| 
						 | 
					@ -1312,7 +1318,7 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
 | 
				
			||||||
    BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of top layer - end";
 | 
					    BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of top layer - end";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of bottom layer - begin";
 | 
					    BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of bottom layer - begin";
 | 
				
			||||||
    for (size_t layer_idx = 0; layer_idx < input_expolygons.size(); ++layer_idx) {
 | 
					    for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
 | 
				
			||||||
        float             extrusion_width     = scale_(get_extrusion_width(layer_idx));
 | 
					        float             extrusion_width     = scale_(get_extrusion_width(layer_idx));
 | 
				
			||||||
        int               bottom_solid_layers = get_bottom_solid_layers(layer_idx);
 | 
					        int               bottom_solid_layers = get_bottom_solid_layers(layer_idx);
 | 
				
			||||||
        const ExPolygons &bottom_expolygon    = bottom_layers[layer_idx];
 | 
					        const ExPolygons &bottom_expolygon    = bottom_layers[layer_idx];
 | 
				
			||||||
| 
						 | 
					@ -1328,7 +1334,7 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
 | 
				
			||||||
            if (!intersection_poly.empty()) {
 | 
					            if (!intersection_poly.empty()) {
 | 
				
			||||||
                triangles_by_color_bottom[color_idx][layer_idx].insert(triangles_by_color_bottom[color_idx][layer_idx].end(), intersection_poly.begin(),
 | 
					                triangles_by_color_bottom[color_idx][layer_idx].insert(triangles_by_color_bottom[color_idx][layer_idx].end(), intersection_poly.begin(),
 | 
				
			||||||
                                                                       intersection_poly.end());
 | 
					                                                                       intersection_poly.end());
 | 
				
			||||||
                for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + bottom_solid_layers, input_expolygons.size()); ++last_idx) {
 | 
					                for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + bottom_solid_layers, num_layers); ++last_idx) {
 | 
				
			||||||
                    float offset_value = float(last_idx - layer_idx) * (-1.0f) * extrusion_width;
 | 
					                    float offset_value = float(last_idx - layer_idx) * (-1.0f) * extrusion_width;
 | 
				
			||||||
                    if (offset_ex(bottom_expolygon, offset_value).empty())
 | 
					                    if (offset_ex(bottom_expolygon, offset_value).empty())
 | 
				
			||||||
                        continue;
 | 
					                        continue;
 | 
				
			||||||
| 
						 | 
					@ -1347,8 +1353,8 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
 | 
				
			||||||
    BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of bottom layer - end";
 | 
					    BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - segmentation of bottom layer - end";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::vector<std::vector<ExPolygons>> triangles_by_color_merged(num_extruders);
 | 
					    std::vector<std::vector<ExPolygons>> triangles_by_color_merged(num_extruders);
 | 
				
			||||||
    triangles_by_color_merged.assign(num_extruders, std::vector<ExPolygons>(input_expolygons.size()));
 | 
					    triangles_by_color_merged.assign(num_extruders, std::vector<ExPolygons>(num_layers));
 | 
				
			||||||
    for (size_t layer_idx = 0; layer_idx < input_expolygons.size(); ++layer_idx) {
 | 
					    for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) {
 | 
				
			||||||
        throw_on_cancel_callback();
 | 
					        throw_on_cancel_callback();
 | 
				
			||||||
        for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) {
 | 
					        for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) {
 | 
				
			||||||
            auto &self = triangles_by_color_merged[color_idx][layer_idx];
 | 
					            auto &self = triangles_by_color_merged[color_idx][layer_idx];
 | 
				
			||||||
| 
						 | 
					@ -1363,6 +1369,169 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
 | 
				
			||||||
                                                                      triangles_by_color_merged[color_idx - 1][layer_idx]);
 | 
					                                                                      triangles_by_color_merged[color_idx - 1][layer_idx]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Maximum number of top / bottom layers accounts for maximum overlap of one thread group into a neighbor thread group.
 | 
				
			||||||
 | 
					    int max_top_layers = 0;
 | 
				
			||||||
 | 
					    int max_bottom_layers = 0;
 | 
				
			||||||
 | 
					    int granularity = 1;
 | 
				
			||||||
 | 
					    for (size_t i = 0; i < print_object.num_printing_regions(); ++ i) {
 | 
				
			||||||
 | 
					        const PrintRegionConfig &config = print_object.printing_region(i).config();
 | 
				
			||||||
 | 
					        max_top_layers    = std::max(max_top_layers, config.top_solid_layers.value);
 | 
				
			||||||
 | 
					        max_bottom_layers = std::max(max_bottom_layers, config.bottom_solid_layers.value);
 | 
				
			||||||
 | 
					        granularity       = std::max(granularity, std::max(config.top_solid_layers.value, config.bottom_solid_layers.value) - 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Project upwards pointing painted triangles over top surfaces,
 | 
				
			||||||
 | 
					    // project downards pointing painted triangles over bottom surfaces.
 | 
				
			||||||
 | 
					    std::vector<std::vector<Polygons>> top_raw(num_extruders), bottom_raw(num_extruders);
 | 
				
			||||||
 | 
					    std::vector<float> zs = zs_from_layers(print_object.layers());
 | 
				
			||||||
 | 
					    Transform3d        object_trafo = print_object.trafo();
 | 
				
			||||||
 | 
					    object_trafo.pretranslate(Vec3d(- unscale<double>(print_object.center_offset().x()), - unscale<double>(print_object.center_offset().y()), 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
 | 
				
			||||||
 | 
					    static int iRun = 0;
 | 
				
			||||||
 | 
					#endif // NDEBUG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (max_top_layers > 0 || max_bottom_layers > 0) {
 | 
				
			||||||
 | 
					        for (const ModelVolume *mv : print_object.model_object()->volumes)
 | 
				
			||||||
 | 
					            if (mv->is_model_part()) {
 | 
				
			||||||
 | 
					                const Transform3d volume_trafo = object_trafo * mv->get_matrix();
 | 
				
			||||||
 | 
					                for (size_t extruder_idx = 0; extruder_idx < num_extruders; ++ extruder_idx) {
 | 
				
			||||||
 | 
					                    const indexed_triangle_set painted = mv->mmu_segmentation_facets.get_facets_strict(*mv, EnforcerBlockerType(extruder_idx));
 | 
				
			||||||
 | 
					#ifdef MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        static int iRun = 0;
 | 
				
			||||||
 | 
					                        its_write_obj(painted, debug_out_path("mm-painted-patch-%d-%d.obj", iRun ++, extruder_idx).c_str());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					#endif // MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
 | 
				
			||||||
 | 
					                    if (! painted.indices.empty()) {
 | 
				
			||||||
 | 
					                        std::vector<Polygons> top, bottom;
 | 
				
			||||||
 | 
					                        slice_mesh_slabs(painted, zs, volume_trafo, max_top_layers > 0 ? &top : nullptr, max_bottom_layers > 0 ? &bottom : nullptr, throw_on_cancel_callback);
 | 
				
			||||||
 | 
					                        auto merge = [](std::vector<Polygons> &&src, std::vector<Polygons> &dst) {
 | 
				
			||||||
 | 
					                            auto it_src = find_if(src.begin(), src.end(), [](const Polygons &p){ return ! p.empty(); });
 | 
				
			||||||
 | 
					                            if (it_src != src.end()) {
 | 
				
			||||||
 | 
					                                if (dst.empty()) {
 | 
				
			||||||
 | 
					                                    dst = std::move(src);
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    assert(src.size() == dst.size());
 | 
				
			||||||
 | 
					                                    auto it_dst = dst.begin() + (it_src - src.begin());
 | 
				
			||||||
 | 
					                                    for (; it_src != src.end(); ++ it_src, ++ it_dst)
 | 
				
			||||||
 | 
					                                        if (! it_src->empty()) {
 | 
				
			||||||
 | 
					                                            if (it_dst->empty())
 | 
				
			||||||
 | 
					                                                *it_dst = std::move(*it_src);
 | 
				
			||||||
 | 
					                                            else
 | 
				
			||||||
 | 
					                                                append(*it_dst, std::move(*it_src));
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
 | 
					                        merge(std::move(top),    top_raw[extruder_idx]);
 | 
				
			||||||
 | 
					                        merge(std::move(bottom), bottom_raw[extruder_idx]);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const char* colors[] = { "aqua", "black", "blue", "fuchsia", "gray", "green", "lime", "maroon", "navy", "olive", "purple", "red", "silver", "teal", "yellow" };
 | 
				
			||||||
 | 
					        static int iRun = 0;
 | 
				
			||||||
 | 
					        for (size_t layer_id = 0; layer_id < zs.size(); ++layer_id) {
 | 
				
			||||||
 | 
					            std::vector<std::pair<Slic3r::ExPolygons, SVG::ExPolygonAttributes>> svg;
 | 
				
			||||||
 | 
					            for (size_t extruder_idx = 0; extruder_idx < num_extruders; ++ extruder_idx) {
 | 
				
			||||||
 | 
					                if (! top_raw[extruder_idx].empty() && ! top_raw[extruder_idx][layer_id].empty())
 | 
				
			||||||
 | 
					                    if (ExPolygons expoly = union_ex(top_raw[extruder_idx][layer_id]); ! expoly.empty()) {
 | 
				
			||||||
 | 
					                        const char *color = colors[extruder_idx];
 | 
				
			||||||
 | 
					                        svg.emplace_back(expoly, SVG::ExPolygonAttributes{ format("top%d", extruder_idx), color, color, color });
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                if (! bottom_raw[extruder_idx].empty() && ! bottom_raw[extruder_idx][layer_id].empty())
 | 
				
			||||||
 | 
					                    if (ExPolygons expoly = union_ex(bottom_raw[extruder_idx][layer_id]); ! expoly.empty()) {
 | 
				
			||||||
 | 
					                        const char *color = colors[extruder_idx + 8];
 | 
				
			||||||
 | 
					                        svg.emplace_back(expoly, SVG::ExPolygonAttributes{ format("bottom%d", extruder_idx), color, color, color });
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            SVG::export_expolygons(debug_out_path("mm-segmentation-top-bottom-%d-%d-%lf.svg", iRun, layer_id, zs[layer_id]), svg);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ++ iRun;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#endif // MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<std::vector<ExPolygons>> triangles_by_color_bottom(num_extruders);
 | 
				
			||||||
 | 
					    std::vector<std::vector<ExPolygons>> triangles_by_color_top(num_extruders);
 | 
				
			||||||
 | 
					    triangles_by_color_bottom.assign(num_extruders, std::vector<ExPolygons>(num_layers * 2));
 | 
				
			||||||
 | 
					    triangles_by_color_top.assign(num_extruders, std::vector<ExPolygons>(num_layers * 2));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tbb::parallel_for(tbb::blocked_range<size_t>(0, num_layers, granularity), [&](const tbb::blocked_range<size_t> &range) {
 | 
				
			||||||
 | 
					        size_t group_idx   = range.begin() / granularity;
 | 
				
			||||||
 | 
					        size_t layer_idx_offset = (group_idx & 1) * num_layers;
 | 
				
			||||||
 | 
					        for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
 | 
				
			||||||
 | 
					            float   extrusion_width     = scale_(get_extrusion_width(layer_idx));
 | 
				
			||||||
 | 
					            int     top_solid_layers    = get_top_solid_layers(layer_idx);
 | 
				
			||||||
 | 
					            int     bottom_solid_layers = get_bottom_solid_layers(layer_idx);
 | 
				
			||||||
 | 
					            float   narrow_island_width = 0.1f * float(extrusion_width);
 | 
				
			||||||
 | 
					            for (size_t color_idx = 0; color_idx < num_extruders; ++ color_idx) {
 | 
				
			||||||
 | 
					                throw_on_cancel_callback();
 | 
				
			||||||
 | 
					                if (std::vector<Polygons> &top = top_raw[color_idx]; ! top.empty() && ! top[layer_idx].empty())
 | 
				
			||||||
 | 
					                    if (ExPolygons top_ex = union_ex(top[layer_idx]); ! top_ex.empty()) {
 | 
				
			||||||
 | 
					                        // Clean up thin projections. They are not printable anyways.
 | 
				
			||||||
 | 
					                        top_ex = offset2_ex(top_ex, - narrow_island_width, + narrow_island_width);
 | 
				
			||||||
 | 
					                        if (! top_ex.empty()) {
 | 
				
			||||||
 | 
					                            append(triangles_by_color_top[color_idx][layer_idx + layer_idx_offset], top_ex);
 | 
				
			||||||
 | 
					                            float offset = 0.f;
 | 
				
			||||||
 | 
					                            ExPolygons layer_slices_trimmed = input_expolygons[layer_idx];
 | 
				
			||||||
 | 
					                            for (int last_idx = int(layer_idx) - 1; last_idx >= std::max(int(layer_idx - top_solid_layers), int(0)); --last_idx) {
 | 
				
			||||||
 | 
					                                offset -= extrusion_width;
 | 
				
			||||||
 | 
					                                layer_slices_trimmed = intersection_ex(layer_slices_trimmed, input_expolygons[last_idx]);
 | 
				
			||||||
 | 
					                                ExPolygons last = intersection_ex(top_ex, offset_ex(layer_slices_trimmed, offset));
 | 
				
			||||||
 | 
					                                if (last.empty())
 | 
				
			||||||
 | 
					                                    break;
 | 
				
			||||||
 | 
					                                append(triangles_by_color_top[color_idx][last_idx + layer_idx_offset], std::move(last));
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                if (std::vector<Polygons> &bottom = bottom_raw[color_idx]; ! bottom.empty() && ! bottom[layer_idx].empty())
 | 
				
			||||||
 | 
					                    if (ExPolygons bottom_ex = union_ex(bottom[layer_idx]); ! bottom_ex.empty()) {
 | 
				
			||||||
 | 
					                        // Clean up thin projections. They are not printable anyways.
 | 
				
			||||||
 | 
					                        bottom_ex = offset2_ex(bottom_ex, - narrow_island_width, + narrow_island_width);
 | 
				
			||||||
 | 
					                        if (! bottom_ex.empty()) {
 | 
				
			||||||
 | 
					                            append(triangles_by_color_bottom[color_idx][layer_idx + layer_idx_offset], bottom_ex);
 | 
				
			||||||
 | 
					                            float offset = 0.f;
 | 
				
			||||||
 | 
					                            ExPolygons layer_slices_trimmed = input_expolygons[layer_idx];
 | 
				
			||||||
 | 
					                            for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + bottom_solid_layers, num_layers); ++last_idx) {
 | 
				
			||||||
 | 
					                                offset -= extrusion_width;
 | 
				
			||||||
 | 
					                                layer_slices_trimmed = intersection_ex(layer_slices_trimmed, input_expolygons[last_idx]);
 | 
				
			||||||
 | 
					                                ExPolygons last = intersection_ex(bottom_ex, offset_ex(layer_slices_trimmed, offset));
 | 
				
			||||||
 | 
					                                if (last.empty())
 | 
				
			||||||
 | 
					                                    break;
 | 
				
			||||||
 | 
					                                append(triangles_by_color_bottom[color_idx][last_idx + layer_idx_offset], std::move(last));
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<std::vector<ExPolygons>> triangles_by_color_merged(num_extruders);
 | 
				
			||||||
 | 
					    triangles_by_color_merged.assign(num_extruders, std::vector<ExPolygons>(num_layers));
 | 
				
			||||||
 | 
					    tbb::parallel_for(tbb::blocked_range<size_t>(0, num_layers), [&](const tbb::blocked_range<size_t> &range) {
 | 
				
			||||||
 | 
					        for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
 | 
				
			||||||
 | 
					            throw_on_cancel_callback();
 | 
				
			||||||
 | 
					            for (size_t color_idx = 0; color_idx < triangles_by_color_merged.size(); ++color_idx) {
 | 
				
			||||||
 | 
					                auto &self = triangles_by_color_merged[color_idx][layer_idx];
 | 
				
			||||||
 | 
					                append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx]));
 | 
				
			||||||
 | 
					                append(self, std::move(triangles_by_color_bottom[color_idx][layer_idx + num_layers]));
 | 
				
			||||||
 | 
					                append(self, std::move(triangles_by_color_top[color_idx][layer_idx]));
 | 
				
			||||||
 | 
					                append(self, std::move(triangles_by_color_top[color_idx][layer_idx + num_layers]));
 | 
				
			||||||
 | 
					                self = union_ex(self);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // Trim one region by the other if some of the regions overlap.
 | 
				
			||||||
 | 
					            for (size_t color_idx = 1; color_idx < triangles_by_color_merged.size(); ++ color_idx)
 | 
				
			||||||
 | 
					                triangles_by_color_merged[color_idx][layer_idx] = diff_ex(triangles_by_color_merged[color_idx][layer_idx],
 | 
				
			||||||
 | 
					                                                                          triangles_by_color_merged[color_idx - 1][layer_idx]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return triangles_by_color_merged;
 | 
					    return triangles_by_color_merged;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -655,10 +655,8 @@ std::string Print::validate(std::string* warning) const
 | 
				
			||||||
            // Notify the user in that case.
 | 
					            // Notify the user in that case.
 | 
				
			||||||
            if (! object->has_support() && warning) {
 | 
					            if (! object->has_support() && warning) {
 | 
				
			||||||
                for (const ModelVolume* mv : object->model_object()->volumes) {
 | 
					                for (const ModelVolume* mv : object->model_object()->volumes) {
 | 
				
			||||||
                    bool has_enforcers = mv->is_support_enforcer()
 | 
					                    bool has_enforcers = mv->is_support_enforcer() || 
 | 
				
			||||||
                        || (mv->is_model_part()
 | 
					                        (mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER));
 | 
				
			||||||
                            && ! mv->supported_facets.empty()
 | 
					 | 
				
			||||||
                            && ! mv->supported_facets.get_facets(*mv, EnforcerBlockerType::ENFORCER).indices.empty());
 | 
					 | 
				
			||||||
                    if (has_enforcers) {
 | 
					                    if (has_enforcers) {
 | 
				
			||||||
                        *warning = "_SUPPORTS_OFF";
 | 
					                        *warning = "_SUPPORTS_OFF";
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -326,12 +326,12 @@ public:
 | 
				
			||||||
    void slice();
 | 
					    void slice();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Helpers to slice support enforcer / blocker meshes by the support generator.
 | 
					    // Helpers to slice support enforcer / blocker meshes by the support generator.
 | 
				
			||||||
    std::vector<ExPolygons>     slice_support_volumes(const ModelVolumeType model_volume_type) const;
 | 
					    std::vector<Polygons>       slice_support_volumes(const ModelVolumeType model_volume_type) const;
 | 
				
			||||||
    std::vector<ExPolygons>     slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); }
 | 
					    std::vector<Polygons>       slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); }
 | 
				
			||||||
    std::vector<ExPolygons>     slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
 | 
					    std::vector<Polygons>       slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Helpers to project custom facets on slices
 | 
					    // Helpers to project custom facets on slices
 | 
				
			||||||
    void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector<ExPolygons>& expolys) const;
 | 
					    void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector<Polygons>& expolys) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    // to be called from Print only.
 | 
					    // to be called from Print only.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -402,10 +402,8 @@ void PrintObject::generate_support_material()
 | 
				
			||||||
            // Notify the user in that case.
 | 
					            // Notify the user in that case.
 | 
				
			||||||
            if (! this->has_support()) {
 | 
					            if (! this->has_support()) {
 | 
				
			||||||
                for (const ModelVolume* mv : this->model_object()->volumes) {
 | 
					                for (const ModelVolume* mv : this->model_object()->volumes) {
 | 
				
			||||||
                    bool has_enforcers = mv->is_support_enforcer()
 | 
					                    bool has_enforcers = mv->is_support_enforcer() || 
 | 
				
			||||||
                        || (mv->is_model_part()
 | 
					                        (mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER));
 | 
				
			||||||
                            && ! mv->supported_facets.empty()
 | 
					 | 
				
			||||||
                            && ! mv->supported_facets.get_facets(*mv, EnforcerBlockerType::ENFORCER).indices.empty());
 | 
					 | 
				
			||||||
                    if (has_enforcers) {
 | 
					                    if (has_enforcers) {
 | 
				
			||||||
                        this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
 | 
					                        this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
 | 
				
			||||||
                            L("An object has custom support enforcers which will not be used "
 | 
					                            L("An object has custom support enforcers which will not be used "
 | 
				
			||||||
| 
						 | 
					@ -2101,206 +2099,206 @@ void PrintObject::_generate_support_material()
 | 
				
			||||||
    support_material.generate(*this);
 | 
					    support_material.generate(*this);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const indexed_triangle_set &custom_facets, const Transform3f &tr, bool seam, std::vector<Polygons> &out)
 | 
				
			||||||
void PrintObject::project_and_append_custom_facets(
 | 
					 | 
				
			||||||
        bool seam, EnforcerBlockerType type, std::vector<ExPolygons>& expolys) const
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    for (const ModelVolume* mv : this->model_object()->volumes) {
 | 
					    if (custom_facets.indices.empty())
 | 
				
			||||||
        const indexed_triangle_set custom_facets = seam
 | 
					        return;
 | 
				
			||||||
                ? mv->seam_facets.get_facets(*mv, type)
 | 
					
 | 
				
			||||||
                : mv->supported_facets.get_facets(*mv, type);
 | 
					    const float tr_det_sign = (tr.matrix().determinant() > 0. ? 1.f : -1.f);
 | 
				
			||||||
        if (! mv->is_model_part() || custom_facets.indices.empty())
 | 
					
 | 
				
			||||||
 | 
					    // The projection will be at most a pentagon. Let's minimize heap
 | 
				
			||||||
 | 
					    // reallocations by saving in in the following struct.
 | 
				
			||||||
 | 
					    // Points are used so that scaling can be done in parallel
 | 
				
			||||||
 | 
					    // and they can be moved from to create an ExPolygon later.
 | 
				
			||||||
 | 
					    struct LightPolygon {
 | 
				
			||||||
 | 
					        LightPolygon() { pts.reserve(5); }
 | 
				
			||||||
 | 
					        LightPolygon(const std::array<Vec2f, 3>& tri) {
 | 
				
			||||||
 | 
					            pts.reserve(3);
 | 
				
			||||||
 | 
					            pts.emplace_back(scaled<coord_t>(tri.front()));
 | 
				
			||||||
 | 
					            pts.emplace_back(scaled<coord_t>(tri[1]));
 | 
				
			||||||
 | 
					            pts.emplace_back(scaled<coord_t>(tri.back()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Points pts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void add(const Vec2f& pt) {
 | 
				
			||||||
 | 
					            pts.emplace_back(scaled<coord_t>(pt));
 | 
				
			||||||
 | 
					            assert(pts.size() <= 5);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Structure to collect projected polygons. One element for each triangle.
 | 
				
			||||||
 | 
					    // Saves vector of polygons and layer_id of the first one.
 | 
				
			||||||
 | 
					    struct TriangleProjections {
 | 
				
			||||||
 | 
					        size_t first_layer_id;
 | 
				
			||||||
 | 
					        std::vector<LightPolygon> polygons;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Vector to collect resulting projections from each triangle.
 | 
				
			||||||
 | 
					    std::vector<TriangleProjections> projections_of_triangles(custom_facets.indices.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Iterate over all triangles.
 | 
				
			||||||
 | 
					    tbb::parallel_for(
 | 
				
			||||||
 | 
					        tbb::blocked_range<size_t>(0, custom_facets.indices.size()),
 | 
				
			||||||
 | 
					        [&custom_facets, &tr, tr_det_sign, seam, layers, &projections_of_triangles](const tbb::blocked_range<size_t>& range) {
 | 
				
			||||||
 | 
					        for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::array<Vec3f, 3> facet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Transform the triangle into worlds coords.
 | 
				
			||||||
 | 
					        for (int i=0; i<3; ++i)
 | 
				
			||||||
 | 
					            facet[i] = tr * custom_facets.vertices[custom_facets.indices[idx](i)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Ignore triangles with upward-pointing normal. Don't forget about mirroring.
 | 
				
			||||||
 | 
					        float z_comp = (facet[1]-facet[0]).cross(facet[2]-facet[0]).z();
 | 
				
			||||||
 | 
					        if (! seam && tr_det_sign * z_comp > 0.)
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const Transform3f& tr1 = mv->get_matrix().cast<float>();
 | 
					        // The algorithm does not process vertical triangles, but it should for seam.
 | 
				
			||||||
        const Transform3f& tr2 = this->trafo().cast<float>();
 | 
					        // In that case, tilt the triangle a bit so the projection does not degenerate.
 | 
				
			||||||
        const Transform3f  tr  = tr2 * tr1;
 | 
					        if (seam && z_comp == 0.f)
 | 
				
			||||||
        const float        tr_det_sign = (tr.matrix().determinant() > 0. ? 1.f : -1.f);
 | 
					            facet[0].x() += float(EPSILON);
 | 
				
			||||||
        const Vec2f        center = unscaled<float>(this->center_offset());
 | 
					 | 
				
			||||||
        ConstLayerPtrsAdaptor layers = this->layers();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // The projection will be at most a pentagon. Let's minimize heap
 | 
					        // Sort the three vertices according to z-coordinate.
 | 
				
			||||||
        // reallocations by saving in in the following struct.
 | 
					        std::sort(facet.begin(), facet.end(),
 | 
				
			||||||
        // Points are used so that scaling can be done in parallel
 | 
					                  [](const Vec3f& pt1, const Vec3f&pt2) {
 | 
				
			||||||
        // and they can be moved from to create an ExPolygon later.
 | 
					                      return pt1.z() < pt2.z();
 | 
				
			||||||
        struct LightPolygon {
 | 
					                  });
 | 
				
			||||||
            LightPolygon() { pts.reserve(5); }
 | 
					 | 
				
			||||||
            LightPolygon(const std::array<Vec2f, 3>& tri) {
 | 
					 | 
				
			||||||
                pts.reserve(3);
 | 
					 | 
				
			||||||
                pts.emplace_back(scaled<coord_t>(tri.front()));
 | 
					 | 
				
			||||||
                pts.emplace_back(scaled<coord_t>(tri[1]));
 | 
					 | 
				
			||||||
                pts.emplace_back(scaled<coord_t>(tri.back()));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Points pts;
 | 
					        std::array<Vec2f, 3> trianglef;
 | 
				
			||||||
 | 
					        for (int i=0; i<3; ++i)
 | 
				
			||||||
 | 
					            trianglef[i] = to_2d(facet[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            void add(const Vec2f& pt) {
 | 
					        // Find lowest slice not below the triangle.
 | 
				
			||||||
                pts.emplace_back(scaled<coord_t>(pt));
 | 
					        auto it = std::lower_bound(layers.begin(), layers.end(), facet[0].z()+EPSILON,
 | 
				
			||||||
                assert(pts.size() <= 5);
 | 
					                      [](const Layer* l1, float z) {
 | 
				
			||||||
            }
 | 
					                           return l1->slice_z < z;
 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Structure to collect projected polygons. One element for each triangle.
 | 
					 | 
				
			||||||
        // Saves vector of polygons and layer_id of the first one.
 | 
					 | 
				
			||||||
        struct TriangleProjections {
 | 
					 | 
				
			||||||
            size_t first_layer_id;
 | 
					 | 
				
			||||||
            std::vector<LightPolygon> polygons;
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Vector to collect resulting projections from each triangle.
 | 
					 | 
				
			||||||
        std::vector<TriangleProjections> projections_of_triangles(custom_facets.indices.size());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Iterate over all triangles.
 | 
					 | 
				
			||||||
        tbb::parallel_for(
 | 
					 | 
				
			||||||
            tbb::blocked_range<size_t>(0, custom_facets.indices.size()),
 | 
					 | 
				
			||||||
            [center, &custom_facets, &tr, tr_det_sign, seam, layers, &projections_of_triangles](const tbb::blocked_range<size_t>& range) {
 | 
					 | 
				
			||||||
            for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            std::array<Vec3f, 3> facet;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Transform the triangle into worlds coords.
 | 
					 | 
				
			||||||
            for (int i=0; i<3; ++i)
 | 
					 | 
				
			||||||
                facet[i] = tr * custom_facets.vertices[custom_facets.indices[idx](i)];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Ignore triangles with upward-pointing normal. Don't forget about mirroring.
 | 
					 | 
				
			||||||
            float z_comp = (facet[1]-facet[0]).cross(facet[2]-facet[0]).z();
 | 
					 | 
				
			||||||
            if (! seam && tr_det_sign * z_comp > 0.)
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // The algorithm does not process vertical triangles, but it should for seam.
 | 
					 | 
				
			||||||
            // In that case, tilt the triangle a bit so the projection does not degenerate.
 | 
					 | 
				
			||||||
            if (seam && z_comp == 0.f)
 | 
					 | 
				
			||||||
                facet[0].x() += float(EPSILON);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Sort the three vertices according to z-coordinate.
 | 
					 | 
				
			||||||
            std::sort(facet.begin(), facet.end(),
 | 
					 | 
				
			||||||
                      [](const Vec3f& pt1, const Vec3f&pt2) {
 | 
					 | 
				
			||||||
                          return pt1.z() < pt2.z();
 | 
					 | 
				
			||||||
                      });
 | 
					                      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            std::array<Vec2f, 3> trianglef;
 | 
					        // Count how many projections will be generated for this triangle
 | 
				
			||||||
            for (int i=0; i<3; ++i)
 | 
					        // and allocate respective amount in projections_of_triangles.
 | 
				
			||||||
                trianglef[i] = to_2d(facet[i]) - center;
 | 
					        size_t first_layer_id = projections_of_triangles[idx].first_layer_id = it - layers.begin();
 | 
				
			||||||
 | 
					        size_t last_layer_id  = first_layer_id;
 | 
				
			||||||
 | 
					        // The cast in the condition below is important. The comparison must
 | 
				
			||||||
 | 
					        // be an exact opposite of the one lower in the code where
 | 
				
			||||||
 | 
					        // the polygons are appended. And that one is on floats.
 | 
				
			||||||
 | 
					        while (last_layer_id + 1 < layers.size()
 | 
				
			||||||
 | 
					            && float(layers[last_layer_id]->slice_z) <= facet[2].z())
 | 
				
			||||||
 | 
					            ++last_layer_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Find lowest slice not below the triangle.
 | 
					        if (first_layer_id == last_layer_id) {
 | 
				
			||||||
            auto it = std::lower_bound(layers.begin(), layers.end(), facet[0].z()+EPSILON,
 | 
					            // The triangle fits just a single slab, just project it. This also avoids division by zero for horizontal triangles.
 | 
				
			||||||
                          [](const Layer* l1, float z) {
 | 
					            float dz = facet[2].z() - facet[0].z();
 | 
				
			||||||
                               return l1->slice_z < z;
 | 
					            assert(dz >= 0);
 | 
				
			||||||
                          });
 | 
					            // The face is nearly horizontal and it crosses the slicing plane at first_layer_id - 1.
 | 
				
			||||||
 | 
					            // Rather add this face to both the planes.
 | 
				
			||||||
            // Count how many projections will be generated for this triangle
 | 
					            bool add_below = dz < float(2. * EPSILON) && first_layer_id > 0 && layers[first_layer_id - 1]->slice_z > facet[0].z() - EPSILON;
 | 
				
			||||||
            // and allocate respective amount in projections_of_triangles.
 | 
					            projections_of_triangles[idx].polygons.reserve(add_below ? 2 : 1);
 | 
				
			||||||
            size_t first_layer_id = projections_of_triangles[idx].first_layer_id = it - layers.begin();
 | 
					            projections_of_triangles[idx].polygons.emplace_back(trianglef);
 | 
				
			||||||
            size_t last_layer_id  = first_layer_id;
 | 
					            if (add_below) {
 | 
				
			||||||
            // The cast in the condition below is important. The comparison must
 | 
					                -- projections_of_triangles[idx].first_layer_id;
 | 
				
			||||||
            // be an exact opposite of the one lower in the code where
 | 
					 | 
				
			||||||
            // the polygons are appended. And that one is on floats.
 | 
					 | 
				
			||||||
            while (last_layer_id + 1 < layers.size()
 | 
					 | 
				
			||||||
                && float(layers[last_layer_id]->slice_z) <= facet[2].z())
 | 
					 | 
				
			||||||
                ++last_layer_id;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (first_layer_id == last_layer_id) {
 | 
					 | 
				
			||||||
                // The triangle fits just a single slab, just project it. This also avoids division by zero for horizontal triangles.
 | 
					 | 
				
			||||||
                float dz = facet[2].z() - facet[0].z();
 | 
					 | 
				
			||||||
                assert(dz >= 0);
 | 
					 | 
				
			||||||
                // The face is nearly horizontal and it crosses the slicing plane at first_layer_id - 1.
 | 
					 | 
				
			||||||
                // Rather add this face to both the planes.
 | 
					 | 
				
			||||||
                bool add_below = dz < float(2. * EPSILON) && first_layer_id > 0 && layers[first_layer_id - 1]->slice_z > facet[0].z() - EPSILON;
 | 
					 | 
				
			||||||
                projections_of_triangles[idx].polygons.reserve(add_below ? 2 : 1);
 | 
					 | 
				
			||||||
                projections_of_triangles[idx].polygons.emplace_back(trianglef);
 | 
					                projections_of_triangles[idx].polygons.emplace_back(trianglef);
 | 
				
			||||||
                if (add_below) {
 | 
					 | 
				
			||||||
                    -- projections_of_triangles[idx].first_layer_id;
 | 
					 | 
				
			||||||
                    projections_of_triangles[idx].polygons.emplace_back(trianglef);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            projections_of_triangles[idx].polygons.resize(last_layer_id - first_layer_id + 1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Calculate how to move points on triangle sides per unit z increment.
 | 
					 | 
				
			||||||
            Vec2f ta(trianglef[1] - trianglef[0]);
 | 
					 | 
				
			||||||
            Vec2f tb(trianglef[2] - trianglef[0]);
 | 
					 | 
				
			||||||
            ta *= 1.f/(facet[1].z() - facet[0].z());
 | 
					 | 
				
			||||||
            tb *= 1.f/(facet[2].z() - facet[0].z());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Projection on current slice will be build directly in place.
 | 
					 | 
				
			||||||
            LightPolygon* proj = &projections_of_triangles[idx].polygons[0];
 | 
					 | 
				
			||||||
            proj->add(trianglef[0]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            bool passed_first = false;
 | 
					 | 
				
			||||||
            bool stop = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Project a sub-polygon on all slices intersecting the triangle.
 | 
					 | 
				
			||||||
            while (it != layers.end()) {
 | 
					 | 
				
			||||||
                const float z = float((*it)->slice_z);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Projections of triangle sides intersections with slices.
 | 
					 | 
				
			||||||
                // a moves along one side, b tracks the other.
 | 
					 | 
				
			||||||
                Vec2f a;
 | 
					 | 
				
			||||||
                Vec2f b;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // If the middle vertex was already passed, append the vertex
 | 
					 | 
				
			||||||
                // and use ta for tracking the remaining side.
 | 
					 | 
				
			||||||
                if (z > facet[1].z() && ! passed_first) {
 | 
					 | 
				
			||||||
                    proj->add(trianglef[1]);
 | 
					 | 
				
			||||||
                    ta = trianglef[2]-trianglef[1];
 | 
					 | 
				
			||||||
                    ta *= 1.f/(facet[2].z() - facet[1].z());
 | 
					 | 
				
			||||||
                    passed_first = true;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // This slice is above the triangle already.
 | 
					 | 
				
			||||||
                if (z > facet[2].z() || it+1 == layers.end()) {
 | 
					 | 
				
			||||||
                    proj->add(trianglef[2]);
 | 
					 | 
				
			||||||
                    stop = true;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else {
 | 
					 | 
				
			||||||
                    // Move a, b along the side it currently tracks to get
 | 
					 | 
				
			||||||
                    // projected intersection with current slice.
 | 
					 | 
				
			||||||
                    a = passed_first ? (trianglef[1]+ta*(z-facet[1].z()))
 | 
					 | 
				
			||||||
                                     : (trianglef[0]+ta*(z-facet[0].z()));
 | 
					 | 
				
			||||||
                    b = trianglef[0]+tb*(z-facet[0].z());
 | 
					 | 
				
			||||||
                    proj->add(a);
 | 
					 | 
				
			||||||
                    proj->add(b);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
               if (stop)
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Advance to the next layer.
 | 
					 | 
				
			||||||
                ++it;
 | 
					 | 
				
			||||||
                ++proj;
 | 
					 | 
				
			||||||
                assert(proj <= &projections_of_triangles[idx].polygons.back() );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // a, b are first two points of the polygon for the next layer.
 | 
					 | 
				
			||||||
                proj->add(b);
 | 
					 | 
				
			||||||
                proj->add(a);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        }); // end of parallel_for
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Make sure that the output vector can be used.
 | 
					        projections_of_triangles[idx].polygons.resize(last_layer_id - first_layer_id + 1);
 | 
				
			||||||
        expolys.resize(layers.size());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Now append the collected polygons to respective layers.
 | 
					        // Calculate how to move points on triangle sides per unit z increment.
 | 
				
			||||||
        for (auto& trg : projections_of_triangles) {
 | 
					        Vec2f ta(trianglef[1] - trianglef[0]);
 | 
				
			||||||
            int layer_id = int(trg.first_layer_id);
 | 
					        Vec2f tb(trianglef[2] - trianglef[0]);
 | 
				
			||||||
            for (LightPolygon &poly : trg.polygons) {
 | 
					        ta *= 1.f/(facet[1].z() - facet[0].z());
 | 
				
			||||||
                if (layer_id >= int(expolys.size()))
 | 
					        tb *= 1.f/(facet[2].z() - facet[0].z());
 | 
				
			||||||
                    break; // part of triangle could be projected above top layer
 | 
					
 | 
				
			||||||
                assert(! poly.pts.empty());
 | 
					        // Projection on current slice will be built directly in place.
 | 
				
			||||||
                // The resulting triangles are fed to the Clipper library, which seem to handle flipped triangles well.
 | 
					        LightPolygon* proj = &projections_of_triangles[idx].polygons[0];
 | 
				
			||||||
 | 
					        proj->add(trianglef[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool passed_first = false;
 | 
				
			||||||
 | 
					        bool stop = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Project a sub-polygon on all slices intersecting the triangle.
 | 
				
			||||||
 | 
					        while (it != layers.end()) {
 | 
				
			||||||
 | 
					            const float z = float((*it)->slice_z);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Projections of triangle sides intersections with slices.
 | 
				
			||||||
 | 
					            // a moves along one side, b tracks the other.
 | 
				
			||||||
 | 
					            Vec2f a;
 | 
				
			||||||
 | 
					            Vec2f b;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // If the middle vertex was already passed, append the vertex
 | 
				
			||||||
 | 
					            // and use ta for tracking the remaining side.
 | 
				
			||||||
 | 
					            if (z > facet[1].z() && ! passed_first) {
 | 
				
			||||||
 | 
					                proj->add(trianglef[1]);
 | 
				
			||||||
 | 
					                ta = trianglef[2]-trianglef[1];
 | 
				
			||||||
 | 
					                ta *= 1.f/(facet[2].z() - facet[1].z());
 | 
				
			||||||
 | 
					                passed_first = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // This slice is above the triangle already.
 | 
				
			||||||
 | 
					            if (z > facet[2].z() || it+1 == layers.end()) {
 | 
				
			||||||
 | 
					                proj->add(trianglef[2]);
 | 
				
			||||||
 | 
					                stop = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                // Move a, b along the side it currently tracks to get
 | 
				
			||||||
 | 
					                // projected intersection with current slice.
 | 
				
			||||||
 | 
					                a = passed_first ? (trianglef[1]+ta*(z-facet[1].z()))
 | 
				
			||||||
 | 
					                                 : (trianglef[0]+ta*(z-facet[0].z()));
 | 
				
			||||||
 | 
					                b = trianglef[0]+tb*(z-facet[0].z());
 | 
				
			||||||
 | 
					                proj->add(a);
 | 
				
			||||||
 | 
					                proj->add(b);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					           if (stop)
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Advance to the next layer.
 | 
				
			||||||
 | 
					            ++it;
 | 
				
			||||||
 | 
					            ++proj;
 | 
				
			||||||
 | 
					            assert(proj <= &projections_of_triangles[idx].polygons.back() );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // a, b are first two points of the polygon for the next layer.
 | 
				
			||||||
 | 
					            proj->add(b);
 | 
				
			||||||
 | 
					            proj->add(a);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    }); // end of parallel_for
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Make sure that the output vector can be used.
 | 
				
			||||||
 | 
					    out.resize(layers.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Now append the collected polygons to respective layers.
 | 
				
			||||||
 | 
					    for (auto& trg : projections_of_triangles) {
 | 
				
			||||||
 | 
					        int layer_id = int(trg.first_layer_id);
 | 
				
			||||||
 | 
					        for (LightPolygon &poly : trg.polygons) {
 | 
				
			||||||
 | 
					            if (layer_id >= int(out.size()))
 | 
				
			||||||
 | 
					                break; // part of triangle could be projected above top layer
 | 
				
			||||||
 | 
					            assert(! poly.pts.empty());
 | 
				
			||||||
 | 
					            // The resulting triangles are fed to the Clipper library, which seem to handle flipped triangles well.
 | 
				
			||||||
//                if (cross2(Vec2d((poly.pts[1] - poly.pts[0]).cast<double>()), Vec2d((poly.pts[2] - poly.pts[1]).cast<double>())) < 0)
 | 
					//                if (cross2(Vec2d((poly.pts[1] - poly.pts[0]).cast<double>()), Vec2d((poly.pts[2] - poly.pts[1]).cast<double>())) < 0)
 | 
				
			||||||
//                    std::swap(poly.pts.front(), poly.pts.back());
 | 
					//                    std::swap(poly.pts.front(), poly.pts.back());
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                expolys[layer_id].emplace_back(std::move(poly.pts));
 | 
					            out[layer_id].emplace_back(std::move(poly.pts));
 | 
				
			||||||
                ++layer_id;
 | 
					            ++layer_id;
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    } // loop over ModelVolumes
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void PrintObject::project_and_append_custom_facets(
 | 
				
			||||||
 | 
					        bool seam, EnforcerBlockerType type, std::vector<Polygons>& out) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    for (const ModelVolume* mv : this->model_object()->volumes)
 | 
				
			||||||
 | 
					        if (mv->is_model_part()) {
 | 
				
			||||||
 | 
					            const indexed_triangle_set custom_facets = seam
 | 
				
			||||||
 | 
					                    ? mv->seam_facets.get_facets_strict(*mv, type)
 | 
				
			||||||
 | 
					                    : mv->supported_facets.get_facets_strict(*mv, type);
 | 
				
			||||||
 | 
					            if (! custom_facets.indices.empty())
 | 
				
			||||||
 | 
					                project_triangles_to_slabs(this->layers(), custom_facets, 
 | 
				
			||||||
 | 
					                    (Eigen::Translation3d(to_3d(unscaled<double>(this->center_offset()), 0.)) * this->trafo() * mv->get_matrix()).cast<float>(), 
 | 
				
			||||||
 | 
					                    seam, out);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Layer* PrintObject::get_layer_at_printz(coordf_t print_z) const {
 | 
					const Layer* PrintObject::get_layer_at_printz(coordf_t print_z) const {
 | 
				
			||||||
    auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [print_z](const Layer *layer) { return layer->print_z < print_z; });
 | 
					    auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [print_z](const Layer *layer) { return layer->print_z < print_z; });
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,16 +39,6 @@ LayerPtrs new_layers(
 | 
				
			||||||
    return out;
 | 
					    return out;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template<typename LayerContainer>
 | 
					 | 
				
			||||||
static inline std::vector<float> zs_from_layers(const LayerContainer &layers)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    std::vector<float> zs;
 | 
					 | 
				
			||||||
    zs.reserve(layers.size());
 | 
					 | 
				
			||||||
    for (const Layer *l : layers)
 | 
					 | 
				
			||||||
        zs.emplace_back((float)l->slice_z);
 | 
					 | 
				
			||||||
    return zs;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
 | 
					//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
 | 
				
			||||||
// This function will go away once we get rid of admesh from ModelVolume.
 | 
					// This function will go away once we get rid of admesh from ModelVolume.
 | 
				
			||||||
static indexed_triangle_set get_mesh_its_fix_mesh_connectivity(TriangleMesh mesh)
 | 
					static indexed_triangle_set get_mesh_its_fix_mesh_connectivity(TriangleMesh mesh)
 | 
				
			||||||
| 
						 | 
					@ -604,14 +594,18 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
 | 
				
			||||||
                if (! layer_split)
 | 
					                if (! layer_split)
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                // Split LayerRegions by by_extruder regions.
 | 
					                // Split LayerRegions by by_extruder regions.
 | 
				
			||||||
 | 
					                // layer_range.painted_regions are sorted by extruder ID and parent PrintObject region ID.
 | 
				
			||||||
                auto it_painted_region = layer_range.painted_regions.begin();
 | 
					                auto it_painted_region = layer_range.painted_regions.begin();
 | 
				
			||||||
                for (int region_id = 0; region_id < int(layer->region_count()); ++ region_id)
 | 
					                for (int region_id = 0; region_id < int(layer->region_count()); ++ region_id)
 | 
				
			||||||
                    if (LayerRegion &layerm = *layer->get_region(region_id); ! layerm.slices.surfaces.empty()) {
 | 
					                    if (LayerRegion &layerm = *layer->get_region(region_id); ! layerm.slices.surfaces.empty()) {
 | 
				
			||||||
 | 
					                        assert(layerm.region().print_object_region_id() == region_id);
 | 
				
			||||||
                        const BoundingBox bbox = get_extents(layerm.slices.surfaces);
 | 
					                        const BoundingBox bbox = get_extents(layerm.slices.surfaces);
 | 
				
			||||||
                        assert(it_painted_region < layer_range.painted_regions.end());
 | 
					                        assert(it_painted_region < layer_range.painted_regions.end());
 | 
				
			||||||
 | 
					                        // Find the first it_painted_region which overrides this region.
 | 
				
			||||||
                        for (; layer_range.volume_regions[it_painted_region->parent].region->print_object_region_id() < region_id; ++ it_painted_region)
 | 
					                        for (; layer_range.volume_regions[it_painted_region->parent].region->print_object_region_id() < region_id; ++ it_painted_region)
 | 
				
			||||||
                            assert(it_painted_region < layer_range.painted_regions.end());
 | 
					                            assert(it_painted_region != layer_range.painted_regions.end());
 | 
				
			||||||
                        assert(&layerm.region() == it_painted_region->region && layerm.region().print_object_region_id() == region_id);
 | 
					                        assert(it_painted_region != layer_range.painted_regions.end());
 | 
				
			||||||
 | 
					                        assert(layer_range.volume_regions[it_painted_region->parent].region == &layerm.region());
 | 
				
			||||||
                        // 1-based extruder ID
 | 
					                        // 1-based extruder ID
 | 
				
			||||||
                        bool   self_trimmed = false;
 | 
					                        bool   self_trimmed = false;
 | 
				
			||||||
                        int    self_extruder_id = -1;
 | 
					                        int    self_extruder_id = -1;
 | 
				
			||||||
| 
						 | 
					@ -619,7 +613,7 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
 | 
				
			||||||
                            if (ByExtruder &segmented = by_extruder[extruder_id - 1]; segmented.bbox.defined && bbox.overlap(segmented.bbox)) {
 | 
					                            if (ByExtruder &segmented = by_extruder[extruder_id - 1]; segmented.bbox.defined && bbox.overlap(segmented.bbox)) {
 | 
				
			||||||
                                // Find the target region.
 | 
					                                // Find the target region.
 | 
				
			||||||
                                for (; int(it_painted_region->extruder_id) < extruder_id; ++ it_painted_region)
 | 
					                                for (; int(it_painted_region->extruder_id) < extruder_id; ++ it_painted_region)
 | 
				
			||||||
                                    assert(it_painted_region < layer_range.painted_regions.end());
 | 
					                                    assert(it_painted_region != layer_range.painted_regions.end());
 | 
				
			||||||
                                assert(layer_range.volume_regions[it_painted_region->parent].region == &layerm.region() && int(it_painted_region->extruder_id) == extruder_id);
 | 
					                                assert(layer_range.volume_regions[it_painted_region->parent].region == &layerm.region() && int(it_painted_region->extruder_id) == extruder_id);
 | 
				
			||||||
                                //FIXME Don't trim by self, it is not reliable.
 | 
					                                //FIXME Don't trim by self, it is not reliable.
 | 
				
			||||||
                                if (&layerm.region() == it_painted_region->region) {
 | 
					                                if (&layerm.region() == it_painted_region->region) {
 | 
				
			||||||
| 
						 | 
					@ -669,7 +663,7 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
 | 
				
			||||||
                    ByRegion &src = by_region[region_id];
 | 
					                    ByRegion &src = by_region[region_id];
 | 
				
			||||||
                    if (src.needs_merge)
 | 
					                    if (src.needs_merge)
 | 
				
			||||||
                        // Multiple regions were merged into one.
 | 
					                        // Multiple regions were merged into one.
 | 
				
			||||||
                        src.expolygons = offset2_ex(src.expolygons, float(scale_(EPSILON)), - float(scale_(EPSILON)));
 | 
					                        src.expolygons = offset2_ex(src.expolygons, float(scale_(10 * EPSILON)), - float(scale_(10 * EPSILON)));
 | 
				
			||||||
                    layer->get_region(region_id)->slices.set(std::move(src.expolygons), stInternal);
 | 
					                    layer->get_region(region_id)->slices.set(std::move(src.expolygons), stInternal);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -701,7 +695,7 @@ void PrintObject::slice_volumes()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::vector<float>                   slice_zs      = zs_from_layers(m_layers);
 | 
					    std::vector<float>                   slice_zs      = zs_from_layers(m_layers);
 | 
				
			||||||
    Transform3d                          trafo         = this->trafo();
 | 
					    Transform3d                          trafo         = this->trafo();
 | 
				
			||||||
    trafo.pretranslate(Vec3d(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0));
 | 
					    trafo.pretranslate(Vec3d(- unscale<double>(m_center_offset.x()), - unscale<double>(m_center_offset.y()), 0));
 | 
				
			||||||
    std::vector<std::vector<ExPolygons>> region_slices = slices_to_regions(this->model_object()->volumes, *m_shared_regions, slice_zs,
 | 
					    std::vector<std::vector<ExPolygons>> region_slices = slices_to_regions(this->model_object()->volumes, *m_shared_regions, slice_zs,
 | 
				
			||||||
        slice_volumes_inner(
 | 
					        slice_volumes_inner(
 | 
				
			||||||
            print->config(), this->config(), trafo,
 | 
					            print->config(), this->config(), trafo,
 | 
				
			||||||
| 
						 | 
					@ -812,12 +806,12 @@ void PrintObject::slice_volumes()
 | 
				
			||||||
    BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - end";
 | 
					    BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - end";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType model_volume_type) const
 | 
					std::vector<Polygons> PrintObject::slice_support_volumes(const ModelVolumeType model_volume_type) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    auto it_volume     = this->model_object()->volumes.begin();
 | 
					    auto it_volume     = this->model_object()->volumes.begin();
 | 
				
			||||||
    auto it_volume_end = this->model_object()->volumes.end();
 | 
					    auto it_volume_end = this->model_object()->volumes.end();
 | 
				
			||||||
    for (; it_volume != it_volume_end && (*it_volume)->type() != model_volume_type; ++ it_volume) ;
 | 
					    for (; it_volume != it_volume_end && (*it_volume)->type() != model_volume_type; ++ it_volume) ;
 | 
				
			||||||
    std::vector<ExPolygons> slices;
 | 
					    std::vector<Polygons> slices;
 | 
				
			||||||
    if (it_volume != it_volume_end) {
 | 
					    if (it_volume != it_volume_end) {
 | 
				
			||||||
        // Found at least a single support volume of model_volume_type.
 | 
					        // Found at least a single support volume of model_volume_type.
 | 
				
			||||||
        std::vector<float> zs = zs_from_layers(this->layers());
 | 
					        std::vector<float> zs = zs_from_layers(this->layers());
 | 
				
			||||||
| 
						 | 
					@ -831,16 +825,18 @@ std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType
 | 
				
			||||||
        for (; it_volume != it_volume_end; ++ it_volume)
 | 
					        for (; it_volume != it_volume_end; ++ it_volume)
 | 
				
			||||||
            if ((*it_volume)->type() == model_volume_type) {
 | 
					            if ((*it_volume)->type() == model_volume_type) {
 | 
				
			||||||
                std::vector<ExPolygons> slices2 = slice_volume(*(*it_volume), zs, params, throw_on_cancel_callback);
 | 
					                std::vector<ExPolygons> slices2 = slice_volume(*(*it_volume), zs, params, throw_on_cancel_callback);
 | 
				
			||||||
                if (slices.empty())
 | 
					                if (slices.empty()) {
 | 
				
			||||||
                    slices = std::move(slices2);
 | 
					                    slices.reserve(slices2.size());
 | 
				
			||||||
                else if (! slices2.empty()) {
 | 
					                    for (ExPolygons &src : slices2)
 | 
				
			||||||
 | 
					                        slices.emplace_back(to_polygons(std::move(src)));
 | 
				
			||||||
 | 
					                } else if (!slices2.empty()) {
 | 
				
			||||||
                    if (merge_layers.empty())
 | 
					                    if (merge_layers.empty())
 | 
				
			||||||
                        merge_layers.assign(zs.size(), false);
 | 
					                        merge_layers.assign(zs.size(), false);
 | 
				
			||||||
                    for (size_t i = 0; i < zs.size(); ++ i) {
 | 
					                    for (size_t i = 0; i < zs.size(); ++ i) {
 | 
				
			||||||
                        if (slices[i].empty())
 | 
					                        if (slices[i].empty())
 | 
				
			||||||
                            slices[i] = std::move(slices2[i]);
 | 
					                            slices[i] = to_polygons(std::move(slices2[i]));
 | 
				
			||||||
                        else if (! slices2[i].empty()) {
 | 
					                        else if (! slices2[i].empty()) {
 | 
				
			||||||
                            append(slices[i], std::move(slices2[i]));
 | 
					                            append(slices[i], to_polygons(std::move(slices2[i])));
 | 
				
			||||||
                            merge_layers[i] = true;
 | 
					                            merge_layers[i] = true;
 | 
				
			||||||
                            merge = true;
 | 
					                            merge = true;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
| 
						 | 
					@ -848,7 +844,7 @@ std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        if (merge) {
 | 
					        if (merge) {
 | 
				
			||||||
            std::vector<ExPolygons*> to_merge;
 | 
					            std::vector<Polygons*> to_merge;
 | 
				
			||||||
            to_merge.reserve(zs.size());
 | 
					            to_merge.reserve(zs.size());
 | 
				
			||||||
            for (size_t i = 0; i < zs.size(); ++ i)
 | 
					            for (size_t i = 0; i < zs.size(); ++ i)
 | 
				
			||||||
                if (merge_layers[i])
 | 
					                if (merge_layers[i])
 | 
				
			||||||
| 
						 | 
					@ -857,7 +853,7 @@ std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType
 | 
				
			||||||
                tbb::blocked_range<size_t>(0, to_merge.size()),
 | 
					                tbb::blocked_range<size_t>(0, to_merge.size()),
 | 
				
			||||||
                [&to_merge](const tbb::blocked_range<size_t> &range) {
 | 
					                [&to_merge](const tbb::blocked_range<size_t> &range) {
 | 
				
			||||||
                    for (size_t i = range.begin(); i < range.end(); ++ i)
 | 
					                    for (size_t i = range.begin(); i < range.end(); ++ i)
 | 
				
			||||||
                        *to_merge[i] = union_ex(*to_merge[i]);
 | 
					                        *to_merge[i] = union_(*to_merge[i]);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1353,9 +1353,9 @@ struct SupportAnnotations
 | 
				
			||||||
        object.project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers_layers);
 | 
					        object.project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers_layers);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::vector<ExPolygons>         enforcers_layers;
 | 
					    std::vector<Polygons>         enforcers_layers;
 | 
				
			||||||
    std::vector<ExPolygons>         blockers_layers;
 | 
					    std::vector<Polygons>         blockers_layers;
 | 
				
			||||||
    const std::vector<Polygons>&    buildplate_covered;
 | 
					    const std::vector<Polygons>&  buildplate_covered;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct SlicesMarginCache
 | 
					struct SlicesMarginCache
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -728,7 +728,7 @@ static std::vector<EdgeToFace> create_edge_map(
 | 
				
			||||||
// Map from a face edge to a unique edge identifier or -1 if no neighbor exists.
 | 
					// Map from a face edge to a unique edge identifier or -1 if no neighbor exists.
 | 
				
			||||||
// Two neighbor faces share a unique edge identifier even if they are flipped.
 | 
					// Two neighbor faces share a unique edge identifier even if they are flipped.
 | 
				
			||||||
template<typename ThrowOnCancelCallback>
 | 
					template<typename ThrowOnCancelCallback>
 | 
				
			||||||
static inline std::vector<Vec3i> create_face_neighbors_index_impl(const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel)
 | 
					static inline std::vector<Vec3i> its_face_edge_ids_impl(const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1));
 | 
					    std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -778,14 +778,56 @@ static inline std::vector<Vec3i> create_face_neighbors_index_impl(const indexed_
 | 
				
			||||||
    return out;
 | 
					    return out;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::vector<Vec3i> create_face_neighbors_index(const indexed_triangle_set &its)
 | 
					std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return create_face_neighbors_index_impl(its, [](){});
 | 
					    return its_face_edge_ids_impl(its, [](){});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::vector<Vec3i> create_face_neighbors_index(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback)
 | 
					std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return create_face_neighbors_index_impl(its, throw_on_cancel_callback);
 | 
					    return its_face_edge_ids_impl(its, throw_on_cancel_callback);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.
 | 
				
			||||||
 | 
					std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors, bool assign_unbound_edges, int *num_edges)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // out elements are not initialized!
 | 
				
			||||||
 | 
					    std::vector<Vec3i> out(face_neighbors.size());
 | 
				
			||||||
 | 
					    int last_edge_id = 0;
 | 
				
			||||||
 | 
					    for (int i = 0; i < int(face_neighbors.size()); ++ i) {
 | 
				
			||||||
 | 
					        const stl_triangle_vertex_indices   &triangle  = its.indices[i];
 | 
				
			||||||
 | 
					        const Vec3i                         &neighbors = face_neighbors[i];
 | 
				
			||||||
 | 
					        for (int j = 0; j < 3; ++ j) {
 | 
				
			||||||
 | 
					            int n = neighbors[j];
 | 
				
			||||||
 | 
					            if (n > i) {
 | 
				
			||||||
 | 
					                const stl_triangle_vertex_indices &triangle2 = its.indices[n];
 | 
				
			||||||
 | 
					                int   edge_id = last_edge_id ++;
 | 
				
			||||||
 | 
					                Vec2i edge    = its_triangle_edge(triangle, j);
 | 
				
			||||||
 | 
					                // First find an edge with opposite orientation.
 | 
				
			||||||
 | 
					                std::swap(edge(0), edge(1));
 | 
				
			||||||
 | 
					                int   k       = its_triangle_edge_index(triangle2, edge);
 | 
				
			||||||
 | 
					                //FIXME is the following realistic? Could face_neighbors contain such faces?
 | 
				
			||||||
 | 
					                // And if it does, do we want to produce the same edge ID for those mutually incorrectly oriented edges?
 | 
				
			||||||
 | 
					                if (k == -1) {
 | 
				
			||||||
 | 
					                    // Second find an edge with the same orientation (the neighbor triangle may be flipped).
 | 
				
			||||||
 | 
					                    std::swap(edge(0), edge(1));
 | 
				
			||||||
 | 
					                    k = its_triangle_edge_index(triangle2, edge);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                assert(k >= 0);
 | 
				
			||||||
 | 
					                out[i](j) = edge_id;
 | 
				
			||||||
 | 
					                out[n](k) = edge_id;
 | 
				
			||||||
 | 
					            } else if (n == -1) {
 | 
				
			||||||
 | 
					                out[i](j) = assign_unbound_edges ? last_edge_id ++ : -1;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Triangle shall never be neighbor of itself.
 | 
				
			||||||
 | 
					                assert(n < i);
 | 
				
			||||||
 | 
					                // Don't do anything, the neighbor will assign us an edge ID in later iterations.
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (num_edges)
 | 
				
			||||||
 | 
					        *num_edges = last_edge_id;
 | 
				
			||||||
 | 
					    return out;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Merge duplicate vertices, return number of vertices removed.
 | 
					// Merge duplicate vertices, return number of vertices removed.
 | 
				
			||||||
| 
						 | 
					@ -1219,14 +1261,14 @@ void VertexFaceIndex::create(const indexed_triangle_set &its)
 | 
				
			||||||
    m_vertex_to_face_start.front() = 0;
 | 
					    m_vertex_to_face_start.front() = 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::vector<Vec3i> its_create_neighbors_index(const indexed_triangle_set &its)
 | 
					std::vector<Vec3i> its_face_neighbors(const indexed_triangle_set &its)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return create_neighbors_index(ex_seq, its);
 | 
					    return create_face_neighbors_index(ex_seq, its);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::vector<Vec3i> its_create_neighbors_index_par(const indexed_triangle_set &its)
 | 
					std::vector<Vec3i> its_face_neighbors_par(const indexed_triangle_set &its)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return create_neighbors_index(ex_tbb, its);
 | 
					    return create_face_neighbors_index(ex_tbb, its);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace Slic3r
 | 
					} // namespace Slic3r
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,13 +116,14 @@ private:
 | 
				
			||||||
// Map from a face edge to a unique edge identifier or -1 if no neighbor exists.
 | 
					// Map from a face edge to a unique edge identifier or -1 if no neighbor exists.
 | 
				
			||||||
// Two neighbor faces share a unique edge identifier even if they are flipped.
 | 
					// Two neighbor faces share a unique edge identifier even if they are flipped.
 | 
				
			||||||
// Used for chaining slice lines into polygons.
 | 
					// Used for chaining slice lines into polygons.
 | 
				
			||||||
std::vector<Vec3i> create_face_neighbors_index(const indexed_triangle_set &its);
 | 
					std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its);
 | 
				
			||||||
std::vector<Vec3i> create_face_neighbors_index(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback);
 | 
					std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback);
 | 
				
			||||||
 | 
					// Having the face neighbors available, assign unique edge IDs to face edges for chaining of polygons over slices.
 | 
				
			||||||
 | 
					std::vector<Vec3i> its_face_edge_ids(const indexed_triangle_set &its, std::vector<Vec3i> &face_neighbors, bool assign_unbound_edges = false, int *num_edges = nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create index that gives neighbor faces for each face. Ignores face orientations.
 | 
					// Create index that gives neighbor faces for each face. Ignores face orientations.
 | 
				
			||||||
// TODO: naming...
 | 
					std::vector<Vec3i> its_face_neighbors(const indexed_triangle_set &its);
 | 
				
			||||||
std::vector<Vec3i> its_create_neighbors_index(const indexed_triangle_set &its);
 | 
					std::vector<Vec3i> its_face_neighbors_par(const indexed_triangle_set &its);
 | 
				
			||||||
std::vector<Vec3i> its_create_neighbors_index_par(const indexed_triangle_set &its);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// After applying a transformation with negative determinant, flip the faces to keep the transformed mesh volume positive.
 | 
					// After applying a transformation with negative determinant, flip the faces to keep the transformed mesh volume positive.
 | 
				
			||||||
void its_flip_triangles(indexed_triangle_set &its);
 | 
					void its_flip_triangles(indexed_triangle_set &its);
 | 
				
			||||||
| 
						 | 
					@ -153,6 +154,28 @@ void its_collect_mesh_projection_points_above(const indexed_triangle_set &its, c
 | 
				
			||||||
Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Matrix3f &m, const float z);
 | 
					Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Matrix3f &m, const float z);
 | 
				
			||||||
Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Transform3f &t, const float z);
 | 
					Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Transform3f &t, const float z);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Index of a vertex inside triangle_indices.
 | 
				
			||||||
 | 
					inline int its_triangle_vertex_index(const stl_triangle_vertex_indices &triangle_indices, int vertex_idx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return vertex_idx == triangle_indices[0] ? 0 :
 | 
				
			||||||
 | 
					           vertex_idx == triangle_indices[1] ? 1 :
 | 
				
			||||||
 | 
					           vertex_idx == triangle_indices[2] ? 2 : -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline Vec2i its_triangle_edge(const stl_triangle_vertex_indices &triangle_indices, int edge_idx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int next_edge_idx = (edge_idx == 2) ? 0 : edge_idx + 1;
 | 
				
			||||||
 | 
					    return { triangle_indices[edge_idx], triangle_indices[next_edge_idx] };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Index of an edge inside triangle.
 | 
				
			||||||
 | 
					inline int its_triangle_edge_index(const stl_triangle_vertex_indices &triangle_indices, const Vec2i &triangle_edge)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return triangle_edge(0) == triangle_indices[0] && triangle_edge(1) == triangle_indices[1] ? 0 :
 | 
				
			||||||
 | 
					           triangle_edge(0) == triangle_indices[1] && triangle_edge(1) == triangle_indices[2] ? 1 :
 | 
				
			||||||
 | 
					           triangle_edge(0) == triangle_indices[2] && triangle_edge(1) == triangle_indices[0] ? 2 : -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using its_triangle = std::array<stl_vertex, 3>;
 | 
					using its_triangle = std::array<stl_vertex, 3>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline its_triangle its_triangle_vertices(const indexed_triangle_set &its,
 | 
					inline its_triangle its_triangle_vertices(const indexed_triangle_set &its,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@
 | 
				
			||||||
#include "Tesselate.hpp"
 | 
					#include "Tesselate.hpp"
 | 
				
			||||||
#include "TriangleMesh.hpp"
 | 
					#include "TriangleMesh.hpp"
 | 
				
			||||||
#include "TriangleMeshSlicer.hpp"
 | 
					#include "TriangleMeshSlicer.hpp"
 | 
				
			||||||
 | 
					#include "Utils.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <algorithm>
 | 
					#include <algorithm>
 | 
				
			||||||
#include <cmath>
 | 
					#include <cmath>
 | 
				
			||||||
| 
						 | 
					@ -14,6 +15,10 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <tbb/parallel_for.h>
 | 
					#include <tbb/parallel_for.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef NDEBUG
 | 
				
			||||||
 | 
					//    #define EXPENSIVE_DEBUG_CHECKS
 | 
				
			||||||
 | 
					#endif // NDEBUG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if 0
 | 
					#if 0
 | 
				
			||||||
    #define DEBUG
 | 
					    #define DEBUG
 | 
				
			||||||
    #define _DEBUG
 | 
					    #define _DEBUG
 | 
				
			||||||
| 
						 | 
					@ -26,6 +31,8 @@
 | 
				
			||||||
#include <boost/thread/mutex.hpp>
 | 
					#include <boost/thread/mutex.hpp>
 | 
				
			||||||
#include <boost/thread/lock_guard.hpp>
 | 
					#include <boost/thread/lock_guard.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// #define SLIC3R_DEBUG_SLICE_PROCESSING
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING)
 | 
					#if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING)
 | 
				
			||||||
#include "SVG.hpp"
 | 
					#include "SVG.hpp"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -65,6 +72,8 @@ public:
 | 
				
			||||||
    bool is_seed_candidate() const { return (this->flags & NO_SEED) == 0 && ! this->skip(); }
 | 
					    bool is_seed_candidate() const { return (this->flags & NO_SEED) == 0 && ! this->skip(); }
 | 
				
			||||||
    void set_no_seed(bool set) { if (set) this->flags |= NO_SEED; else this->flags &= ~NO_SEED; }
 | 
					    void set_no_seed(bool set) { if (set) this->flags |= NO_SEED; else this->flags &= ~NO_SEED; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void reverse() { std::swap(a, b); std::swap(a_id, b_id); std::swap(edge_a_id, edge_b_id); }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    // Inherits Point a, b
 | 
					    // Inherits Point a, b
 | 
				
			||||||
    // For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1.
 | 
					    // For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1.
 | 
				
			||||||
    // Vertex indices of the line end points.
 | 
					    // Vertex indices of the line end points.
 | 
				
			||||||
| 
						 | 
					@ -81,8 +90,14 @@ public:
 | 
				
			||||||
        Top,
 | 
					        Top,
 | 
				
			||||||
        // Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
 | 
					        // Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
 | 
				
			||||||
        Bottom,
 | 
					        Bottom,
 | 
				
			||||||
 | 
					        // Two vertices are aligned with the cutting plane, the edge is shared by two triangles, where one
 | 
				
			||||||
 | 
					        // triangle is below or at the cutting plane and the other is above or at the cutting plane (only one
 | 
				
			||||||
 | 
					        // vertex may lie on the plane).
 | 
				
			||||||
 | 
					        TopBottom,
 | 
				
			||||||
        // All three vertices of a face are aligned with the cutting plane.
 | 
					        // All three vertices of a face are aligned with the cutting plane.
 | 
				
			||||||
        Horizontal
 | 
					        Horizontal,
 | 
				
			||||||
 | 
					        // Edge 
 | 
				
			||||||
 | 
					        Slab,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // feGeneral, feTop, feBottom, feHorizontal
 | 
					    // feGeneral, feTop, feBottom, feHorizontal
 | 
				
			||||||
| 
						 | 
					@ -102,6 +117,15 @@ public:
 | 
				
			||||||
        SKIP                = 0x200,
 | 
					        SKIP                = 0x200,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    uint32_t        flags { 0 };
 | 
					    uint32_t        flags { 0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef NDEBUG
 | 
				
			||||||
 | 
					    enum class Source {
 | 
				
			||||||
 | 
					        BottomPlane,
 | 
				
			||||||
 | 
					        TopPlane,
 | 
				
			||||||
 | 
					        Slab,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    Source          source { Source::BottomPlane };
 | 
				
			||||||
 | 
					#endif // NDEBUG
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using IntersectionLines = std::vector<IntersectionLine>;
 | 
					using IntersectionLines = std::vector<IntersectionLine>;
 | 
				
			||||||
| 
						 | 
					@ -119,7 +143,7 @@ static FacetSliceType slice_facet(
 | 
				
			||||||
    // 3 vertices of the triangle, XY scaled. Z scaled or unscaled (same as slice_z).
 | 
					    // 3 vertices of the triangle, XY scaled. Z scaled or unscaled (same as slice_z).
 | 
				
			||||||
    const stl_vertex                    *vertices,
 | 
					    const stl_vertex                    *vertices,
 | 
				
			||||||
    const stl_triangle_vertex_indices   &indices,
 | 
					    const stl_triangle_vertex_indices   &indices,
 | 
				
			||||||
    const Vec3i                         &edge_neighbor,
 | 
					    const Vec3i                         &edge_ids,
 | 
				
			||||||
    const int                            idx_vertex_lowest,
 | 
					    const int                            idx_vertex_lowest,
 | 
				
			||||||
    const bool                           horizontal,
 | 
					    const bool                           horizontal,
 | 
				
			||||||
    IntersectionLine                    &line_out)
 | 
					    IntersectionLine                    &line_out)
 | 
				
			||||||
| 
						 | 
					@ -138,7 +162,7 @@ static FacetSliceType slice_facet(
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            int   k = (idx_vertex_lowest + j) % 3;
 | 
					            int   k = (idx_vertex_lowest + j) % 3;
 | 
				
			||||||
            int   l = (k + 1) % 3;
 | 
					            int   l = (k + 1) % 3;
 | 
				
			||||||
            edge_id = edge_neighbor(k);
 | 
					            edge_id = edge_ids(k);
 | 
				
			||||||
            a_id    = indices[k];
 | 
					            a_id    = indices[k];
 | 
				
			||||||
            a       = vertices + k;
 | 
					            a       = vertices + k;
 | 
				
			||||||
            b_id    = indices[l];
 | 
					            b_id    = indices[l];
 | 
				
			||||||
| 
						 | 
					@ -284,11 +308,11 @@ void slice_facet_at_zs(
 | 
				
			||||||
    const std::vector<Vec3f>                         &mesh_vertices,
 | 
					    const std::vector<Vec3f>                         &mesh_vertices,
 | 
				
			||||||
    const TransformVertex                            &transform_vertex_fn,
 | 
					    const TransformVertex                            &transform_vertex_fn,
 | 
				
			||||||
    const stl_triangle_vertex_indices                &indices,
 | 
					    const stl_triangle_vertex_indices                &indices,
 | 
				
			||||||
    const Vec3i                                      &facet_neighbors,
 | 
					    const Vec3i                                      &edge_ids,
 | 
				
			||||||
    // Scaled or unscaled zs. If vertices have their zs scaled or transform_vertex_fn scales them, then zs have to be scaled as well.
 | 
					    // Scaled or unscaled zs. If vertices have their zs scaled or transform_vertex_fn scales them, then zs have to be scaled as well.
 | 
				
			||||||
    const std::vector<float>                         &zs,
 | 
					    const std::vector<float>                         &zs,
 | 
				
			||||||
    std::vector<IntersectionLines>                   &lines,
 | 
					    std::vector<IntersectionLines>                   &lines,
 | 
				
			||||||
    boost::mutex                                     &lines_mutex)
 | 
					    std::array<std::mutex, 64>                       &lines_mutex)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    stl_vertex vertices[3] { transform_vertex_fn(mesh_vertices[indices(0)]), transform_vertex_fn(mesh_vertices[indices(1)]), transform_vertex_fn(mesh_vertices[indices(2)]) };
 | 
					    stl_vertex vertices[3] { transform_vertex_fn(mesh_vertices[indices(0)]), transform_vertex_fn(mesh_vertices[indices(1)]), transform_vertex_fn(mesh_vertices[indices(2)]) };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -299,43 +323,439 @@ void slice_facet_at_zs(
 | 
				
			||||||
    // find layer extents
 | 
					    // find layer extents
 | 
				
			||||||
    auto min_layer = std::lower_bound(zs.begin(), zs.end(), min_z); // first layer whose slice_z is >= min_z
 | 
					    auto min_layer = std::lower_bound(zs.begin(), zs.end(), min_z); // first layer whose slice_z is >= min_z
 | 
				
			||||||
    auto max_layer = std::upper_bound(min_layer, zs.end(), max_z); // first layer whose slice_z is > max_z
 | 
					    auto max_layer = std::upper_bound(min_layer, zs.end(), max_z); // first layer whose slice_z is > max_z
 | 
				
			||||||
 | 
					    int  idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    for (auto it = min_layer; it != max_layer; ++ it) {
 | 
					    for (auto it = min_layer; it != max_layer; ++ it) {
 | 
				
			||||||
        IntersectionLine il;
 | 
					        IntersectionLine il;
 | 
				
			||||||
        int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
 | 
					        // Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume.
 | 
				
			||||||
        if (slice_facet(*it, vertices, indices, facet_neighbors, idx_vertex_lowest, min_z == max_z, il) == FacetSliceType::Slicing &&
 | 
					        if (min_z != max_z && slice_facet(*it, vertices, indices, edge_ids, idx_vertex_lowest, false, il) == FacetSliceType::Slicing) {
 | 
				
			||||||
            il.edge_type != IntersectionLine::FacetEdgeType::Horizontal) {
 | 
					            assert(il.edge_type != IntersectionLine::FacetEdgeType::Horizontal);
 | 
				
			||||||
            // Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume.
 | 
					            size_t slice_id = it - zs.begin();
 | 
				
			||||||
            boost::lock_guard<boost::mutex> l(lines_mutex);
 | 
					            boost::lock_guard<std::mutex> l(lines_mutex[slice_id >> 6]);
 | 
				
			||||||
            lines[it - zs.begin()].emplace_back(il);
 | 
					            lines[slice_id].emplace_back(il);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template<typename TransformVertex, typename ThrowOnCancel>
 | 
					template<typename TransformVertex, typename ThrowOnCancel>
 | 
				
			||||||
inline std::vector<IntersectionLines> slice_make_lines(
 | 
					static inline std::vector<IntersectionLines> slice_make_lines(
 | 
				
			||||||
    const std::vector<stl_vertex>                   &vertices,
 | 
					    const std::vector<stl_vertex>                   &vertices,
 | 
				
			||||||
    const TransformVertex                           &transform_vertex_fn,
 | 
					    const TransformVertex                           &transform_vertex_fn,
 | 
				
			||||||
    const std::vector<stl_triangle_vertex_indices>  &indices,
 | 
					    const std::vector<stl_triangle_vertex_indices>  &indices,
 | 
				
			||||||
    const std::vector<Vec3i>                        &face_neighbors,
 | 
					    const std::vector<Vec3i>                        &face_edge_ids,
 | 
				
			||||||
    const std::vector<float>                        &zs,
 | 
					    const std::vector<float>                        &zs,
 | 
				
			||||||
    const ThrowOnCancel                              throw_on_cancel_fn)
 | 
					    const ThrowOnCancel                              throw_on_cancel_fn)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    std::vector<IntersectionLines>  lines(zs.size(), IntersectionLines());
 | 
					    std::vector<IntersectionLines>  lines(zs.size(), IntersectionLines());
 | 
				
			||||||
    boost::mutex                    lines_mutex;
 | 
					    std::array<std::mutex, 64>      lines_mutex;
 | 
				
			||||||
    tbb::parallel_for(
 | 
					    tbb::parallel_for(
 | 
				
			||||||
        tbb::blocked_range<int>(0, int(indices.size())),
 | 
					        tbb::blocked_range<int>(0, int(indices.size())),
 | 
				
			||||||
        [&vertices, &transform_vertex_fn, &indices, &face_neighbors, &zs, &lines, &lines_mutex, throw_on_cancel_fn](const tbb::blocked_range<int> &range) {
 | 
					        [&vertices, &transform_vertex_fn, &indices, &face_edge_ids, &zs, &lines, &lines_mutex, throw_on_cancel_fn](const tbb::blocked_range<int> &range) {
 | 
				
			||||||
            for (int face_idx = range.begin(); face_idx < range.end(); ++ face_idx) {
 | 
					            for (int face_idx = range.begin(); face_idx < range.end(); ++ face_idx) {
 | 
				
			||||||
                if ((face_idx & 0x0ffff) == 0)
 | 
					                if ((face_idx & 0x0ffff) == 0)
 | 
				
			||||||
                    throw_on_cancel_fn();
 | 
					                    throw_on_cancel_fn();
 | 
				
			||||||
                slice_facet_at_zs(vertices, transform_vertex_fn, indices[face_idx], face_neighbors[face_idx], zs, lines, lines_mutex);
 | 
					                slice_facet_at_zs(vertices, transform_vertex_fn, indices[face_idx], face_edge_ids[face_idx], zs, lines, lines_mutex);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    return lines;
 | 
					    return lines;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// For projecting triangle sets onto slice slabs.
 | 
				
			||||||
 | 
					struct SlabLines {
 | 
				
			||||||
 | 
					    // Intersection lines of a slice with a triangle set, CCW oriented.
 | 
				
			||||||
 | 
					    std::vector<IntersectionLines>  at_slice;
 | 
				
			||||||
 | 
					    // Projections of triangle set boundary lines into layer below (for projection from the top)
 | 
				
			||||||
 | 
					    // or into layer above (for projection from the bottom).
 | 
				
			||||||
 | 
					    // In both cases the intersection liens are CCW oriented.
 | 
				
			||||||
 | 
					    std::vector<IntersectionLines>  between_slices;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Orientation of the face normal in regard to a XY plane pointing upwards.
 | 
				
			||||||
 | 
					enum class FaceOrientation : char {
 | 
				
			||||||
 | 
					    // Z component of the normal is positive.
 | 
				
			||||||
 | 
					    Up,
 | 
				
			||||||
 | 
					    // Z component of the normal is negative.
 | 
				
			||||||
 | 
					    Down,
 | 
				
			||||||
 | 
					    // Z component of the normal is zero.
 | 
				
			||||||
 | 
					    Vertical,
 | 
				
			||||||
 | 
					    // Triangle is degenerate, thus its normal is undefined. We may want to slice the degenerate triangles
 | 
				
			||||||
 | 
					    // because of the connectivity information they carry.
 | 
				
			||||||
 | 
					    Degenerate
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<bool ProjectionFromTop>
 | 
				
			||||||
 | 
					void slice_facet_with_slabs(
 | 
				
			||||||
 | 
					    // Scaled or unscaled vertices. transform_vertex_fn may scale zs.
 | 
				
			||||||
 | 
					    const std::vector<Vec3f>                         &mesh_vertices,
 | 
				
			||||||
 | 
					    const std::vector<stl_triangle_vertex_indices>   &mesh_triangles,
 | 
				
			||||||
 | 
					    const size_t                                      facet_idx,
 | 
				
			||||||
 | 
					    const Vec3i                                      &facet_neighbors,
 | 
				
			||||||
 | 
					    const Vec3i                                      &facet_edge_ids,
 | 
				
			||||||
 | 
					    // Increase edge_ids at the top plane of the slab edges by num_edges to allow chaining
 | 
				
			||||||
 | 
					    // from bottom plane of the slab to the top plane of the slab and vice versa.
 | 
				
			||||||
 | 
					    const int                                         num_edges,
 | 
				
			||||||
 | 
					    const std::vector<float>                         &zs,
 | 
				
			||||||
 | 
					    SlabLines                                        &lines,
 | 
				
			||||||
 | 
					    std::array<std::mutex, 64>                       &lines_mutex)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const stl_triangle_vertex_indices &indices = mesh_triangles[facet_idx];
 | 
				
			||||||
 | 
					    stl_vertex vertices[3] { mesh_vertices[indices(0)], mesh_vertices[indices(1)], mesh_vertices[indices(2)] };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // find facet extents
 | 
				
			||||||
 | 
					    const float min_z = fminf(vertices[0].z(), fminf(vertices[1].z(), vertices[2].z()));
 | 
				
			||||||
 | 
					    const float max_z = fmaxf(vertices[0].z(), fmaxf(vertices[1].z(), vertices[2].z()));
 | 
				
			||||||
 | 
					    const bool  horizontal = min_z == max_z;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // find layer extents
 | 
				
			||||||
 | 
					    auto min_layer = std::lower_bound(zs.begin(), zs.end(), min_z); // first layer whose slice_z is >= min_z
 | 
				
			||||||
 | 
					    auto max_layer = std::upper_bound(min_layer, zs.end(), max_z); // first layer whose slice_z is > max_z
 | 
				
			||||||
 | 
					    assert(min_layer == zs.end() ? max_layer == zs.end() : *min_layer >= min_z);
 | 
				
			||||||
 | 
					    assert(max_layer == zs.end() || *max_layer > max_z);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto emit_slab_edge = [&lines, &lines_mutex](IntersectionLine il, size_t slab_id, bool reverse) {
 | 
				
			||||||
 | 
					        if (reverse)
 | 
				
			||||||
 | 
					            il.reverse();
 | 
				
			||||||
 | 
					        boost::lock_guard<std::mutex> l(lines_mutex[(slab_id + 32) >> 6]);
 | 
				
			||||||
 | 
					        lines.between_slices[slab_id].emplace_back(il);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (min_layer == max_layer || horizontal) {
 | 
				
			||||||
 | 
					        // Horizontal face or a nearly horizontal face that fits between two layers or below the bottom most or above the top most layer.
 | 
				
			||||||
 | 
					        assert(horizontal || zs.empty() || max_z < zs.front() || min_z > zs.back() ||
 | 
				
			||||||
 | 
					            (min_layer == max_layer && min_layer != zs.end() && min_layer != zs.begin() && *(min_layer - 1) < min_z && *min_layer > max_z));
 | 
				
			||||||
 | 
					        if (horizontal && min_layer != zs.end() && *min_layer == min_z) {
 | 
				
			||||||
 | 
					            // Slicing a horizontal triangle with a slicing plane. The triangle has to be upwards facing for ProjectionFromTop
 | 
				
			||||||
 | 
					            // and downwards facing for ! ProjectionFromTop.
 | 
				
			||||||
 | 
					            assert(min_layer != max_layer);
 | 
				
			||||||
 | 
					            size_t line_id = min_layer - zs.begin();
 | 
				
			||||||
 | 
					            for (int iedge = 0; iedge < 3; ++ iedge)
 | 
				
			||||||
 | 
					                if (facet_neighbors(iedge) == -1) {
 | 
				
			||||||
 | 
					                    int i = iedge;
 | 
				
			||||||
 | 
					                    int j = next_idx_modulo(i, 3);
 | 
				
			||||||
 | 
					                    assert(vertices[i].z() == zs[line_id]);
 | 
				
			||||||
 | 
					                    assert(vertices[j].z() == zs[line_id]);
 | 
				
			||||||
 | 
					                    IntersectionLine il {
 | 
				
			||||||
 | 
					                        { to_2d(vertices[i]).cast<coord_t>(), to_2d(vertices[j]).cast<coord_t>() },
 | 
				
			||||||
 | 
					                        indices(i), indices(j), -1, -1, 
 | 
				
			||||||
 | 
					                        ProjectionFromTop ? IntersectionLine::FacetEdgeType::Bottom : IntersectionLine::FacetEdgeType::Top
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                    // Don't flip the FacetEdgeType::Top edge, it will be flipped when chaining.
 | 
				
			||||||
 | 
					                    // if (! ProjectionFromTop) il.reverse();
 | 
				
			||||||
 | 
					                    boost::lock_guard<std::mutex> l(lines_mutex[line_id >> 6]);
 | 
				
			||||||
 | 
					                    lines.at_slice[line_id].emplace_back(il);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // Triangle is completely between two slicing planes, the triangle may or may not be horizontal, which 
 | 
				
			||||||
 | 
					            // does not matter for the processing of such a triangle.
 | 
				
			||||||
 | 
					            size_t slab_id;
 | 
				
			||||||
 | 
					            if (ProjectionFromTop) {
 | 
				
			||||||
 | 
					                if (max_layer == zs.begin()) {
 | 
				
			||||||
 | 
					                    // Not slicing the triangle and it is below the lowest layer.
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // Not slicing the triangle and it could be projected into a slab.
 | 
				
			||||||
 | 
					                    slab_id = max_layer - zs.begin();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // projection from bottom
 | 
				
			||||||
 | 
					                if (min_layer == zs.end()) {
 | 
				
			||||||
 | 
					                    // Not slicing the triangle and it is above the highest layer.
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // Not slicing the triangle and it could be projected into a slab.
 | 
				
			||||||
 | 
					                    slab_id = min_layer - zs.begin();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (ProjectionFromTop)
 | 
				
			||||||
 | 
					                -- slab_id;
 | 
				
			||||||
 | 
					            for (int iedge = 0; iedge < 3; ++ iedge)
 | 
				
			||||||
 | 
					                if (facet_neighbors(iedge) == -1) {
 | 
				
			||||||
 | 
					                    int i = iedge;
 | 
				
			||||||
 | 
					                    int j = next_idx_modulo(i, 3);
 | 
				
			||||||
 | 
					                    assert(ProjectionFromTop ? vertices[i].z() >= zs[slab_id] : vertices[i].z() <= zs[slab_id]);
 | 
				
			||||||
 | 
					                    assert(ProjectionFromTop ? vertices[j].z() >= zs[slab_id] : vertices[j].z() <= zs[slab_id]);
 | 
				
			||||||
 | 
					                    emit_slab_edge(
 | 
				
			||||||
 | 
					                        IntersectionLine {
 | 
				
			||||||
 | 
					                            { to_2d(vertices[i]).cast<coord_t>(), to_2d(vertices[j]).cast<coord_t>() },
 | 
				
			||||||
 | 
					                            indices(i), indices(j), -1, -1, IntersectionLine::FacetEdgeType::Slab
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        slab_id, ! ProjectionFromTop);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        // The triangle is not horizontal and at least a single slicing plane intersects the triangle.
 | 
				
			||||||
 | 
					        int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
 | 
				
			||||||
 | 
					        IntersectionLine il_prev;
 | 
				
			||||||
 | 
					        for (auto it = min_layer; it != max_layer; ++ it) {
 | 
				
			||||||
 | 
					            IntersectionLine il;
 | 
				
			||||||
 | 
					            auto type = slice_facet(*it, vertices, indices, facet_edge_ids, idx_vertex_lowest, false, il);
 | 
				
			||||||
 | 
					            if (type == FacetSliceType::NoSlice) {
 | 
				
			||||||
 | 
					                // One and exactly one vertex is touching the slicing plane.
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                if (il.edge_type == IntersectionLine::FacetEdgeType::Top || il.edge_type == IntersectionLine::FacetEdgeType::Bottom) {
 | 
				
			||||||
 | 
					                    // The non-horizontal triangle is being sliced at one of its edges.
 | 
				
			||||||
 | 
					                    // If the edge is open (it does not have a neighbor), add it.
 | 
				
			||||||
 | 
					                    // If the edge has a neighbor, then add it as TopBottom, and do it just once.
 | 
				
			||||||
 | 
					                    assert(il.a_id != -1 && il.b_id != -1);
 | 
				
			||||||
 | 
					                    assert(il.edge_a_id == -1 && il.edge_b_id == -1);
 | 
				
			||||||
 | 
					                    // Identify edge ID from the edge vertices.
 | 
				
			||||||
 | 
					                    int edge_id;
 | 
				
			||||||
 | 
					                    if (type == FacetSliceType::Cutting) {
 | 
				
			||||||
 | 
					                        // The edge is oriented CCW along the face perimeter.
 | 
				
			||||||
 | 
					                        assert(il.edge_type == IntersectionLine::FacetEdgeType::Bottom);
 | 
				
			||||||
 | 
					                        edge_id = il.a_id == indices(0) ? 0 : il.a_id == indices(1) ? 1 : 2;
 | 
				
			||||||
 | 
					                        assert(il.a_id == indices(edge_id));
 | 
				
			||||||
 | 
					                        assert(il.b_id == indices(next_idx_modulo(edge_id, 3)));
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        // The edge is oriented CW along the face perimeter.
 | 
				
			||||||
 | 
					                        assert(il.edge_type == IntersectionLine::FacetEdgeType::Top);
 | 
				
			||||||
 | 
					                        edge_id = il.b_id == indices(0) ? 0 : il.b_id == indices(1) ? 1 : 2;
 | 
				
			||||||
 | 
					                        assert(il.b_id == indices(edge_id));
 | 
				
			||||||
 | 
					                        assert(il.a_id == indices(next_idx_modulo(edge_id, 3)));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    int neighbor_idx = facet_neighbors(edge_id);
 | 
				
			||||||
 | 
					                    if (neighbor_idx == -1) {
 | 
				
			||||||
 | 
					                        // Save the open edge for sure.
 | 
				
			||||||
 | 
					                        type = FacetSliceType::Slicing;
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        const stl_triangle_vertex_indices &neighbor = mesh_triangles[neighbor_idx];
 | 
				
			||||||
 | 
					                        float z = *it;
 | 
				
			||||||
 | 
					#ifndef NDEBUG
 | 
				
			||||||
 | 
					                        int num_on_plane = (mesh_vertices[neighbor(0)].z() == z) + (mesh_vertices[neighbor(1)].z() == z) + (mesh_vertices[neighbor(2)].z() == z);
 | 
				
			||||||
 | 
					                        assert(num_on_plane == 2 || num_on_plane == 3);
 | 
				
			||||||
 | 
					#endif // NDEBUG
 | 
				
			||||||
 | 
					                        if (mesh_vertices[neighbor(0)].z() == z && mesh_vertices[neighbor(1)].z() == z && mesh_vertices[neighbor(2)].z() == z) {
 | 
				
			||||||
 | 
					                            // The neighbor triangle is horizontal.
 | 
				
			||||||
 | 
					                            // Is the corner convex or concave?
 | 
				
			||||||
 | 
					                            if (il.edge_type == (ProjectionFromTop ? IntersectionLine::FacetEdgeType::Top : IntersectionLine::FacetEdgeType::Bottom)) {
 | 
				
			||||||
 | 
					                                // Convex corner. Add this edge to both slabs, the edge is a boundary edge of both the projection patch below and
 | 
				
			||||||
 | 
					                                // above this slicing plane.
 | 
				
			||||||
 | 
					                                type = FacetSliceType::Slicing;
 | 
				
			||||||
 | 
					                                il.edge_type = IntersectionLine::FacetEdgeType::TopBottom;
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                // Concave corner. Ignore this edge, it is internal to the projection patch.
 | 
				
			||||||
 | 
					                                type = FacetSliceType::Cutting;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else if (il.edge_type == IntersectionLine::FacetEdgeType::Top) {
 | 
				
			||||||
 | 
					                            // Indicate that the edge belongs to both the slab below and above the plane.
 | 
				
			||||||
 | 
					                            assert(type == FacetSliceType::Slicing);
 | 
				
			||||||
 | 
					                            il.edge_type = IntersectionLine::FacetEdgeType::TopBottom;
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            // Don't add this edge, as the neighbor triangle will add the same edge as FacetEdgeType::TopBottom.
 | 
				
			||||||
 | 
					                            assert(type == FacetSliceType::Cutting);
 | 
				
			||||||
 | 
					                            assert(il.edge_type == IntersectionLine::FacetEdgeType::Bottom);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (type == FacetSliceType::Slicing) {
 | 
				
			||||||
 | 
					                    if (! ProjectionFromTop)
 | 
				
			||||||
 | 
					                        il.reverse();
 | 
				
			||||||
 | 
					                    size_t line_id = it - zs.begin();
 | 
				
			||||||
 | 
					                    boost::lock_guard<std::mutex> l(lines_mutex[line_id >> 6]);
 | 
				
			||||||
 | 
					                    lines.at_slice[line_id].emplace_back(il);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (! ProjectionFromTop || it != zs.begin()) {
 | 
				
			||||||
 | 
					                size_t slab_id = it - zs.begin();
 | 
				
			||||||
 | 
					                if (ProjectionFromTop)
 | 
				
			||||||
 | 
					                    -- slab_id;
 | 
				
			||||||
 | 
					                // Try to project unbound edges.
 | 
				
			||||||
 | 
					                for (int iedge = 0; iedge < 3; ++ iedge)
 | 
				
			||||||
 | 
					                    if (facet_neighbors(iedge) == -1) {
 | 
				
			||||||
 | 
					                        // Unbound edge.
 | 
				
			||||||
 | 
					                        int  edge_id         = facet_edge_ids(iedge);
 | 
				
			||||||
 | 
					                        bool intersects_this = il.edge_a_id == edge_id || il.edge_b_id == edge_id;
 | 
				
			||||||
 | 
					                        bool intersects_prev = il_prev.edge_a_id == edge_id || il_prev.edge_b_id == edge_id;
 | 
				
			||||||
 | 
					                        int i = iedge;
 | 
				
			||||||
 | 
					                        int j = next_idx_modulo(i, 3);
 | 
				
			||||||
 | 
					                        assert((! intersects_this && ! intersects_prev) || vertices[j].z() != vertices[i].z());
 | 
				
			||||||
 | 
					                        bool edge_up = vertices[j].z() > vertices[i].z();
 | 
				
			||||||
 | 
					                        if (intersects_this && intersects_prev) {
 | 
				
			||||||
 | 
					                            // Intersects both, emit the segment between these intersections.
 | 
				
			||||||
 | 
					                            Line l(il_prev.edge_a_id == edge_id ? il_prev.a : il_prev.b, 
 | 
				
			||||||
 | 
					                                   il.edge_a_id == edge_id ? il.a : il.b);
 | 
				
			||||||
 | 
					                            emit_slab_edge(
 | 
				
			||||||
 | 
					                                IntersectionLine { l, -1, -1, edge_id, edge_id + num_edges, IntersectionLine::FacetEdgeType::Slab },
 | 
				
			||||||
 | 
					                                slab_id, ProjectionFromTop != edge_up);
 | 
				
			||||||
 | 
					                        } else if (intersects_this) {
 | 
				
			||||||
 | 
					                            // Intersects just the top plane, may touch the bottom plane.
 | 
				
			||||||
 | 
					                            assert((vertices[i].z() > *it && vertices[j].z() < *it) || (vertices[i].z() < *it && vertices[j].z() > *it));
 | 
				
			||||||
 | 
					                            assert(il.edge_a_id == edge_id || il.edge_b_id == edge_id);
 | 
				
			||||||
 | 
					                            emit_slab_edge(
 | 
				
			||||||
 | 
					                                IntersectionLine { {
 | 
				
			||||||
 | 
					                                        to_2d(edge_up ? vertices[i] : vertices[j]).cast<coord_t>(),
 | 
				
			||||||
 | 
					                                        il.edge_a_id == edge_id ? il.a : il.b
 | 
				
			||||||
 | 
					                                    },
 | 
				
			||||||
 | 
					                                    edge_up ? indices(i) : indices(j), -1, -1, edge_id + num_edges, IntersectionLine::FacetEdgeType::Slab
 | 
				
			||||||
 | 
					                                },
 | 
				
			||||||
 | 
					                                slab_id, ProjectionFromTop != edge_up);
 | 
				
			||||||
 | 
					                        } else if (intersects_prev) {
 | 
				
			||||||
 | 
					                            // Intersects just the bottom plane, may touch the top vertex.
 | 
				
			||||||
 | 
					                            assert(*it <= max_z);
 | 
				
			||||||
 | 
					#ifndef NDEBUG
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                auto it_prev = it;
 | 
				
			||||||
 | 
					                                -- it_prev;
 | 
				
			||||||
 | 
					                                assert((vertices[i].z() > *it_prev && vertices[j].z() < *it_prev) || (vertices[i].z() < *it_prev && vertices[j].z() > *it_prev));
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					#endif // NDEBUG
 | 
				
			||||||
 | 
					                            emit_slab_edge(
 | 
				
			||||||
 | 
					                                IntersectionLine { {
 | 
				
			||||||
 | 
					                                        il_prev.edge_a_id == edge_id ? il_prev.a : il_prev.b,
 | 
				
			||||||
 | 
					                                        to_2d(edge_up ? vertices[j] : vertices[i]).cast<coord_t>()
 | 
				
			||||||
 | 
					                                    },
 | 
				
			||||||
 | 
					                                    -1, edge_up ? indices(j) : indices(i), edge_id, -1, IntersectionLine::FacetEdgeType::Slab
 | 
				
			||||||
 | 
					                                },
 | 
				
			||||||
 | 
					                                slab_id, ProjectionFromTop != edge_up);
 | 
				
			||||||
 | 
					                        } else if (float zi = vertices[i].z(), zj = vertices[j].z(); zi < *it || zj < *it) {
 | 
				
			||||||
 | 
					                            // The edge does not intersect the current plane and it does not intersect the previous plane either.
 | 
				
			||||||
 | 
					                            // Both points have to be inside the slab.
 | 
				
			||||||
 | 
					                            assert(zi <= *it && zj <= *it);
 | 
				
			||||||
 | 
					#ifndef NDEBUG
 | 
				
			||||||
 | 
					                            if (type == FacetSliceType::Slicing || type == FacetSliceType::Cutting) {
 | 
				
			||||||
 | 
					                                // Such edge should already be processed in the code above, it shall be skipped here.
 | 
				
			||||||
 | 
					                                assert(indices(i) != il.b_id || indices(j) != il.a_id);
 | 
				
			||||||
 | 
					                                assert(indices(i) != il.a_id || indices(j) != il.b_id);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					#endif // NDEBUG
 | 
				
			||||||
 | 
					                            // Is it inside the slab?
 | 
				
			||||||
 | 
					                            bool inside_slab = true;
 | 
				
			||||||
 | 
					                            if (it != min_layer) {
 | 
				
			||||||
 | 
					                                auto it_prev = it;
 | 
				
			||||||
 | 
					                                -- it_prev;
 | 
				
			||||||
 | 
					                                assert(*it_prev >= *min_layer && *it_prev < *it);
 | 
				
			||||||
 | 
					                                // One point may touch the plane below, the other must not.
 | 
				
			||||||
 | 
					                                inside_slab = zi > *it_prev || zj > *it_prev;
 | 
				
			||||||
 | 
					                                // Both points have to be inside the slab.
 | 
				
			||||||
 | 
					                                assert(! inside_slab || (zi >= *it_prev && zj >= *it_prev));
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            if (inside_slab) {
 | 
				
			||||||
 | 
					                                assert(ProjectionFromTop ? vertices[i].z() >= zs[slab_id] : vertices[i].z() <= zs[slab_id]);
 | 
				
			||||||
 | 
					                                assert(ProjectionFromTop ? vertices[j].z() >= zs[slab_id] : vertices[j].z() <= zs[slab_id]);
 | 
				
			||||||
 | 
					                                emit_slab_edge(
 | 
				
			||||||
 | 
					                                    IntersectionLine {
 | 
				
			||||||
 | 
					                                        { to_2d(vertices[i]).cast<coord_t>(), to_2d(vertices[j]).cast<coord_t>() },
 | 
				
			||||||
 | 
					                                        indices(i), indices(j), -1, -1, IntersectionLine::FacetEdgeType::Slab
 | 
				
			||||||
 | 
					                                    },
 | 
				
			||||||
 | 
					                                    slab_id, ! ProjectionFromTop);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            il_prev = il;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (ProjectionFromTop || max_layer != zs.end()) {
 | 
				
			||||||
 | 
					            // Try to project unbound edges above the last slicing plane to the last slab.
 | 
				
			||||||
 | 
					            // Last layer slicing this triangle.
 | 
				
			||||||
 | 
					            auto   it      = max_layer - 1;
 | 
				
			||||||
 | 
					            size_t slab_id = max_layer - zs.begin();
 | 
				
			||||||
 | 
					            if (ProjectionFromTop)
 | 
				
			||||||
 | 
					                -- slab_id;
 | 
				
			||||||
 | 
					            for (int iedge = 0; iedge < 3; ++ iedge)
 | 
				
			||||||
 | 
					                if (facet_neighbors(iedge) == -1) {
 | 
				
			||||||
 | 
					                    // Unbound edge.
 | 
				
			||||||
 | 
					                    int edge_id = facet_edge_ids(iedge);
 | 
				
			||||||
 | 
					                    int i = iedge;
 | 
				
			||||||
 | 
					                    int j = next_idx_modulo(i, 3);
 | 
				
			||||||
 | 
					                    if (il_prev.edge_a_id == edge_id || il_prev.edge_b_id == edge_id) {
 | 
				
			||||||
 | 
					                        // Intersects just the bottom plane, may touch the top vertex.
 | 
				
			||||||
 | 
					                        assert((vertices[i].z() > *it && vertices[j].z() < *it) || (vertices[i].z() < *it && vertices[j].z() > *it));
 | 
				
			||||||
 | 
					                        bool edge_up = vertices[j].z() > vertices[i].z();
 | 
				
			||||||
 | 
					                        emit_slab_edge(
 | 
				
			||||||
 | 
					                            IntersectionLine{ {
 | 
				
			||||||
 | 
					                                    il_prev.edge_a_id == edge_id ? il_prev.a : il_prev.b,
 | 
				
			||||||
 | 
					                                    to_2d(edge_up ? vertices[j] : vertices[i]).cast<coord_t>()
 | 
				
			||||||
 | 
					                                },
 | 
				
			||||||
 | 
					                                -1, edge_up ? indices(j) : indices(i), edge_id, -1, IntersectionLine::FacetEdgeType::Slab
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                            slab_id, ProjectionFromTop != edge_up);
 | 
				
			||||||
 | 
					                    } else if (float zi = vertices[i].z(), zj = vertices[j].z(); zi > *it || zj > *it) {
 | 
				
			||||||
 | 
					                        // The edge does not intersect the current plane and it does not intersect the previous plane either.
 | 
				
			||||||
 | 
					                        // Both points have to be inside the slab.
 | 
				
			||||||
 | 
					                        assert(zi >= *it && zj >= *it);
 | 
				
			||||||
 | 
					                        assert(max_layer == zs.end() || (zi < *max_layer && zj < *max_layer));
 | 
				
			||||||
 | 
					                        emit_slab_edge(
 | 
				
			||||||
 | 
					                            IntersectionLine{
 | 
				
			||||||
 | 
					                                { to_2d(vertices[i]).cast<coord_t>(), to_2d(vertices[j]).cast<coord_t>() },
 | 
				
			||||||
 | 
					                                indices(i), indices(j), -1, -1, IntersectionLine::FacetEdgeType::Slab
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                            slab_id, ! ProjectionFromTop);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// used by slice_mesh_slabs() to produce on-slice lines and between-slices lines.
 | 
				
			||||||
 | 
					// Returning top / bottom SlabLines.
 | 
				
			||||||
 | 
					template<typename ThrowOnCancel>
 | 
				
			||||||
 | 
					inline std::pair<SlabLines, SlabLines> slice_slabs_make_lines(
 | 
				
			||||||
 | 
					    const std::vector<stl_vertex>                   &vertices,
 | 
				
			||||||
 | 
					    const std::vector<stl_triangle_vertex_indices>  &indices,
 | 
				
			||||||
 | 
					    const std::vector<Vec3i>                        &face_neighbors,
 | 
				
			||||||
 | 
					    const std::vector<Vec3i>                        &face_edge_ids,
 | 
				
			||||||
 | 
					    // Total number of edges. All face_edge_ids are lower than num_edges.
 | 
				
			||||||
 | 
					    // num_edges will be used to distinguish between intersections with the top and bottom plane.
 | 
				
			||||||
 | 
					    const int                                        num_edges,
 | 
				
			||||||
 | 
					    const std::vector<FaceOrientation>              &face_orientation,
 | 
				
			||||||
 | 
					    const std::vector<float>                        &zs,
 | 
				
			||||||
 | 
					    bool                                             top,
 | 
				
			||||||
 | 
					    bool                                             bottom,
 | 
				
			||||||
 | 
					    const ThrowOnCancel                              throw_on_cancel_fn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    std::pair<SlabLines, SlabLines> out;
 | 
				
			||||||
 | 
					    SlabLines   &lines_top      = out.first;
 | 
				
			||||||
 | 
					    SlabLines   &lines_bottom   = out.second;
 | 
				
			||||||
 | 
					    std::array<std::mutex, 64> lines_mutex_top;
 | 
				
			||||||
 | 
					    std::array<std::mutex, 64> lines_mutex_bottom;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (top) {
 | 
				
			||||||
 | 
					        lines_top.at_slice.assign(zs.size(), IntersectionLines());
 | 
				
			||||||
 | 
					        lines_top.between_slices.assign(zs.size(), IntersectionLines());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (bottom) {
 | 
				
			||||||
 | 
					        lines_bottom.at_slice.assign(zs.size(), IntersectionLines());
 | 
				
			||||||
 | 
					        lines_bottom.between_slices.assign(zs.size(), IntersectionLines());        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tbb::parallel_for(
 | 
				
			||||||
 | 
					        tbb::blocked_range<int>(0, int(indices.size())),
 | 
				
			||||||
 | 
					        [&vertices, &indices, &face_neighbors, &face_edge_ids, num_edges, &face_orientation, &zs, top, bottom, &lines_top, &lines_bottom, &lines_mutex_top, &lines_mutex_bottom, throw_on_cancel_fn]
 | 
				
			||||||
 | 
					        (const tbb::blocked_range<int> &range) {
 | 
				
			||||||
 | 
					            for (int face_idx = range.begin(); face_idx < range.end(); ++ face_idx) {
 | 
				
			||||||
 | 
					                if ((face_idx & 0x0ffff) == 0)
 | 
				
			||||||
 | 
					                    throw_on_cancel_fn();
 | 
				
			||||||
 | 
					                FaceOrientation fo       = face_orientation[face_idx];
 | 
				
			||||||
 | 
					                Vec3i           edge_ids = face_edge_ids[face_idx];
 | 
				
			||||||
 | 
					                if (top && (fo == FaceOrientation::Up || fo == FaceOrientation::Degenerate)) {
 | 
				
			||||||
 | 
					                    Vec3i neighbors = face_neighbors[face_idx];
 | 
				
			||||||
 | 
					                    // Reset neighborship of this triangle in case the other triangle is oriented backwards from this one.
 | 
				
			||||||
 | 
					                    for (int i = 0; i < 3; ++ i)
 | 
				
			||||||
 | 
					                        if (neighbors(i) != -1) {
 | 
				
			||||||
 | 
					                            FaceOrientation fo2 = face_orientation[neighbors(i)];
 | 
				
			||||||
 | 
					                            if (fo2 != FaceOrientation::Up && fo2 != FaceOrientation::Degenerate)
 | 
				
			||||||
 | 
					                                neighbors(i) = -1;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    slice_facet_with_slabs<true>(vertices, indices, face_idx, neighbors, edge_ids, num_edges, zs, lines_top, lines_mutex_top);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (bottom && (fo == FaceOrientation::Down || fo == FaceOrientation::Degenerate)) {
 | 
				
			||||||
 | 
					                    Vec3i neighbors = face_neighbors[face_idx];
 | 
				
			||||||
 | 
					                    // Reset neighborship of this triangle in case the other triangle is oriented backwards from this one.
 | 
				
			||||||
 | 
					                    for (int i = 0; i < 3; ++ i)
 | 
				
			||||||
 | 
					                        if (neighbors(i) != -1) {
 | 
				
			||||||
 | 
					                            FaceOrientation fo2 = face_orientation[neighbors(i)];
 | 
				
			||||||
 | 
					                            if (fo2 != FaceOrientation::Down && fo2 != FaceOrientation::Degenerate)
 | 
				
			||||||
 | 
					                                neighbors(i) = -1;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    slice_facet_with_slabs<false>(vertices, indices, face_idx, neighbors, edge_ids, num_edges, zs, lines_bottom, lines_mutex_bottom);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    return out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if 0
 | 
					#if 0
 | 
				
			||||||
//FIXME Should this go away? For valid meshes the function slice_facet() returns Slicing
 | 
					//FIXME Should this go away? For valid meshes the function slice_facet() returns Slicing
 | 
				
			||||||
// and sets edges of vertical triangles to produce only a single edge per pair of neighbor faces.
 | 
					// and sets edges of vertical triangles to produce only a single edge per pair of neighbor faces.
 | 
				
			||||||
| 
						 | 
					@ -495,6 +915,7 @@ static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polyg
 | 
				
			||||||
                if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) || 
 | 
					                if ((first_line->edge_a_id != -1 && first_line->edge_a_id == last_line->edge_b_id) || 
 | 
				
			||||||
                    (first_line->a_id      != -1 && first_line->a_id      == last_line->b_id)) {
 | 
					                    (first_line->a_id      != -1 && first_line->a_id      == last_line->b_id)) {
 | 
				
			||||||
                    // The current loop is complete. Add it to the output.
 | 
					                    // The current loop is complete. Add it to the output.
 | 
				
			||||||
 | 
					                    assert(first_line->a == last_line->b);
 | 
				
			||||||
                    loops.emplace_back(std::move(loop_pts));
 | 
					                    loops.emplace_back(std::move(loop_pts));
 | 
				
			||||||
                    #ifdef SLIC3R_TRIANGLEMESH_DEBUG
 | 
					                    #ifdef SLIC3R_TRIANGLEMESH_DEBUG
 | 
				
			||||||
                    printf("  Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
 | 
					                    printf("  Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size());
 | 
				
			||||||
| 
						 | 
					@ -513,6 +934,7 @@ static void chain_lines_by_triangle_connectivity(IntersectionLines &lines, Polyg
 | 
				
			||||||
                next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id,
 | 
					                next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id,
 | 
				
			||||||
                next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y);
 | 
					                next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y);
 | 
				
			||||||
            */
 | 
					            */
 | 
				
			||||||
 | 
					            assert(last_line->b == next_line->a);
 | 
				
			||||||
            loop_pts.emplace_back(next_line->a);
 | 
					            loop_pts.emplace_back(next_line->a);
 | 
				
			||||||
            last_line = next_line;
 | 
					            last_line = next_line;
 | 
				
			||||||
            next_line->set_skip();
 | 
					            next_line->set_skip();
 | 
				
			||||||
| 
						 | 
					@ -778,7 +1200,7 @@ static Polygons make_loops(
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            static int iRun = 0;
 | 
					            static int iRun = 0;
 | 
				
			||||||
            SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-%d.svg", iRun ++).c_str(), bbox_svg);
 | 
					            SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-%d.svg", iRun ++).c_str(), bbox_svg);
 | 
				
			||||||
            svg.draw(union_ex(*loops));
 | 
					            svg.draw(union_ex(loops));
 | 
				
			||||||
            for (const OpenPolyline &pl : open_polylines)
 | 
					            for (const OpenPolyline &pl : open_polylines)
 | 
				
			||||||
                svg.draw(Polyline(pl.points), "red");
 | 
					                svg.draw(Polyline(pl.points), "red");
 | 
				
			||||||
            svg.Close();
 | 
					            svg.Close();
 | 
				
			||||||
| 
						 | 
					@ -795,7 +1217,7 @@ static Polygons make_loops(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        static int iRun = 0;
 | 
					        static int iRun = 0;
 | 
				
			||||||
        SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines2-%d.svg", iRun++).c_str(), bbox_svg);
 | 
					        SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines2-%d.svg", iRun++).c_str(), bbox_svg);
 | 
				
			||||||
        svg.draw(union_ex(*loops));
 | 
					        svg.draw(union_ex(loops));
 | 
				
			||||||
        for (const OpenPolyline &pl : open_polylines) {
 | 
					        for (const OpenPolyline &pl : open_polylines) {
 | 
				
			||||||
            if (pl.points.empty())
 | 
					            if (pl.points.empty())
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
| 
						 | 
					@ -825,7 +1247,7 @@ static Polygons make_loops(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        static int iRun = 0;
 | 
					        static int iRun = 0;
 | 
				
			||||||
        SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-final-%d.svg", iRun++).c_str(), bbox_svg);
 | 
					        SVG svg(debug_out_path("TriangleMeshSlicer_make_loops-polylines-final-%d.svg", iRun++).c_str(), bbox_svg);
 | 
				
			||||||
        svg.draw(union_ex(*loops));
 | 
					        svg.draw(union_ex(loops));
 | 
				
			||||||
        for (const OpenPolyline &pl : open_polylines) {
 | 
					        for (const OpenPolyline &pl : open_polylines) {
 | 
				
			||||||
            if (pl.points.empty())
 | 
					            if (pl.points.empty())
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
| 
						 | 
					@ -892,6 +1314,134 @@ static std::vector<Polygons> make_loops(
 | 
				
			||||||
    return layers;
 | 
					    return layers;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// used by slice_mesh_slabs() to produce loops from on-slice lines and between-slices lines.
 | 
				
			||||||
 | 
					template<bool ProjectionFromTop, typename ThrowOnCancel>
 | 
				
			||||||
 | 
					static std::vector<Polygons> make_slab_loops(
 | 
				
			||||||
 | 
					    // Lines will have their flags modified.
 | 
				
			||||||
 | 
					    SlabLines                      &lines, 
 | 
				
			||||||
 | 
					    // To differentiate edge IDs of the top plane from the edge IDs of the bottom plane for chaining.
 | 
				
			||||||
 | 
					    int                             num_edges,
 | 
				
			||||||
 | 
					    ThrowOnCancel                   throw_on_cancel)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
 | 
				
			||||||
 | 
					    static int iRun = 0;
 | 
				
			||||||
 | 
					    ++ iRun;
 | 
				
			||||||
 | 
					#endif // SLIC3R_DEBUG_SLICE_PROCESSING
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert(! lines.at_slice.empty() && lines.at_slice.size() == lines.between_slices.size());
 | 
				
			||||||
 | 
					    std::vector<Polygons> layers;
 | 
				
			||||||
 | 
					    layers.resize(lines.at_slice.size());
 | 
				
			||||||
 | 
					    tbb::parallel_for(
 | 
				
			||||||
 | 
					        tbb::blocked_range<int>(0, int(lines.at_slice.size())),
 | 
				
			||||||
 | 
					        [&lines, num_edges, &layers, throw_on_cancel](const tbb::blocked_range<int> &range) {
 | 
				
			||||||
 | 
					            for (int line_idx = range.begin(); line_idx < range.end(); ++ line_idx) {
 | 
				
			||||||
 | 
					                if ((line_idx & 0x0ffff) == 0)
 | 
				
			||||||
 | 
					                    throw_on_cancel();
 | 
				
			||||||
 | 
					                IntersectionLines in;
 | 
				
			||||||
 | 
					                size_t nlines          = lines.between_slices[line_idx].size();
 | 
				
			||||||
 | 
					                int    slice_below     = ProjectionFromTop ? line_idx : line_idx - 1;
 | 
				
			||||||
 | 
					                int    slice_above     = ProjectionFromTop ? line_idx + 1 : line_idx;
 | 
				
			||||||
 | 
					                bool   has_slice_below = ProjectionFromTop || line_idx > 0;
 | 
				
			||||||
 | 
					                bool   has_slice_above = ! ProjectionFromTop || line_idx + 1 < int(lines.at_slice.size());
 | 
				
			||||||
 | 
					                if (has_slice_below)
 | 
				
			||||||
 | 
					                    nlines += lines.at_slice[slice_below].size();
 | 
				
			||||||
 | 
					                if (has_slice_above)
 | 
				
			||||||
 | 
					                    nlines += lines.at_slice[slice_above].size();
 | 
				
			||||||
 | 
					                if (nlines) {
 | 
				
			||||||
 | 
					                    in.reserve(nlines);
 | 
				
			||||||
 | 
					                    if (has_slice_below) {
 | 
				
			||||||
 | 
					                        for (const IntersectionLine &l : lines.at_slice[slice_below])
 | 
				
			||||||
 | 
					                            if (l.edge_type != IntersectionLine::FacetEdgeType::Top) {
 | 
				
			||||||
 | 
					                                in.emplace_back(l);
 | 
				
			||||||
 | 
					#ifndef NDEBUG
 | 
				
			||||||
 | 
					                                in.back().source = IntersectionLine::Source::BottomPlane;
 | 
				
			||||||
 | 
					#endif // NDEBUG
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        // Edges in between slice_below and slice_above.
 | 
				
			||||||
 | 
					#ifndef NDEBUG
 | 
				
			||||||
 | 
					                        size_t old_size = in.size();
 | 
				
			||||||
 | 
					#endif // NDEBUG
 | 
				
			||||||
 | 
					                        // Edge IDs of end points on in-between lines that touch the layer above are already increased with num_edges.
 | 
				
			||||||
 | 
					                        append(in, lines.between_slices[line_idx]);
 | 
				
			||||||
 | 
					#ifndef NDEBUG
 | 
				
			||||||
 | 
					                        for (auto it = in.begin() + old_size; it != in.end(); ++ it) {
 | 
				
			||||||
 | 
					                            assert(it->edge_type == IntersectionLine::FacetEdgeType::Slab);
 | 
				
			||||||
 | 
					                            it->source = IntersectionLine::Source::Slab;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					#endif // NDEBUG
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if (has_slice_above) {
 | 
				
			||||||
 | 
					                        for (const IntersectionLine &lsrc : lines.at_slice[slice_above])
 | 
				
			||||||
 | 
					                            if (lsrc.edge_type != IntersectionLine::FacetEdgeType::Bottom) {
 | 
				
			||||||
 | 
					                                in.emplace_back(lsrc);
 | 
				
			||||||
 | 
					                                auto &l = in.back();
 | 
				
			||||||
 | 
					                                l.reverse();
 | 
				
			||||||
 | 
					                                // Differentiate edge IDs of the top plane from the edge IDs of the bottom plane for chaining.
 | 
				
			||||||
 | 
					                                if (l.edge_a_id >= 0)
 | 
				
			||||||
 | 
					                                    l.edge_a_id += num_edges;
 | 
				
			||||||
 | 
					                                if (l.edge_b_id >= 0)
 | 
				
			||||||
 | 
					                                    l.edge_b_id += num_edges;
 | 
				
			||||||
 | 
					#ifndef NDEBUG
 | 
				
			||||||
 | 
					                                l.source = IntersectionLine::Source::TopPlane;
 | 
				
			||||||
 | 
					#endif // NDEBUG
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if (! in.empty()) {
 | 
				
			||||||
 | 
					#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
 | 
				
			||||||
 | 
					                        BoundingBox bbox_svg;
 | 
				
			||||||
 | 
					                        coordf_t stroke_width = scale_(0.02);
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            for (const IntersectionLine &line : in) {
 | 
				
			||||||
 | 
					                                bbox_svg.merge(line.a);
 | 
				
			||||||
 | 
					                                bbox_svg.merge(line.b);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            SVG svg(debug_out_path("make_slab_loops-in-%d-%d-%s.svg", iRun, line_idx, ProjectionFromTop ? "top" : "bottom").c_str(), bbox_svg);
 | 
				
			||||||
 | 
					                            svg.arrows = true;
 | 
				
			||||||
 | 
					                            for (const IntersectionLine& line : in) {
 | 
				
			||||||
 | 
					                                const char* color = line.source == IntersectionLine::Source::BottomPlane ? "red" : line.source == IntersectionLine::Source::TopPlane ? "blue" : "green";
 | 
				
			||||||
 | 
					                                svg.draw(line, color, stroke_width);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            svg.Close();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
 | 
				
			||||||
 | 
					                        Polygons &loops = layers[line_idx];
 | 
				
			||||||
 | 
					                        std::vector<OpenPolyline> open_polylines;
 | 
				
			||||||
 | 
					                        chain_lines_by_triangle_connectivity(in, loops, open_polylines);
 | 
				
			||||||
 | 
					#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            SVG svg(debug_out_path("make_slab_loops-out-%d-%d-%s.svg", iRun, line_idx, ProjectionFromTop ? "top" : "bottom").c_str(), bbox_svg);
 | 
				
			||||||
 | 
					                            svg.arrows = true;
 | 
				
			||||||
 | 
					                            for (const IntersectionLine& line : in) {
 | 
				
			||||||
 | 
					                                const char* color = line.source == IntersectionLine::Source::BottomPlane ? "red" : line.source == IntersectionLine::Source::TopPlane ? "blue" : "green";
 | 
				
			||||||
 | 
					                                svg.draw(line, color, stroke_width);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            svg.draw(loops, "black");
 | 
				
			||||||
 | 
					                            svg.Close();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            SVG svg(debug_out_path("make_slab_loops-open-polylines-%d-%d-%s.svg", iRun, line_idx, ProjectionFromTop ? "top" : "bottom").c_str(), bbox_svg);
 | 
				
			||||||
 | 
					                            svg.draw(loops, "black");
 | 
				
			||||||
 | 
					                            svg.arrows = true;
 | 
				
			||||||
 | 
					                            for (const OpenPolyline &open_polyline : open_polylines)
 | 
				
			||||||
 | 
					                                svg.draw(Polyline(open_polyline.points), "black", stroke_width);
 | 
				
			||||||
 | 
					                            svg.Close();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
 | 
				
			||||||
 | 
					                        assert(! loops.empty());
 | 
				
			||||||
 | 
					                        assert(open_polylines.empty());
 | 
				
			||||||
 | 
					                        if (! open_polylines.empty())
 | 
				
			||||||
 | 
					                            BOOST_LOG_TRIVIAL(trace) << "make_slab_loops - chaining failed. #" << open_polylines.size() << " open polylines";
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return layers;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Used to cut the mesh into two halves.
 | 
					// Used to cut the mesh into two halves.
 | 
				
			||||||
static ExPolygons make_expolygons_simple(std::vector<IntersectionLine> &lines)
 | 
					static ExPolygons make_expolygons_simple(std::vector<IntersectionLine> &lines)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -1055,6 +1605,44 @@ static void make_expolygons(const Polygons &loops, const float closing_radius, c
 | 
				
			||||||
        union_ex(loops, fill_type));
 | 
					        union_ex(loops, fill_type));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Make a trafo for transforming the vertices. Scale up in XY, not in Z.
 | 
				
			||||||
 | 
					static inline Transform3f make_trafo_for_slicing(const Transform3d &trafo)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    auto t = trafo;
 | 
				
			||||||
 | 
					    static constexpr const double s = 1. / SCALING_FACTOR;
 | 
				
			||||||
 | 
					    t.prescale(Vec3d(s, s, 1.));
 | 
				
			||||||
 | 
					    return t.cast<float>();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool is_identity(const Transform3d &trafo)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return trafo.matrix() == Transform3d::Identity().matrix();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static std::vector<stl_vertex> transform_mesh_vertices_for_slicing(const indexed_triangle_set &mesh, const Transform3d &trafo)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // Copy and scale vertices in XY, don't scale in Z.
 | 
				
			||||||
 | 
					    // Possibly apply the transformation.
 | 
				
			||||||
 | 
					    static constexpr const double   s = 1. / SCALING_FACTOR;
 | 
				
			||||||
 | 
					    std::vector<stl_vertex>         out(mesh.vertices);
 | 
				
			||||||
 | 
					    if (is_identity(trafo)) {
 | 
				
			||||||
 | 
					        // Identity.
 | 
				
			||||||
 | 
					        for (stl_vertex &v : out) {
 | 
				
			||||||
 | 
					            // Scale just XY, leave Z unscaled.
 | 
				
			||||||
 | 
					            v.x() *= float(s);
 | 
				
			||||||
 | 
					            v.y() *= float(s);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        // Transform the vertices, scale up in XY, not in Y.
 | 
				
			||||||
 | 
					        auto t = trafo;
 | 
				
			||||||
 | 
					        t.prescale(Vec3d(s, s, 1.));
 | 
				
			||||||
 | 
					        auto tf = t.cast<float>();
 | 
				
			||||||
 | 
					        for (stl_vertex &v : out)
 | 
				
			||||||
 | 
					            v = tf * v;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::vector<Polygons> slice_mesh(
 | 
					std::vector<Polygons> slice_mesh(
 | 
				
			||||||
    const indexed_triangle_set       &mesh,
 | 
					    const indexed_triangle_set       &mesh,
 | 
				
			||||||
    // Unscaled Zs
 | 
					    // Unscaled Zs
 | 
				
			||||||
| 
						 | 
					@ -1071,41 +1659,23 @@ std::vector<Polygons> slice_mesh(
 | 
				
			||||||
        // Instead of edge identifiers, one shall use a sorted pair of edge vertex indices.
 | 
					        // Instead of edge identifiers, one shall use a sorted pair of edge vertex indices.
 | 
				
			||||||
        // However facets_edges assigns a single edge ID to two triangles only, thus when factoring facets_edges out, one will have
 | 
					        // However facets_edges assigns a single edge ID to two triangles only, thus when factoring facets_edges out, one will have
 | 
				
			||||||
        // to make sure that no code relies on it.
 | 
					        // to make sure that no code relies on it.
 | 
				
			||||||
        std::vector<Vec3i>            facets_edges = create_face_neighbors_index(mesh);
 | 
					        std::vector<Vec3i> face_edge_ids = its_face_edge_ids(mesh);
 | 
				
			||||||
        const bool                    identity     = params.trafo.matrix() == Transform3d::Identity().matrix();
 | 
					 | 
				
			||||||
        static constexpr const double s            = 1. / SCALING_FACTOR;
 | 
					 | 
				
			||||||
        if (zs.size() <= 1) {
 | 
					        if (zs.size() <= 1) {
 | 
				
			||||||
            // It likely is not worthwile to copy the vertices. Apply the transformation in place.
 | 
					            // It likely is not worthwile to copy the vertices. Apply the transformation in place.
 | 
				
			||||||
            if (identity)
 | 
					            if (is_identity(params.trafo)) {
 | 
				
			||||||
                lines = slice_make_lines(
 | 
					                lines = slice_make_lines(
 | 
				
			||||||
                    mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled<float>(p.x()), scaled<float>(p.y()), p.z()); }, 
 | 
					                    mesh.vertices, [](const Vec3f &p) { return Vec3f(scaled<float>(p.x()), scaled<float>(p.y()), p.z()); }, 
 | 
				
			||||||
                    mesh.indices, facets_edges, zs, throw_on_cancel);
 | 
					                    mesh.indices, face_edge_ids, zs, throw_on_cancel);
 | 
				
			||||||
            else {
 | 
					            } else {
 | 
				
			||||||
                // Transform the vertices, scale up in XY, not in Y.
 | 
					                // Transform the vertices, scale up in XY, not in Z.
 | 
				
			||||||
                auto t = params.trafo;
 | 
					                Transform3f tf = make_trafo_for_slicing(params.trafo);
 | 
				
			||||||
                t.prescale(Vec3d(s, s, 1.));
 | 
					                lines = slice_make_lines(mesh.vertices, [tf](const Vec3f &p) { return tf * p; }, mesh.indices, face_edge_ids, zs, throw_on_cancel);
 | 
				
			||||||
                auto tf = t.cast<float>();
 | 
					 | 
				
			||||||
                lines = slice_make_lines(mesh.vertices, [tf](const Vec3f &p) { return tf * p; }, mesh.indices, facets_edges, zs, throw_on_cancel);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // Copy and scale vertices in XY, don't scale in Z.
 | 
					            // Copy and scale vertices in XY, don't scale in Z. Possibly apply the transformation.
 | 
				
			||||||
            // Possibly apply the transformation.
 | 
					            lines = slice_make_lines(
 | 
				
			||||||
            std::vector<stl_vertex> vertices(mesh.vertices);
 | 
					                transform_mesh_vertices_for_slicing(mesh, params.trafo), 
 | 
				
			||||||
            if (identity) {
 | 
					                [](const Vec3f &p) { return p; },  mesh.indices, face_edge_ids, zs, throw_on_cancel);
 | 
				
			||||||
                for (stl_vertex &v : vertices) {
 | 
					 | 
				
			||||||
                    // Scale just XY, leave Z unscaled.
 | 
					 | 
				
			||||||
                    v.x() *= float(s);
 | 
					 | 
				
			||||||
                    v.y() *= float(s);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                // Transform the vertices, scale up in XY, not in Y.
 | 
					 | 
				
			||||||
                auto t = params.trafo;
 | 
					 | 
				
			||||||
                t.prescale(Vec3d(s, s, 1.));
 | 
					 | 
				
			||||||
                auto tf = t.cast<float>();
 | 
					 | 
				
			||||||
                for (stl_vertex &v : vertices)
 | 
					 | 
				
			||||||
                    v = tf * v;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            lines = slice_make_lines(vertices, [](const Vec3f &p) { return p; },  mesh.indices, facets_edges, zs, throw_on_cancel);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1194,6 +1764,107 @@ std::vector<ExPolygons> slice_mesh_ex(
 | 
				
			||||||
    return layers;
 | 
					    return layers;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Slice a triangle set with a set of Z slabs (thick layers).
 | 
				
			||||||
 | 
					// The effect is similar to producing the usual top / bottom layers from a sliced mesh by 
 | 
				
			||||||
 | 
					// subtracting layer[i] from layer[i - 1] for the top surfaces resp.
 | 
				
			||||||
 | 
					// subtracting layer[i] from layer[i + 1] for the bottom surfaces,
 | 
				
			||||||
 | 
					// with the exception that the triangle set this function processes may not cover the whole top resp. bottom surface.
 | 
				
			||||||
 | 
					// top resp. bottom surfaces are calculated only if out_top resp. out_bottom is not null.
 | 
				
			||||||
 | 
					void slice_mesh_slabs(
 | 
				
			||||||
 | 
					    const indexed_triangle_set       &mesh,
 | 
				
			||||||
 | 
					    // Unscaled Zs
 | 
				
			||||||
 | 
					    const std::vector<float>         &zs,
 | 
				
			||||||
 | 
					    const Transform3d                &trafo,
 | 
				
			||||||
 | 
					    std::vector<Polygons>            *out_top,
 | 
				
			||||||
 | 
					    std::vector<Polygons>            *out_bottom,
 | 
				
			||||||
 | 
					    std::function<void()>             throw_on_cancel)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    BOOST_LOG_TRIVIAL(debug) << "slice_mesh_slabs to polygons";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef EXPENSIVE_DEBUG_CHECKS
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Verify that the vertices are unique.
 | 
				
			||||||
 | 
					        auto v = mesh.vertices;
 | 
				
			||||||
 | 
					        std::sort(v.begin(), v.end(), [](auto &l, auto &r) {
 | 
				
			||||||
 | 
					                return l.x() < r.x() || (l.x() == r.x() && (l.y() < r.y() || (l.y() == r.y() && l.z() < r.z())));
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        size_t num_duplicates = v.end() - std::unique(v.begin(), v.end());
 | 
				
			||||||
 | 
					        assert(num_duplicates == 0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (0)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Verify that there are no T-joints.
 | 
				
			||||||
 | 
					        // The T-joints could likely be already part of the source mesh.
 | 
				
			||||||
 | 
					        for (const auto &tri : mesh.indices)
 | 
				
			||||||
 | 
					            for (int i = 0; i < 3; ++ i) {
 | 
				
			||||||
 | 
					                int j = next_idx_modulo(i, 3);
 | 
				
			||||||
 | 
					                int k = next_idx_modulo(j, 3);
 | 
				
			||||||
 | 
					                auto &v1 = mesh.vertices[tri(i)];
 | 
				
			||||||
 | 
					                auto &v2 = mesh.vertices[tri(j)];
 | 
				
			||||||
 | 
					                auto &v3 = mesh.vertices[tri(k)];
 | 
				
			||||||
 | 
					                for (auto &pt : mesh.vertices)
 | 
				
			||||||
 | 
					                    if (&pt != &v1 && &pt != &v2) {
 | 
				
			||||||
 | 
					                        assert(pt != v1 && pt != v2);
 | 
				
			||||||
 | 
					                        assert((pt - v1).norm() > EPSILON);
 | 
				
			||||||
 | 
					                        assert((pt - v2).norm() > EPSILON);
 | 
				
			||||||
 | 
					                        auto   l2  = (v2 - v1).squaredNorm();
 | 
				
			||||||
 | 
					                        assert(l2 > 0);
 | 
				
			||||||
 | 
					                        auto   t   = (pt - v1).dot(v2 - v1);
 | 
				
			||||||
 | 
					                        if (t > 0 && t < l2) {
 | 
				
			||||||
 | 
					                            auto d2 = (pt - v1).squaredNorm() - sqr(t) / l2;
 | 
				
			||||||
 | 
					                            auto d = sqrt(std::max(d2, 0.f));
 | 
				
			||||||
 | 
					                            if (&pt == &v3) {
 | 
				
			||||||
 | 
					                                if (d < EPSILON)
 | 
				
			||||||
 | 
					                                    printf("Degenerate triangle!\n");
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                assert(d > EPSILON);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#endif // EXPENSIVE_DEBUG_CHECKS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<stl_vertex> vertices_transformed = transform_mesh_vertices_for_slicing(mesh, trafo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<FaceOrientation> face_orientation(mesh.indices.size(), FaceOrientation::Up);
 | 
				
			||||||
 | 
					    for (const stl_triangle_vertex_indices &tri : mesh.indices) {
 | 
				
			||||||
 | 
					        const Vec3f   fa = vertices_transformed[tri(0)];
 | 
				
			||||||
 | 
					        const Vec3f   fb = vertices_transformed[tri(1)];
 | 
				
			||||||
 | 
					        const Vec3f   fc = vertices_transformed[tri(2)];
 | 
				
			||||||
 | 
					        assert(fa != fb && fa != fc && fb != fc);
 | 
				
			||||||
 | 
					        const Point   a = to_2d(fa).cast<coord_t>();
 | 
				
			||||||
 | 
					        const Point   b = to_2d(fb).cast<coord_t>();
 | 
				
			||||||
 | 
					        const Point   c = to_2d(fc).cast<coord_t>();
 | 
				
			||||||
 | 
					        const int64_t d = cross2((b - a).cast<int64_t>(), (c - b).cast<int64_t>());
 | 
				
			||||||
 | 
					        FaceOrientation fo = FaceOrientation::Vertical;
 | 
				
			||||||
 | 
					        if (d > 0)
 | 
				
			||||||
 | 
					            fo = FaceOrientation::Up;
 | 
				
			||||||
 | 
					        else if (d < 0)
 | 
				
			||||||
 | 
					            fo = FaceOrientation::Down;
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            // Is the triangle vertical or degenerate?
 | 
				
			||||||
 | 
					            assert(d == 0);
 | 
				
			||||||
 | 
					            fo = fa == fb || fa == fc || fb == fc ? FaceOrientation::Degenerate : FaceOrientation::Vertical;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        face_orientation[&tri - mesh.indices.data()] = fo;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<Vec3i> face_neighbors = its_face_neighbors_par(mesh);
 | 
				
			||||||
 | 
					    int                num_edges;
 | 
				
			||||||
 | 
					    std::vector<Vec3i> face_edge_ids  = its_face_edge_ids(mesh, face_neighbors, true, &num_edges);
 | 
				
			||||||
 | 
					    std::pair<SlabLines, SlabLines> lines = slice_slabs_make_lines(
 | 
				
			||||||
 | 
					        vertices_transformed, mesh.indices, face_neighbors, face_edge_ids, num_edges, face_orientation, zs, 
 | 
				
			||||||
 | 
					        out_top != nullptr, out_bottom != nullptr, throw_on_cancel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    throw_on_cancel();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (out_top)
 | 
				
			||||||
 | 
					        *out_top = make_slab_loops<true>(lines.first, num_edges, throw_on_cancel);
 | 
				
			||||||
 | 
					    if (out_bottom)
 | 
				
			||||||
 | 
					        *out_bottom = make_slab_loops<false>(lines.second, num_edges, throw_on_cancel);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Remove duplicates of slice_vertices, optionally triangulate the cut.
 | 
					// Remove duplicates of slice_vertices, optionally triangulate the cut.
 | 
				
			||||||
static void triangulate_slice(
 | 
					static void triangulate_slice(
 | 
				
			||||||
    indexed_triangle_set    &its, 
 | 
					    indexed_triangle_set    &its, 
 | 
				
			||||||
| 
						 | 
					@ -1308,7 +1979,7 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
 | 
				
			||||||
    // To triangulate the caps after slicing.
 | 
					    // To triangulate the caps after slicing.
 | 
				
			||||||
    IntersectionLines  upper_lines, lower_lines;
 | 
					    IntersectionLines  upper_lines, lower_lines;
 | 
				
			||||||
    std::vector<int>   upper_slice_vertices, lower_slice_vertices;
 | 
					    std::vector<int>   upper_slice_vertices, lower_slice_vertices;
 | 
				
			||||||
    std::vector<Vec3i> facets_edges = create_face_neighbors_index(mesh);
 | 
					    std::vector<Vec3i> facets_edge_ids = its_face_edge_ids(mesh);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (int facet_idx = 0; facet_idx < int(mesh.indices.size()); ++ facet_idx) {
 | 
					    for (int facet_idx = 0; facet_idx < int(mesh.indices.size()); ++ facet_idx) {
 | 
				
			||||||
        const stl_triangle_vertex_indices &facet = mesh.indices[facet_idx];
 | 
					        const stl_triangle_vertex_indices &facet = mesh.indices[facet_idx];
 | 
				
			||||||
| 
						 | 
					@ -1329,7 +2000,7 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
 | 
				
			||||||
                dst.y() = scale_(src.y());
 | 
					                dst.y() = scale_(src.y());
 | 
				
			||||||
                dst.z() = src.z();
 | 
					                dst.z() = src.z();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            slice_type = slice_facet(z, vertices_scaled, mesh.indices[facet_idx], facets_edges[facet_idx], idx_vertex_lowest, min_z == max_z, line);
 | 
					            slice_type = slice_facet(z, vertices_scaled, mesh.indices[facet_idx], facets_edge_ids[facet_idx], idx_vertex_lowest, min_z == max_z, line);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (slice_type != FacetSliceType::NoSlice) {
 | 
					        if (slice_type != FacetSliceType::NoSlice) {
 | 
				
			||||||
| 
						 | 
					@ -1371,8 +2042,8 @@ void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *u
 | 
				
			||||||
            // get vertices starting from the isolated one
 | 
					            // get vertices starting from the isolated one
 | 
				
			||||||
            int iv = isolated_vertex;
 | 
					            int iv = isolated_vertex;
 | 
				
			||||||
            stl_vertex v0v1, v2v0;
 | 
					            stl_vertex v0v1, v2v0;
 | 
				
			||||||
            assert(facets_edges[facet_idx](iv) == line.edge_a_id ||facets_edges[facet_idx](iv) == line.edge_b_id);
 | 
					            assert(facets_edge_ids[facet_idx](iv) == line.edge_a_id ||facets_edge_ids[facet_idx](iv) == line.edge_b_id);
 | 
				
			||||||
            if (facets_edges[facet_idx](iv) == line.edge_a_id) {
 | 
					            if (facets_edge_ids[facet_idx](iv) == line.edge_a_id) {
 | 
				
			||||||
                v0v1 = to_3d(unscaled<float>(line.a), z);
 | 
					                v0v1 = to_3d(unscaled<float>(line.a), z);
 | 
				
			||||||
                v2v0 = to_3d(unscaled<float>(line.b), z);
 | 
					                v2v0 = to_3d(unscaled<float>(line.b), z);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -77,6 +77,21 @@ inline std::vector<ExPolygons>  slice_mesh_ex(
 | 
				
			||||||
    return slice_mesh_ex(mesh, zs, params, throw_on_cancel);
 | 
					    return slice_mesh_ex(mesh, zs, params, throw_on_cancel);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Slice a triangle set with a set of Z slabs (thick layers).
 | 
				
			||||||
 | 
					// The effect is similar to producing the usual top / bottom layers from a sliced mesh by 
 | 
				
			||||||
 | 
					// subtracting layer[i] from layer[i - 1] for the top surfaces resp.
 | 
				
			||||||
 | 
					// subtracting layer[i] from layer[i + 1] for the bottom surfaces,
 | 
				
			||||||
 | 
					// with the exception that the triangle set this function processes may not cover the whole top resp. bottom surface.
 | 
				
			||||||
 | 
					// top resp. bottom surfaces are calculated only if out_top resp. out_bottom is not null.
 | 
				
			||||||
 | 
					void slice_mesh_slabs(
 | 
				
			||||||
 | 
					    const indexed_triangle_set       &mesh,
 | 
				
			||||||
 | 
					    // Unscaled Zs
 | 
				
			||||||
 | 
					    const std::vector<float>         &zs,
 | 
				
			||||||
 | 
					    const Transform3d                &trafo,
 | 
				
			||||||
 | 
					    std::vector<Polygons>            *out_top,
 | 
				
			||||||
 | 
					    std::vector<Polygons>            *out_bottom,
 | 
				
			||||||
 | 
					    std::function<void()>             throw_on_cancel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void                            cut_mesh(
 | 
					void                            cut_mesh(
 | 
				
			||||||
    const indexed_triangle_set      &mesh,
 | 
					    const indexed_triangle_set      &mesh,
 | 
				
			||||||
    float                            z,
 | 
					    float                            z,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -810,7 +810,11 @@ int TriangleSelector::push_triangle(int a, int b, int c, int source_triangle, co
 | 
				
			||||||
void TriangleSelector::perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state)
 | 
					void TriangleSelector::perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    // Reserve space for the new triangles upfront, so that the reference to this triangle will not change.
 | 
					    // Reserve space for the new triangles upfront, so that the reference to this triangle will not change.
 | 
				
			||||||
    m_triangles.reserve(m_triangles.size() + m_triangles[facet_idx].number_of_split_sides() + 1);
 | 
					    {
 | 
				
			||||||
 | 
					        size_t num_triangles_new = m_triangles.size() + m_triangles[facet_idx].number_of_split_sides() + 1;
 | 
				
			||||||
 | 
					        if (m_triangles.capacity() < num_triangles_new)
 | 
				
			||||||
 | 
					            m_triangles.reserve(next_highest_power_of_2(num_triangles_new));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Triangle &tr = m_triangles[facet_idx];
 | 
					    Triangle &tr = m_triangles[facet_idx];
 | 
				
			||||||
    assert(tr.is_split());
 | 
					    assert(tr.is_split());
 | 
				
			||||||
| 
						 | 
					@ -918,7 +922,7 @@ indexed_triangle_set TriangleSelector::get_facets_strict(EnforcerBlockerType sta
 | 
				
			||||||
            ++ num_vertices;
 | 
					            ++ num_vertices;
 | 
				
			||||||
    out.vertices.reserve(num_vertices);
 | 
					    out.vertices.reserve(num_vertices);
 | 
				
			||||||
    std::vector<int> vertex_map(m_vertices.size(), -1);
 | 
					    std::vector<int> vertex_map(m_vertices.size(), -1);
 | 
				
			||||||
    for (int i = 0; i < m_vertices.size(); ++ i)
 | 
					    for (size_t i = 0; i < m_vertices.size(); ++ i)
 | 
				
			||||||
        if (const Vertex &v = m_vertices[i]; v.ref_cnt > 0) {
 | 
					        if (const Vertex &v = m_vertices[i]; v.ref_cnt > 0) {
 | 
				
			||||||
            vertex_map[i] = int(out.vertices.size());
 | 
					            vertex_map[i] = int(out.vertices.size());
 | 
				
			||||||
            out.vertices.emplace_back(v.v);
 | 
					            out.vertices.emplace_back(v.v);
 | 
				
			||||||
| 
						 | 
					@ -958,10 +962,13 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i vertices, const V
 | 
				
			||||||
        this->triangle_midpoint(neighbors(1), vertices(2), vertices(1)),
 | 
					        this->triangle_midpoint(neighbors(1), vertices(2), vertices(1)),
 | 
				
			||||||
        this->triangle_midpoint(neighbors(2), vertices(0), vertices(2)));
 | 
					        this->triangle_midpoint(neighbors(2), vertices(0), vertices(2)));
 | 
				
			||||||
    int splits = (midpoints(0) != -1) + (midpoints(1) != -1) + (midpoints(2) != -1);
 | 
					    int splits = (midpoints(0) != -1) + (midpoints(1) != -1) + (midpoints(2) != -1);
 | 
				
			||||||
    if (splits == 0) {
 | 
					    switch (splits) {
 | 
				
			||||||
 | 
					    case 0:
 | 
				
			||||||
        // Just emit this triangle.
 | 
					        // Just emit this triangle.
 | 
				
			||||||
        out_triangles.emplace_back(vertices(0), midpoints(0), midpoints(2));
 | 
					        out_triangles.emplace_back(vertices(0), vertices(1), vertices(2));
 | 
				
			||||||
    } else if (splits == 1) {
 | 
					        break;
 | 
				
			||||||
 | 
					    case 1:
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        // Split to two triangles
 | 
					        // Split to two triangles
 | 
				
			||||||
        int i = midpoints(0) != -1 ? 2 : midpoints(1) != -1 ? 0 : 1;
 | 
					        int i = midpoints(0) != -1 ? 2 : midpoints(1) != -1 ? 0 : 1;
 | 
				
			||||||
        int j = next_idx_modulo(i, 3);
 | 
					        int j = next_idx_modulo(i, 3);
 | 
				
			||||||
| 
						 | 
					@ -969,16 +976,19 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i vertices, const V
 | 
				
			||||||
        this->get_facets_split_by_tjoints(
 | 
					        this->get_facets_split_by_tjoints(
 | 
				
			||||||
            { vertices(i), vertices(j), midpoints(j) },
 | 
					            { vertices(i), vertices(j), midpoints(j) },
 | 
				
			||||||
            { neighbors(i),
 | 
					            { neighbors(i),
 | 
				
			||||||
              this->neighbor_child(neighbors(j), vertices(j), vertices(k), Partition::Second),
 | 
					              this->neighbor_child(neighbors(j), vertices(k), vertices(j), Partition::Second),
 | 
				
			||||||
              -1 },
 | 
					              -1 },
 | 
				
			||||||
              out_triangles);
 | 
					              out_triangles);
 | 
				
			||||||
        this->get_facets_split_by_tjoints(
 | 
					        this->get_facets_split_by_tjoints(
 | 
				
			||||||
            { midpoints(j), vertices(j), vertices(k) },
 | 
					            { midpoints(j), vertices(k), vertices(i) },
 | 
				
			||||||
            { this->neighbor_child(neighbors(j), vertices(j), vertices(k), Partition::First),
 | 
					            { this->neighbor_child(neighbors(j), vertices(k), vertices(j), Partition::First),
 | 
				
			||||||
              neighbors(k),
 | 
					              neighbors(k),
 | 
				
			||||||
              -1 },
 | 
					              -1 },
 | 
				
			||||||
              out_triangles);
 | 
					              out_triangles);
 | 
				
			||||||
    } else if (splits == 2) {
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 2:
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
        // Split to three triangles.
 | 
					        // Split to three triangles.
 | 
				
			||||||
        int i = midpoints(0) == -1 ? 2 : midpoints(1) == -1 ? 0 : 1;
 | 
					        int i = midpoints(0) == -1 ? 2 : midpoints(1) == -1 ? 0 : 1;
 | 
				
			||||||
        int j = next_idx_modulo(i, 3);
 | 
					        int j = next_idx_modulo(i, 3);
 | 
				
			||||||
| 
						 | 
					@ -1000,7 +1010,10 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i vertices, const V
 | 
				
			||||||
              this->neighbor_child(neighbors(k), vertices(i), vertices(k), Partition::Second),
 | 
					              this->neighbor_child(neighbors(k), vertices(i), vertices(k), Partition::Second),
 | 
				
			||||||
              -1 },
 | 
					              -1 },
 | 
				
			||||||
              out_triangles);
 | 
					              out_triangles);
 | 
				
			||||||
    } else if (splits == 4) {
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        assert(splits == 3);
 | 
				
			||||||
        // Split to 4 triangles.
 | 
					        // Split to 4 triangles.
 | 
				
			||||||
        this->get_facets_split_by_tjoints(
 | 
					        this->get_facets_split_by_tjoints(
 | 
				
			||||||
            { vertices(0), midpoints(0), midpoints(2) },
 | 
					            { vertices(0), midpoints(0), midpoints(2) },
 | 
				
			||||||
| 
						 | 
					@ -1021,6 +1034,7 @@ void TriangleSelector::get_facets_split_by_tjoints(const Vec3i vertices, const V
 | 
				
			||||||
              -1 },
 | 
					              -1 },
 | 
				
			||||||
              out_triangles);
 | 
					              out_triangles);
 | 
				
			||||||
        out_triangles.emplace_back(midpoints);
 | 
					        out_triangles.emplace_back(midpoints);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1106,6 +1120,13 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    reset(); // dump any current state
 | 
					    reset(); // dump any current state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Reserve number of triangles as if each triangle was saved with 4 bits.
 | 
				
			||||||
 | 
					    // With MMU painting this estimate may be somehow low, but better than nothing.
 | 
				
			||||||
 | 
					    m_triangles.reserve(std::max(m_mesh->its.indices.size(), data.second.size() / 4));
 | 
				
			||||||
 | 
					    // Number of triangles is twice the number of vertices on a large manifold mesh of genus zero.
 | 
				
			||||||
 | 
					    // Here the triangles count account for both the nodes and leaves, thus the following line may overestimate.
 | 
				
			||||||
 | 
					    m_vertices.reserve(std::max(m_mesh->its.vertices.size(), m_triangles.size() / 2));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Vector to store all parents that have offsprings.
 | 
					    // Vector to store all parents that have offsprings.
 | 
				
			||||||
    struct ProcessingInfo {
 | 
					    struct ProcessingInfo {
 | 
				
			||||||
        int facet_id = 0;
 | 
					        int facet_id = 0;
 | 
				
			||||||
| 
						 | 
					@ -1203,7 +1224,8 @@ bool TriangleSelector::has_facets(const std::pair<std::vector<std::pair<int, int
 | 
				
			||||||
    std::vector<int> parents_children;
 | 
					    std::vector<int> parents_children;
 | 
				
			||||||
    parents_children.reserve(64);
 | 
					    parents_children.reserve(64);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (auto [triangle_id, ibit] : data.first) {
 | 
					    for (const std::pair<int, int> &triangle_id_and_ibit : data.first) {
 | 
				
			||||||
 | 
					        int ibit = triangle_id_and_ibit.second;
 | 
				
			||||||
        assert(ibit < data.second.size());
 | 
					        assert(ibit < data.second.size());
 | 
				
			||||||
        auto next_nibble = [&data, &ibit = ibit]() {
 | 
					        auto next_nibble = [&data, &ibit = ibit]() {
 | 
				
			||||||
            int n = 0;
 | 
					            int n = 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,26 +39,22 @@ enum ModelInstanceEPrintVolumeState : unsigned char;
 | 
				
			||||||
// possibly indexed by triangles and / or quads.
 | 
					// possibly indexed by triangles and / or quads.
 | 
				
			||||||
class GLIndexedVertexArray {
 | 
					class GLIndexedVertexArray {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    GLIndexedVertexArray() : 
 | 
					    // Only Eigen types of Nx16 size are vectorized. This bounding box will not be vectorized.
 | 
				
			||||||
        vertices_and_normals_interleaved_VBO_id(0),
 | 
					    static_assert(sizeof(Eigen::AlignedBox<float, 3>) == 24, "Eigen::AlignedBox<float, 3> is not being vectorized, thus it does not need to be aligned");
 | 
				
			||||||
        triangle_indices_VBO_id(0),
 | 
					    using BoundingBox = Eigen::AlignedBox<float, 3>;
 | 
				
			||||||
        quad_indices_VBO_id(0)
 | 
					
 | 
				
			||||||
        {}
 | 
					    GLIndexedVertexArray() { m_bounding_box.setEmpty(); }
 | 
				
			||||||
    GLIndexedVertexArray(const GLIndexedVertexArray &rhs) :
 | 
					    GLIndexedVertexArray(const GLIndexedVertexArray &rhs) :
 | 
				
			||||||
        vertices_and_normals_interleaved(rhs.vertices_and_normals_interleaved),
 | 
					        vertices_and_normals_interleaved(rhs.vertices_and_normals_interleaved),
 | 
				
			||||||
        triangle_indices(rhs.triangle_indices),
 | 
					        triangle_indices(rhs.triangle_indices),
 | 
				
			||||||
        quad_indices(rhs.quad_indices),
 | 
					        quad_indices(rhs.quad_indices),
 | 
				
			||||||
        vertices_and_normals_interleaved_VBO_id(0),
 | 
					        m_bounding_box(rhs.m_bounding_box)
 | 
				
			||||||
        triangle_indices_VBO_id(0),
 | 
					        { assert(! rhs.has_VBOs()); m_bounding_box.setEmpty(); }
 | 
				
			||||||
        quad_indices_VBO_id(0)
 | 
					 | 
				
			||||||
        { assert(! rhs.has_VBOs()); }
 | 
					 | 
				
			||||||
    GLIndexedVertexArray(GLIndexedVertexArray &&rhs) :
 | 
					    GLIndexedVertexArray(GLIndexedVertexArray &&rhs) :
 | 
				
			||||||
        vertices_and_normals_interleaved(std::move(rhs.vertices_and_normals_interleaved)),
 | 
					        vertices_and_normals_interleaved(std::move(rhs.vertices_and_normals_interleaved)),
 | 
				
			||||||
        triangle_indices(std::move(rhs.triangle_indices)),
 | 
					        triangle_indices(std::move(rhs.triangle_indices)),
 | 
				
			||||||
        quad_indices(std::move(rhs.quad_indices)),
 | 
					        quad_indices(std::move(rhs.quad_indices)),
 | 
				
			||||||
        vertices_and_normals_interleaved_VBO_id(0),
 | 
					        m_bounding_box(rhs.m_bounding_box)
 | 
				
			||||||
        triangle_indices_VBO_id(0),
 | 
					 | 
				
			||||||
        quad_indices_VBO_id(0)
 | 
					 | 
				
			||||||
        { assert(! rhs.has_VBOs()); }
 | 
					        { assert(! rhs.has_VBOs()); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ~GLIndexedVertexArray() { release_geometry(); }
 | 
					    ~GLIndexedVertexArray() { release_geometry(); }
 | 
				
			||||||
| 
						 | 
					@ -92,7 +88,7 @@ public:
 | 
				
			||||||
        this->vertices_and_normals_interleaved 		 = std::move(rhs.vertices_and_normals_interleaved);
 | 
					        this->vertices_and_normals_interleaved 		 = std::move(rhs.vertices_and_normals_interleaved);
 | 
				
			||||||
        this->triangle_indices                 		 = std::move(rhs.triangle_indices);
 | 
					        this->triangle_indices                 		 = std::move(rhs.triangle_indices);
 | 
				
			||||||
        this->quad_indices                     		 = std::move(rhs.quad_indices);
 | 
					        this->quad_indices                     		 = std::move(rhs.quad_indices);
 | 
				
			||||||
        this->m_bounding_box                   		 = std::move(rhs.m_bounding_box);
 | 
					        this->m_bounding_box                   		 = rhs.m_bounding_box;
 | 
				
			||||||
        this->vertices_and_normals_interleaved_size  = rhs.vertices_and_normals_interleaved_size;
 | 
					        this->vertices_and_normals_interleaved_size  = rhs.vertices_and_normals_interleaved_size;
 | 
				
			||||||
        this->triangle_indices_size                  = rhs.triangle_indices_size;
 | 
					        this->triangle_indices_size                  = rhs.triangle_indices_size;
 | 
				
			||||||
        this->quad_indices_size                      = rhs.quad_indices_size;
 | 
					        this->quad_indices_size                      = rhs.quad_indices_size;
 | 
				
			||||||
| 
						 | 
					@ -147,7 +143,7 @@ public:
 | 
				
			||||||
        this->vertices_and_normals_interleaved.emplace_back(z);
 | 
					        this->vertices_and_normals_interleaved.emplace_back(z);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size();
 | 
					        this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size();
 | 
				
			||||||
        m_bounding_box.merge(Vec3f(x, y, z).cast<double>());
 | 
					        m_bounding_box.extend(Vec3f(x, y, z));
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    inline void push_geometry(double x, double y, double z, double nx, double ny, double nz) {
 | 
					    inline void push_geometry(double x, double y, double z, double nx, double ny, double nz) {
 | 
				
			||||||
| 
						 | 
					@ -203,10 +199,10 @@ public:
 | 
				
			||||||
        this->vertices_and_normals_interleaved.clear();
 | 
					        this->vertices_and_normals_interleaved.clear();
 | 
				
			||||||
        this->triangle_indices.clear();
 | 
					        this->triangle_indices.clear();
 | 
				
			||||||
        this->quad_indices.clear();
 | 
					        this->quad_indices.clear();
 | 
				
			||||||
        this->m_bounding_box.reset();
 | 
					 | 
				
			||||||
        vertices_and_normals_interleaved_size = 0;
 | 
					        vertices_and_normals_interleaved_size = 0;
 | 
				
			||||||
        triangle_indices_size = 0;
 | 
					        triangle_indices_size = 0;
 | 
				
			||||||
        quad_indices_size = 0;
 | 
					        quad_indices_size = 0;
 | 
				
			||||||
 | 
					        m_bounding_box.setEmpty();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Shrink the internal storage to tighly fit the data stored.
 | 
					    // Shrink the internal storage to tighly fit the data stored.
 | 
				
			||||||
| 
						 | 
					@ -216,7 +212,7 @@ public:
 | 
				
			||||||
        this->quad_indices.shrink_to_fit();
 | 
					        this->quad_indices.shrink_to_fit();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const BoundingBoxf3& bounding_box() const { return m_bounding_box; }
 | 
					    const BoundingBox& bounding_box() const { return m_bounding_box; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Return an estimate of the memory consumed by this class.
 | 
					    // Return an estimate of the memory consumed by this class.
 | 
				
			||||||
    size_t cpu_memory_used() const { return sizeof(*this) + vertices_and_normals_interleaved.capacity() * sizeof(float) + triangle_indices.capacity() * sizeof(int) + quad_indices.capacity() * sizeof(int); }
 | 
					    size_t cpu_memory_used() const { return sizeof(*this) + vertices_and_normals_interleaved.capacity() * sizeof(float) + triangle_indices.capacity() * sizeof(int) + quad_indices.capacity() * sizeof(int); }
 | 
				
			||||||
| 
						 | 
					@ -235,7 +231,7 @@ public:
 | 
				
			||||||
    size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
 | 
					    size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    BoundingBoxf3 m_bounding_box;
 | 
					    BoundingBox m_bounding_box;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class GLVolume {
 | 
					class GLVolume {
 | 
				
			||||||
| 
						 | 
					@ -355,7 +351,15 @@ public:
 | 
				
			||||||
    std::vector<size_t>         offsets;
 | 
					    std::vector<size_t>         offsets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Bounding box of this volume, in unscaled coordinates.
 | 
					    // Bounding box of this volume, in unscaled coordinates.
 | 
				
			||||||
    const BoundingBoxf3& bounding_box() const { return this->indexed_vertex_array.bounding_box(); }
 | 
					    BoundingBoxf3 bounding_box() const { 
 | 
				
			||||||
 | 
					        BoundingBoxf3 out;
 | 
				
			||||||
 | 
					        if (! this->indexed_vertex_array.bounding_box().isEmpty()) {
 | 
				
			||||||
 | 
					            out.min = this->indexed_vertex_array.bounding_box().min().cast<double>();
 | 
				
			||||||
 | 
					            out.max = this->indexed_vertex_array.bounding_box().max().cast<double>();
 | 
				
			||||||
 | 
					            out.defined = true;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        return out;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void set_render_color(float r, float g, float b, float a);
 | 
					    void set_render_color(float r, float g, float b, float a);
 | 
				
			||||||
    void set_render_color(const float* rgba, unsigned int size);
 | 
					    void set_render_color(const float* rgba, unsigned int size);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <boost/algorithm/string.hpp>
 | 
					#include <boost/algorithm/string.hpp>
 | 
				
			||||||
#include <boost/log/trivial.hpp>
 | 
					#include <boost/log/trivial.hpp>
 | 
				
			||||||
 | 
					#include <boost/filesystem.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <wx/sizer.h>
 | 
					#include <wx/sizer.h>
 | 
				
			||||||
#include <wx/stattext.h>
 | 
					#include <wx/stattext.h>
 | 
				
			||||||
| 
						 | 
					@ -14,6 +15,7 @@
 | 
				
			||||||
#include <wx/statbox.h>
 | 
					#include <wx/statbox.h>
 | 
				
			||||||
#include <wx/wupdlock.h>
 | 
					#include <wx/wupdlock.h>
 | 
				
			||||||
#include <wx/notebook.h>
 | 
					#include <wx/notebook.h>
 | 
				
			||||||
 | 
					#include <wx/listctrl.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "GUI.hpp"
 | 
					#include "GUI.hpp"
 | 
				
			||||||
#include "GUI_App.hpp"
 | 
					#include "GUI_App.hpp"
 | 
				
			||||||
| 
						 | 
					@ -148,32 +150,32 @@ void GalleryDialog::on_dpi_changed(const wxRect& suggested_rect)
 | 
				
			||||||
    Refresh();
 | 
					    Refresh();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void add_border(wxImage& image) 
 | 
					//static void add_border(wxImage& image)
 | 
				
			||||||
{
 | 
					//{
 | 
				
			||||||
    const wxColour& clr = wxGetApp().get_color_hovered_btn_label();
 | 
					//    const wxColour& clr = wxGetApp().get_color_hovered_btn_label();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto px_data = (uint8_t*)image.GetData();
 | 
					//    auto px_data = (uint8_t*)image.GetData();
 | 
				
			||||||
    auto a_data = (uint8_t*)image.GetAlpha();
 | 
					//    auto a_data = (uint8_t*)image.GetAlpha();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int width = image.GetWidth();
 | 
					//    int width = image.GetWidth();
 | 
				
			||||||
    int height = image.GetHeight();
 | 
					//    int height = image.GetHeight();
 | 
				
			||||||
    int border_width = 1;
 | 
					//    int border_width = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (size_t x = 0; x < width; ++x) {
 | 
					//    for (size_t x = 0; x < width; ++x) {
 | 
				
			||||||
        for (size_t y = 0; y < height; ++y) {
 | 
					//        for (size_t y = 0; y < height; ++y) {
 | 
				
			||||||
            if (x < border_width || y < border_width ||
 | 
					//            if (x < border_width || y < border_width ||
 | 
				
			||||||
                x >= (width - border_width) || y >= (height - border_width)) {
 | 
					//                x >= (width - border_width) || y >= (height - border_width)) {
 | 
				
			||||||
                const size_t idx = (x + y * width);
 | 
					//                const size_t idx = (x + y * width);
 | 
				
			||||||
                const size_t idx_rgb = (x + y * width) * 3;
 | 
					//                const size_t idx_rgb = (x + y * width) * 3;
 | 
				
			||||||
                px_data[idx_rgb] = clr.Red();
 | 
					//                px_data[idx_rgb] = clr.Red();
 | 
				
			||||||
                px_data[idx_rgb + 1] = clr.Green();
 | 
					//                px_data[idx_rgb + 1] = clr.Green();
 | 
				
			||||||
                px_data[idx_rgb + 2] = clr.Blue();
 | 
					//                px_data[idx_rgb + 2] = clr.Blue();
 | 
				
			||||||
                if (a_data)
 | 
					//                if (a_data)
 | 
				
			||||||
                    a_data[idx] = 255u;
 | 
					//                    a_data[idx] = 255u;
 | 
				
			||||||
            }
 | 
					//            }
 | 
				
			||||||
        }
 | 
					//        }
 | 
				
			||||||
    }
 | 
					//    }
 | 
				
			||||||
}
 | 
					//}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void add_lock(wxImage& image) 
 | 
					static void add_lock(wxImage& image) 
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class wxListCtrl;
 | 
					class wxListCtrl;
 | 
				
			||||||
class wxImageList;
 | 
					class wxImageList;
 | 
				
			||||||
 | 
					class wxListEvent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Slic3r {
 | 
					namespace Slic3r {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -170,6 +170,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
 | 
				
			||||||
            if (mv->is_model_part()) {
 | 
					            if (mv->is_model_part()) {
 | 
				
			||||||
                ++idx;
 | 
					                ++idx;
 | 
				
			||||||
                m_triangle_selectors[idx]->reset();
 | 
					                m_triangle_selectors[idx]->reset();
 | 
				
			||||||
 | 
					                m_triangle_selectors[idx]->request_update_render_data();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -285,13 +286,12 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Now calculate dot product of vert_direction and facets' normals.
 | 
					        // Now calculate dot product of vert_direction and facets' normals.
 | 
				
			||||||
        int idx = -1;
 | 
					        int idx = -1;
 | 
				
			||||||
        for (const stl_facet& facet : mv->mesh().stl.facet_start) {
 | 
					        for (const stl_facet &facet : mv->mesh().stl.facet_start) {
 | 
				
			||||||
            ++idx;
 | 
					            ++idx;
 | 
				
			||||||
            if (facet.normal.dot(down) > dot_limit)
 | 
					            if (facet.normal.dot(down) > dot_limit) {
 | 
				
			||||||
                m_triangle_selectors[mesh_id]->set_facet(idx,
 | 
					                m_triangle_selectors[mesh_id]->set_facet(idx, block ? EnforcerBlockerType::BLOCKER : EnforcerBlockerType::ENFORCER);
 | 
				
			||||||
                                                         block
 | 
					                m_triangle_selectors.back()->request_update_render_data();
 | 
				
			||||||
                                                         ? EnforcerBlockerType::BLOCKER
 | 
					            }
 | 
				
			||||||
                                                         : EnforcerBlockerType::ENFORCER);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -346,6 +346,7 @@ void GLGizmoFdmSupports::update_from_model_object()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        m_triangle_selectors.emplace_back(std::make_unique<TriangleSelectorGUI>(*mesh));
 | 
					        m_triangle_selectors.emplace_back(std::make_unique<TriangleSelectorGUI>(*mesh));
 | 
				
			||||||
        m_triangle_selectors.back()->deserialize(mv->supported_facets.get_data());
 | 
					        m_triangle_selectors.back()->deserialize(mv->supported_facets.get_data());
 | 
				
			||||||
 | 
					        m_triangle_selectors.back()->request_update_render_data();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -296,8 +296,10 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (m_imgui->checkbox(_L("Seed fill"), m_seed_fill_enabled))
 | 
					    if (m_imgui->checkbox(_L("Seed fill"), m_seed_fill_enabled))
 | 
				
			||||||
        if (!m_seed_fill_enabled)
 | 
					        if (!m_seed_fill_enabled)
 | 
				
			||||||
            for (auto &triangle_selector : m_triangle_selectors)
 | 
					            for (auto &triangle_selector : m_triangle_selectors) {
 | 
				
			||||||
                triangle_selector->seed_fill_unselect_all_triangles();
 | 
					                triangle_selector->seed_fill_unselect_all_triangles();
 | 
				
			||||||
 | 
					                triangle_selector->request_update_render_data();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    m_imgui->text(m_desc["seed_fill_angle"] + ":");
 | 
					    m_imgui->text(m_desc["seed_fill_angle"] + ":");
 | 
				
			||||||
    ImGui::AlignTextToFramePadding();
 | 
					    ImGui::AlignTextToFramePadding();
 | 
				
			||||||
| 
						 | 
					@ -319,6 +321,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
 | 
				
			||||||
            if (mv->is_model_part()) {
 | 
					            if (mv->is_model_part()) {
 | 
				
			||||||
                ++idx;
 | 
					                ++idx;
 | 
				
			||||||
                m_triangle_selectors[idx]->reset();
 | 
					                m_triangle_selectors[idx]->reset();
 | 
				
			||||||
 | 
					                m_triangle_selectors[idx]->request_update_render_data();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -437,8 +440,9 @@ void GLGizmoMmuSegmentation::init_model_triangle_selectors()
 | 
				
			||||||
        const TriangleMesh *mesh = &mv->mesh();
 | 
					        const TriangleMesh *mesh = &mv->mesh();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        int extruder_idx = (mv->extruder_id() > 0) ? mv->extruder_id() - 1 : 0;
 | 
					        int extruder_idx = (mv->extruder_id() > 0) ? mv->extruder_id() - 1 : 0;
 | 
				
			||||||
        m_triangle_selectors.emplace_back(std::make_unique<TriangleSelectorMmuGui>(*mesh, m_modified_extruders_colors, m_original_extruders_colors[size_t(extruder_idx)]));
 | 
					        m_triangle_selectors.emplace_back(std::make_unique<TriangleSelectorMmGui>(*mesh, m_modified_extruders_colors, m_original_extruders_colors[size_t(extruder_idx)]));
 | 
				
			||||||
        m_triangle_selectors.back()->deserialize(mv->mmu_segmentation_facets.get_data());
 | 
					        m_triangle_selectors.back()->deserialize(mv->mmu_segmentation_facets.get_data());
 | 
				
			||||||
 | 
					        m_triangle_selectors.back()->request_update_render_data();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    m_original_volumes_extruder_idxs = get_extruder_id_for_volumes(*mo);
 | 
					    m_original_volumes_extruder_idxs = get_extruder_id_for_volumes(*mo);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -466,56 +470,60 @@ std::array<float, 4> GLGizmoMmuSegmentation::get_cursor_sphere_right_button_colo
 | 
				
			||||||
    return {color[0], color[1], color[2], 0.25f};
 | 
					    return {color[0], color[1], color[2], 0.25f};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void TriangleSelectorMmuGui::render(ImGuiWrapper *imgui)
 | 
					void TriangleSelectorMmGui::render(ImGuiWrapper *imgui)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    static constexpr std::array<float, 4> seed_fill_color{0.f, 1.f, 0.44f, 1.f};
 | 
					    static constexpr std::array<float, 4> seed_fill_color{0.f, 1.f, 0.44f, 1.f};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::vector<int> color_cnt(m_iva_colors.size());
 | 
					    if (m_update_render_data)
 | 
				
			||||||
    int              seed_fill_cnt = 0;
 | 
					        update_render_data();
 | 
				
			||||||
    for (auto &iva_color : m_iva_colors)
 | 
					 | 
				
			||||||
        iva_color.release_geometry();
 | 
					 | 
				
			||||||
    m_iva_seed_fill.release_geometry();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    auto append_triangle = [this](GLIndexedVertexArray &iva, int &cnt, const Triangle &tr) -> void {
 | 
					 | 
				
			||||||
        for (int i = 0; i < 3; ++i)
 | 
					 | 
				
			||||||
            iva.push_geometry(m_vertices[tr.verts_idxs[i]].v, m_mesh->stl.facet_start[tr.source_triangle].normal);
 | 
					 | 
				
			||||||
        iva.push_triangle(cnt, cnt + 1, cnt + 2);
 | 
					 | 
				
			||||||
        cnt += 3;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (size_t color_idx = 0; color_idx < m_iva_colors.size(); ++color_idx) {
 | 
					 | 
				
			||||||
        for (const Triangle &tr : m_triangles) {
 | 
					 | 
				
			||||||
            if (!tr.valid() || tr.is_split() || tr.is_selected_by_seed_fill() || tr.get_state() != EnforcerBlockerType(color_idx))
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            append_triangle(m_iva_colors[color_idx], color_cnt[color_idx], tr);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (const Triangle &tr : m_triangles) {
 | 
					 | 
				
			||||||
        if (!tr.valid() || tr.is_split() || !tr.is_selected_by_seed_fill())
 | 
					 | 
				
			||||||
            continue;
 | 
					 | 
				
			||||||
        append_triangle(m_iva_seed_fill, seed_fill_cnt, tr);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (auto &iva_color : m_iva_colors)
 | 
					 | 
				
			||||||
        iva_color.finalize_geometry(true);
 | 
					 | 
				
			||||||
    m_iva_seed_fill.finalize_geometry(true);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto *shader = wxGetApp().get_current_shader();
 | 
					    auto *shader = wxGetApp().get_current_shader();
 | 
				
			||||||
    if (!shader)
 | 
					    if (!shader)
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    assert(shader->get_name() == "gouraud");
 | 
					    assert(shader->get_name() == "gouraud");
 | 
				
			||||||
 | 
					    ScopeGuard guard([shader]() { if (shader) shader->set_uniform("compute_triangle_normals_in_fs", false);});
 | 
				
			||||||
 | 
					    shader->set_uniform("compute_triangle_normals_in_fs", true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto render = [&shader](const GLIndexedVertexArray &iva, const std::array<float, 4> &color) -> void {
 | 
					    for (size_t color_idx = 0; color_idx < m_gizmo_scene.triangle_indices.size(); ++color_idx)
 | 
				
			||||||
        if (iva.has_VBOs()) {
 | 
					        if (m_gizmo_scene.has_VBOs(color_idx)) {
 | 
				
			||||||
            shader->set_uniform("uniform_color", color);
 | 
					            shader->set_uniform("uniform_color", color_idx == 0                                           ? m_default_volume_color :
 | 
				
			||||||
            iva.render();
 | 
					                                                            color_idx == (m_gizmo_scene.triangle_indices.size() - 1) ? seed_fill_color :
 | 
				
			||||||
 | 
					                                                                                                                       m_colors[color_idx - 1]);
 | 
				
			||||||
 | 
					            m_gizmo_scene.render(color_idx);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (size_t color_idx = 0; color_idx < m_iva_colors.size(); ++color_idx)
 | 
					    m_update_render_data = false;
 | 
				
			||||||
        render(m_iva_colors[color_idx], (color_idx == 0) ? m_default_volume_color : m_colors[color_idx - 1]);
 | 
					}
 | 
				
			||||||
    render(m_iva_seed_fill, seed_fill_color);
 | 
					
 | 
				
			||||||
 | 
					void TriangleSelectorMmGui::update_render_data()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    m_gizmo_scene.release_geometry();
 | 
				
			||||||
 | 
					    m_vertices.reserve(m_vertices.size() * 3);
 | 
				
			||||||
 | 
					    for (const Vertex &vr : m_vertices) {
 | 
				
			||||||
 | 
					        m_gizmo_scene.vertices.emplace_back(vr.v.x());
 | 
				
			||||||
 | 
					        m_gizmo_scene.vertices.emplace_back(vr.v.y());
 | 
				
			||||||
 | 
					        m_gizmo_scene.vertices.emplace_back(vr.v.z());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    m_gizmo_scene.finalize_vertices();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const Triangle &tr : m_triangles)
 | 
				
			||||||
 | 
					        if (tr.valid() && !tr.is_split()) {
 | 
				
			||||||
 | 
					            int               color = int(tr.get_state());
 | 
				
			||||||
 | 
					            std::vector<int> &iva   = tr.is_selected_by_seed_fill()                          ? m_gizmo_scene.triangle_indices.back() :
 | 
				
			||||||
 | 
					                                      color < int(m_gizmo_scene.triangle_indices.size() - 1) ? m_gizmo_scene.triangle_indices[color] :
 | 
				
			||||||
 | 
					                                                                                               m_gizmo_scene.triangle_indices.front();
 | 
				
			||||||
 | 
					            if (iva.size() + 3 > iva.capacity())
 | 
				
			||||||
 | 
					                iva.reserve(next_highest_power_of_2(iva.size() + 3));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            iva.emplace_back(tr.verts_idxs[0]);
 | 
				
			||||||
 | 
					            iva.emplace_back(tr.verts_idxs[1]);
 | 
				
			||||||
 | 
					            iva.emplace_back(tr.verts_idxs[2]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (size_t color_idx = 0; color_idx < m_gizmo_scene.triangle_indices.size(); ++color_idx)
 | 
				
			||||||
 | 
					        m_gizmo_scene.triangle_indices_sizes[color_idx] = m_gizmo_scene.triangle_indices[color_idx].size();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_gizmo_scene.finalize_triangle_indices();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wxString GLGizmoMmuSegmentation::handle_snapshot_action_name(bool shift_down, GLGizmoPainterBase::Button button_down) const
 | 
					wxString GLGizmoMmuSegmentation::handle_snapshot_action_name(bool shift_down, GLGizmoPainterBase::Button button_down) const
 | 
				
			||||||
| 
						 | 
					@ -530,4 +538,77 @@ wxString GLGizmoMmuSegmentation::handle_snapshot_action_name(bool shift_down, GL
 | 
				
			||||||
    return action_name;
 | 
					    return action_name;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GLMmSegmentationGizmo3DScene::release_geometry() {
 | 
				
			||||||
 | 
					    if (this->vertices_VBO_id) {
 | 
				
			||||||
 | 
					        glsafe(::glDeleteBuffers(1, &this->vertices_VBO_id));
 | 
				
			||||||
 | 
					        this->vertices_VBO_id = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for(auto &triangle_indices_VBO_id : triangle_indices_VBO_ids) {
 | 
				
			||||||
 | 
					        glsafe(::glDeleteBuffers(1, &triangle_indices_VBO_id));
 | 
				
			||||||
 | 
					        triangle_indices_VBO_id = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this->clear();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GLMmSegmentationGizmo3DScene::render(size_t triangle_indices_idx) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    assert(triangle_indices_idx < this->triangle_indices_VBO_ids.size());
 | 
				
			||||||
 | 
					    assert(this->triangle_indices_sizes.size() == this->triangle_indices_VBO_ids.size());
 | 
				
			||||||
 | 
					    assert(this->vertices_VBO_id != 0);
 | 
				
			||||||
 | 
					    assert(this->triangle_indices_VBO_ids[triangle_indices_idx] != 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_VBO_id));
 | 
				
			||||||
 | 
					    glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), (const void*)(0 * sizeof(float))));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Render using the Vertex Buffer Objects.
 | 
				
			||||||
 | 
					    if (this->triangle_indices_sizes[triangle_indices_idx] > 0) {
 | 
				
			||||||
 | 
					        glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_ids[triangle_indices_idx]));
 | 
				
			||||||
 | 
					        glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_sizes[triangle_indices_idx]), GL_UNSIGNED_INT, nullptr));
 | 
				
			||||||
 | 
					        glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GLMmSegmentationGizmo3DScene::finalize_vertices()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    assert(this->vertices_VBO_id == 0);
 | 
				
			||||||
 | 
					    if (!this->vertices.empty()) {
 | 
				
			||||||
 | 
					        glsafe(::glGenBuffers(1, &this->vertices_VBO_id));
 | 
				
			||||||
 | 
					        glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_VBO_id));
 | 
				
			||||||
 | 
					        glsafe(::glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * 4, this->vertices.data(), GL_STATIC_DRAW));
 | 
				
			||||||
 | 
					        glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
 | 
				
			||||||
 | 
					        this->vertices.clear();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GLMmSegmentationGizmo3DScene::finalize_triangle_indices()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    assert(triangle_indices_idx < this->triangle_indices.size());
 | 
				
			||||||
 | 
					    assert(std::all_of(triangle_indices_VBO_ids.cbegin(), triangle_indices_VBO_ids.cend(), [](const auto &ti_VBO_id) { return ti_VBO_id == 0; }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert(this->triangle_indices.size() == this->triangle_indices_VBO_ids.size());
 | 
				
			||||||
 | 
					    for (size_t buffer_idx = 0; buffer_idx < this->triangle_indices.size(); ++buffer_idx)
 | 
				
			||||||
 | 
					        if (!this->triangle_indices[buffer_idx].empty()) {
 | 
				
			||||||
 | 
					            glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_ids[buffer_idx]));
 | 
				
			||||||
 | 
					            glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_ids[buffer_idx]));
 | 
				
			||||||
 | 
					            glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices[buffer_idx].size() * 4, this->triangle_indices[buffer_idx].data(),
 | 
				
			||||||
 | 
					                                  GL_STATIC_DRAW));
 | 
				
			||||||
 | 
					            glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
 | 
				
			||||||
 | 
					            this->triangle_indices[buffer_idx].clear();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GLMmSegmentationGizmo3DScene::finalize_geometry()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    assert(this->vertices_VBO_id == 0);
 | 
				
			||||||
 | 
					    assert(this->triangle_indices.size() == this->triangle_indices_VBO_ids.size());
 | 
				
			||||||
 | 
					    finalize_vertices();
 | 
				
			||||||
 | 
					    finalize_triangle_indices();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace Slic3r
 | 
					} // namespace Slic3r
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,24 +5,80 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Slic3r::GUI {
 | 
					namespace Slic3r::GUI {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TriangleSelectorMmuGui : public TriangleSelectorGUI {
 | 
					class GLMmSegmentationGizmo3DScene
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    explicit TriangleSelectorMmuGui(const TriangleMesh& mesh, const std::vector<std::array<float, 4>> &colors, const std::array<float, 4> &default_volume_color)
 | 
					    GLMmSegmentationGizmo3DScene() = delete;
 | 
				
			||||||
        : TriangleSelectorGUI(mesh), m_colors(colors), m_default_volume_color(default_volume_color) {
 | 
					
 | 
				
			||||||
        // Plus 1 is because the first position is allocated for non-painted triangles.
 | 
					    explicit GLMmSegmentationGizmo3DScene(size_t triangle_indices_buffers_count)
 | 
				
			||||||
        m_iva_colors = std::vector<GLIndexedVertexArray>(colors.size() + 1);
 | 
					    {
 | 
				
			||||||
 | 
					        this->triangle_indices         = std::vector<std::vector<int>>(triangle_indices_buffers_count);
 | 
				
			||||||
 | 
					        this->triangle_indices_sizes   = std::vector<size_t>(triangle_indices_buffers_count);
 | 
				
			||||||
 | 
					        this->triangle_indices_VBO_ids = std::vector<unsigned int>(triangle_indices_buffers_count);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    ~TriangleSelectorMmuGui() override = default;
 | 
					
 | 
				
			||||||
 | 
					    virtual ~GLMmSegmentationGizmo3DScene() { release_geometry(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [[nodiscard]] inline bool has_VBOs(size_t triangle_indices_idx) const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        assert(triangle_indices_idx < this->triangle_indices.size());
 | 
				
			||||||
 | 
					        return this->triangle_indices_VBO_ids[triangle_indices_idx] != 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Finalize the initialization of the geometry and indices, upload the geometry and indices to OpenGL VBO objects
 | 
				
			||||||
 | 
					    // and possibly releasing it if it has been loaded into the VBOs.
 | 
				
			||||||
 | 
					    void finalize_geometry();
 | 
				
			||||||
 | 
					    // Release the geometry data, release OpenGL VBOs.
 | 
				
			||||||
 | 
					    void release_geometry();
 | 
				
			||||||
 | 
					    // Finalize the initialization of the geometry, upload the geometry to OpenGL VBO objects
 | 
				
			||||||
 | 
					    // and possibly releasing it if it has been loaded into the VBOs.
 | 
				
			||||||
 | 
					    void finalize_vertices();
 | 
				
			||||||
 | 
					    // Finalize the initialization of the indices, upload the indices to OpenGL VBO objects
 | 
				
			||||||
 | 
					    // and possibly releasing it if it has been loaded into the VBOs.
 | 
				
			||||||
 | 
					    void finalize_triangle_indices();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void clear()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        this->vertices.clear();
 | 
				
			||||||
 | 
					        for (std::vector<int> &ti : this->triangle_indices)
 | 
				
			||||||
 | 
					            ti.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (size_t &triangle_indices_size : this->triangle_indices_sizes)
 | 
				
			||||||
 | 
					            triangle_indices_size = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void render(size_t triangle_indices_idx) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<float>            vertices;
 | 
				
			||||||
 | 
					    std::vector<std::vector<int>> triangle_indices;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // When the triangle indices are loaded into the graphics card as Vertex Buffer Objects,
 | 
				
			||||||
 | 
					    // the above mentioned std::vectors are cleared and the following variables keep their original length.
 | 
				
			||||||
 | 
					    std::vector<size_t> triangle_indices_sizes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // IDs of the Vertex Array Objects, into which the geometry has been loaded.
 | 
				
			||||||
 | 
					    // Zero if the VBOs are not sent to GPU yet.
 | 
				
			||||||
 | 
					    unsigned int              vertices_VBO_id{0};
 | 
				
			||||||
 | 
					    std::vector<unsigned int> triangle_indices_VBO_ids;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TriangleSelectorMmGui : public TriangleSelectorGUI {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    // Plus 2 in the initialization of m_gizmo_scene is because the first position is allocated for non-painted triangles, and the last position is allocated for seed fill.
 | 
				
			||||||
 | 
					    explicit TriangleSelectorMmGui(const TriangleMesh &mesh, const std::vector<std::array<float, 4>> &colors, const std::array<float, 4> &default_volume_color)
 | 
				
			||||||
 | 
					        : TriangleSelectorGUI(mesh), m_colors(colors), m_default_volume_color(default_volume_color), m_gizmo_scene(colors.size() + 2) {}
 | 
				
			||||||
 | 
					    ~TriangleSelectorMmGui() override = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Render current selection. Transformation matrices are supposed
 | 
					    // Render current selection. Transformation matrices are supposed
 | 
				
			||||||
    // to be already set.
 | 
					    // to be already set.
 | 
				
			||||||
    void render(ImGuiWrapper* imgui) override;
 | 
					    void render(ImGuiWrapper* imgui) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					    void update_render_data();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const std::vector<std::array<float, 4>> &m_colors;
 | 
					    const std::vector<std::array<float, 4>> &m_colors;
 | 
				
			||||||
    std::vector<GLIndexedVertexArray>        m_iva_colors;
 | 
					 | 
				
			||||||
    const std::array<float, 4>               m_default_volume_color;
 | 
					    const std::array<float, 4>               m_default_volume_color;
 | 
				
			||||||
    GLIndexedVertexArray                     m_iva_seed_fill;
 | 
					    GLMmSegmentationGizmo3DScene             m_gizmo_scene;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class GLGizmoMmuSegmentation : public GLGizmoPainterBase
 | 
					class GLGizmoMmuSegmentation : public GLGizmoPainterBase
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -402,6 +402,8 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
 | 
				
			||||||
            } else
 | 
					            } else
 | 
				
			||||||
                m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, int(m_rr.facet), camera_pos, m_cursor_radius, m_cursor_type,
 | 
					                m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, int(m_rr.facet), camera_pos, m_cursor_radius, m_cursor_type,
 | 
				
			||||||
                                                                 new_state, trafo_matrix, m_triangle_splitting_enabled);
 | 
					                                                                 new_state, trafo_matrix, m_triangle_splitting_enabled);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
 | 
				
			||||||
            m_last_mouse_click = mouse_position;
 | 
					            m_last_mouse_click = mouse_position;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -428,8 +430,10 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
 | 
				
			||||||
        update_raycast_cache(mouse_position, camera, trafo_matrices);
 | 
					        update_raycast_cache(mouse_position, camera, trafo_matrices);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        auto seed_fill_unselect_all = [this]() {
 | 
					        auto seed_fill_unselect_all = [this]() {
 | 
				
			||||||
            for (auto &triangle_selector : m_triangle_selectors)
 | 
					            for (auto &triangle_selector : m_triangle_selectors) {
 | 
				
			||||||
                triangle_selector->seed_fill_unselect_all_triangles();
 | 
					                triangle_selector->seed_fill_unselect_all_triangles();
 | 
				
			||||||
 | 
					                triangle_selector->request_update_render_data();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (m_rr.mesh_id == -1) {
 | 
					        if (m_rr.mesh_id == -1) {
 | 
				
			||||||
| 
						 | 
					@ -447,6 +451,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
 | 
					        assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
 | 
				
			||||||
        m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), m_seed_fill_angle);
 | 
					        m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), m_seed_fill_angle);
 | 
				
			||||||
 | 
					        m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
 | 
				
			||||||
        m_seed_fill_last_mesh_id = m_rr.mesh_id;
 | 
					        m_seed_fill_last_mesh_id = m_rr.mesh_id;
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -589,28 +594,11 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui)
 | 
				
			||||||
    static constexpr std::array<float, 4> enforcers_color{0.47f, 0.47f, 1.f, 1.f};
 | 
					    static constexpr std::array<float, 4> enforcers_color{0.47f, 0.47f, 1.f, 1.f};
 | 
				
			||||||
    static constexpr std::array<float, 4> blockers_color{1.f, 0.44f, 0.44f, 1.f};
 | 
					    static constexpr std::array<float, 4> blockers_color{1.f, 0.44f, 0.44f, 1.f};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    int enf_cnt       = 0;
 | 
					    if (m_update_render_data) {
 | 
				
			||||||
    int blc_cnt       = 0;
 | 
					        update_render_data();
 | 
				
			||||||
 | 
					        m_update_render_data = false;
 | 
				
			||||||
    for (auto *iva : {&m_iva_enforcers, &m_iva_blockers})
 | 
					 | 
				
			||||||
        iva->release_geometry();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (const Triangle& tr : m_triangles) {
 | 
					 | 
				
			||||||
        if (!tr.valid() || tr.is_split() || tr.get_state() == EnforcerBlockerType::NONE)
 | 
					 | 
				
			||||||
            continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        GLIndexedVertexArray &iva  = tr.get_state() == EnforcerBlockerType::ENFORCER ? m_iva_enforcers : m_iva_blockers;
 | 
					 | 
				
			||||||
        int &                 cnt  = tr.get_state() == EnforcerBlockerType::ENFORCER ? enf_cnt : blc_cnt;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (int i = 0; i < 3; ++i)
 | 
					 | 
				
			||||||
            iva.push_geometry(m_vertices[tr.verts_idxs[i]].v, m_mesh->stl.facet_start[tr.source_triangle].normal);
 | 
					 | 
				
			||||||
        iva.push_triangle(cnt, cnt + 1, cnt + 2);
 | 
					 | 
				
			||||||
        cnt += 3;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (auto *iva : {&m_iva_enforcers, &m_iva_blockers})
 | 
					 | 
				
			||||||
        iva->finalize_geometry(true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    auto* shader = wxGetApp().get_current_shader();
 | 
					    auto* shader = wxGetApp().get_current_shader();
 | 
				
			||||||
    if (! shader)
 | 
					    if (! shader)
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
| 
						 | 
					@ -635,6 +623,33 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TriangleSelectorGUI::update_render_data()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int enf_cnt = 0;
 | 
				
			||||||
 | 
					    int blc_cnt = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (auto *iva : {&m_iva_enforcers, &m_iva_blockers})
 | 
				
			||||||
 | 
					        iva->release_geometry();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const Triangle &tr : m_triangles) {
 | 
				
			||||||
 | 
					        if (!tr.valid() || tr.is_split() || tr.get_state() == EnforcerBlockerType::NONE)
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        GLIndexedVertexArray &iva = tr.get_state() == EnforcerBlockerType::ENFORCER ? m_iva_enforcers : m_iva_blockers;
 | 
				
			||||||
 | 
					        int &                 cnt = tr.get_state() == EnforcerBlockerType::ENFORCER ? enf_cnt : blc_cnt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int i = 0; i < 3; ++i)
 | 
				
			||||||
 | 
					            iva.push_geometry(m_vertices[tr.verts_idxs[i]].v, m_mesh->stl.facet_start[tr.source_triangle].normal);
 | 
				
			||||||
 | 
					        iva.push_triangle(cnt, cnt + 1, cnt + 2);
 | 
				
			||||||
 | 
					        cnt += 3;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (auto *iva : {&m_iva_enforcers, &m_iva_blockers})
 | 
				
			||||||
 | 
					        iva->finalize_geometry(true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG
 | 
					#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG
 | 
				
			||||||
void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui)
 | 
					void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -685,7 +700,7 @@ void TriangleSelectorGUI::render_debug(ImGuiWrapper* imgui)
 | 
				
			||||||
            va = &m_varrays[ORIGINAL];
 | 
					            va = &m_varrays[ORIGINAL];
 | 
				
			||||||
            cnt = &cnts[ORIGINAL];
 | 
					            cnt = &cnts[ORIGINAL];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else if (tr.valid) {
 | 
					        else if (tr.valid()) {
 | 
				
			||||||
            va = &m_varrays[SPLIT];
 | 
					            va = &m_varrays[SPLIT];
 | 
				
			||||||
            cnt = &cnts[SPLIT];
 | 
					            cnt = &cnts[SPLIT];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,13 +38,20 @@ public:
 | 
				
			||||||
    virtual void render(ImGuiWrapper *imgui);
 | 
					    virtual void render(ImGuiWrapper *imgui);
 | 
				
			||||||
    void         render() { this->render(nullptr); }
 | 
					    void         render() { this->render(nullptr); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void request_update_render_data() { m_update_render_data = true; };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG
 | 
					#ifdef PRUSASLICER_TRIANGLE_SELECTOR_DEBUG
 | 
				
			||||||
    void render_debug(ImGuiWrapper* imgui);
 | 
					    void render_debug(ImGuiWrapper* imgui);
 | 
				
			||||||
    bool m_show_triangles{false};
 | 
					    bool m_show_triangles{false};
 | 
				
			||||||
    bool m_show_invalid{false};
 | 
					    bool m_show_invalid{false};
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					    bool m_update_render_data = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					    void update_render_data();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    GLIndexedVertexArray                m_iva_enforcers;
 | 
					    GLIndexedVertexArray                m_iva_enforcers;
 | 
				
			||||||
    GLIndexedVertexArray                m_iva_blockers;
 | 
					    GLIndexedVertexArray                m_iva_blockers;
 | 
				
			||||||
    std::array<GLIndexedVertexArray, 3> m_varrays;
 | 
					    std::array<GLIndexedVertexArray, 3> m_varrays;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -127,6 +127,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
 | 
				
			||||||
            if (mv->is_model_part()) {
 | 
					            if (mv->is_model_part()) {
 | 
				
			||||||
                ++idx;
 | 
					                ++idx;
 | 
				
			||||||
                m_triangle_selectors[idx]->reset();
 | 
					                m_triangle_selectors[idx]->reset();
 | 
				
			||||||
 | 
					                m_triangle_selectors[idx]->request_update_render_data();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -257,6 +258,7 @@ void GLGizmoSeam::update_from_model_object()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        m_triangle_selectors.emplace_back(std::make_unique<TriangleSelectorGUI>(*mesh));
 | 
					        m_triangle_selectors.emplace_back(std::make_unique<TriangleSelectorGUI>(*mesh));
 | 
				
			||||||
        m_triangle_selectors.back()->deserialize(mv->seam_facets.get_data());
 | 
					        m_triangle_selectors.back()->deserialize(mv->seam_facets.get_data());
 | 
				
			||||||
 | 
					        m_triangle_selectors.back()->request_update_render_data();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue