mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 12:11:15 -06:00 
			
		
		
		
	Merge branch 'dev' of https://github.com/prusa3d/PrusaSlicer into et_reload_from_disk
This commit is contained in:
		
						commit
						d6c3c766aa
					
				
					 63 changed files with 1111 additions and 1487 deletions
				
			
		|  | @ -247,7 +247,7 @@ extern void its_transform(indexed_triangle_set &its, T *trafo3x4) | |||
| template<typename T> | ||||
| inline void its_transform(indexed_triangle_set &its, const Eigen::Transform<T, 3, Eigen::Affine, Eigen::DontAlign>& t) | ||||
| { | ||||
| 	const Eigen::Matrix<double, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0); | ||||
| 	//const Eigen::Matrix<double, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0);
 | ||||
| 	for (stl_vertex &v : its.vertices) | ||||
| 		v = (t * v.template cast<T>()).template cast<float>().eval(); | ||||
| } | ||||
|  |  | |||
|  | @ -13,103 +13,200 @@ | |||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| struct SurfaceGroupAttrib | ||||
| struct SurfaceFillParams | ||||
| { | ||||
|     SurfaceGroupAttrib() : is_solid(false), flow_width(0.f), pattern(-1) {} | ||||
|     bool operator==(const SurfaceGroupAttrib &other) const | ||||
|         { return is_solid == other.is_solid && flow_width == other.flow_width && pattern == other.pattern; } | ||||
|     bool    is_solid; | ||||
|     float   flow_width; | ||||
|     // pattern is of type InfillPattern, -1 for an unset pattern.
 | ||||
|     int     pattern; | ||||
| 	SurfaceFillParams() : flow(0.f, 0.f, 0.f, false) { memset(this, 0, sizeof(*this)); } | ||||
| 	// Zero based extruder ID.
 | ||||
| 	unsigned int 	extruder; | ||||
| 	// Infill pattern, adjusted for the density etc.
 | ||||
| 	InfillPattern  	pattern; | ||||
| 
 | ||||
|     // FillBase
 | ||||
|     // in unscaled coordinates
 | ||||
|     coordf_t    	spacing; | ||||
|     // infill / perimeter overlap, in unscaled coordinates
 | ||||
|     coordf_t    	overlap; | ||||
|     // Angle as provided by the region config, in radians.
 | ||||
|     float       	angle; | ||||
|     // Non-negative for a bridge.
 | ||||
|     float 			bridge_angle; | ||||
| 
 | ||||
|     // FillParams
 | ||||
|     float       	density; | ||||
|     // Don't connect the fill lines around the inner perimeter.
 | ||||
|     bool        	dont_connect; | ||||
|     // Don't adjust spacing to fill the space evenly.
 | ||||
|     bool        	dont_adjust; | ||||
| 
 | ||||
|     // width, height of extrusion, nozzle diameter, is bridge
 | ||||
|     // For the output, for fill generator.
 | ||||
| 	Flow 			flow; | ||||
| 
 | ||||
| 	// For the output
 | ||||
| 	ExtrusionRole	extrusion_role; | ||||
| 
 | ||||
| 	// Various print settings?
 | ||||
| 
 | ||||
| 	// Index of this entry in a linear vector.
 | ||||
| 	size_t 			idx; | ||||
| 
 | ||||
| 
 | ||||
| 	bool operator<(const SurfaceFillParams &rhs) const { | ||||
| #define RETURN_COMPARE_NON_EQUAL(KEY) if (this->KEY < rhs.KEY) return true; if (this->KEY > rhs.KEY) return false; | ||||
| #define RETURN_COMPARE_NON_EQUAL_TYPED(TYPE, KEY) if (TYPE(this->KEY) < TYPE(rhs.KEY)) return true; if (TYPE(this->KEY) > TYPE(rhs.KEY)) return false; | ||||
| 
 | ||||
| 		// Sort first by decreasing bridging angle, so that the bridges are processed with priority when trimming one layer by the other.
 | ||||
| 		if (this->bridge_angle > rhs.bridge_angle) return true;  | ||||
| 		if (this->bridge_angle < rhs.bridge_angle) return false; | ||||
| 
 | ||||
| 		RETURN_COMPARE_NON_EQUAL(extruder); | ||||
| 		RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, pattern); | ||||
| 		RETURN_COMPARE_NON_EQUAL(spacing); | ||||
| 		RETURN_COMPARE_NON_EQUAL(overlap); | ||||
| 		RETURN_COMPARE_NON_EQUAL(angle); | ||||
| 		RETURN_COMPARE_NON_EQUAL(density); | ||||
| 		RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_connect); | ||||
| 		RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust); | ||||
| 		RETURN_COMPARE_NON_EQUAL(flow.width); | ||||
| 		RETURN_COMPARE_NON_EQUAL(flow.height); | ||||
| 		RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter); | ||||
| 		RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, flow.bridge); | ||||
| 		RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, extrusion_role); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	bool operator==(const SurfaceFillParams &rhs) const { | ||||
| 		return  this->extruder 			== rhs.extruder 		&& | ||||
| 				this->pattern 			== rhs.pattern 			&& | ||||
| 				this->pattern 			== rhs.pattern 			&& | ||||
| 				this->spacing 			== rhs.spacing 			&& | ||||
| 				this->overlap 			== rhs.overlap 			&& | ||||
| 				this->angle   			== rhs.angle   			&& | ||||
| 				this->density   		== rhs.density   		&& | ||||
| 				this->dont_connect  	== rhs.dont_connect 	&& | ||||
| 				this->dont_adjust   	== rhs.dont_adjust 		&& | ||||
| 				this->flow 				== rhs.flow 			&& | ||||
| 				this->extrusion_role	== rhs.extrusion_role; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| // Generate infills for Slic3r::Layer::Region.
 | ||||
| // The Slic3r::Layer::Region at this point of time may contain
 | ||||
| // surfaces of various types (internal/bridge/top/bottom/solid).
 | ||||
| // The infills are generated on the groups of surfaces with a compatible type. 
 | ||||
| // Returns an array of Slic3r::ExtrusionPath::Collection objects containing the infills generaed now
 | ||||
| // and the thin fills generated by generate_perimeters().
 | ||||
| void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) | ||||
| {     | ||||
| //    Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id;
 | ||||
|      | ||||
|     double  fill_density           = layerm.region()->config().fill_density; | ||||
|     Flow    infill_flow            = layerm.flow(frInfill); | ||||
|     Flow    solid_infill_flow      = layerm.flow(frSolidInfill); | ||||
|     Flow    top_solid_infill_flow  = layerm.flow(frTopSolidInfill); | ||||
| struct SurfaceFill { | ||||
| 	SurfaceFill(const SurfaceFillParams& params) : region_id(size_t(-1)), surface(stCount, ExPolygon()), params(params) {} | ||||
| 
 | ||||
|     Surfaces surfaces; | ||||
|      | ||||
|     // merge adjacent surfaces
 | ||||
|     // in case of bridge surfaces, the ones with defined angle will be attached to the ones
 | ||||
|     // without any angle (shouldn't this logic be moved to process_external_surfaces()?)
 | ||||
|     { | ||||
|         Polygons polygons_bridged; | ||||
|         polygons_bridged.reserve(layerm.fill_surfaces.surfaces.size()); | ||||
|         for (Surfaces::iterator it = layerm.fill_surfaces.surfaces.begin(); it != layerm.fill_surfaces.surfaces.end(); ++ it) | ||||
|             if (it->bridge_angle >= 0) | ||||
|                 polygons_append(polygons_bridged, *it); | ||||
|          | ||||
|         // group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle)
 | ||||
|         // group is of type Slic3r::SurfaceCollection
 | ||||
|         //FIXME: Use some smart heuristics to merge similar surfaces to eliminate tiny regions.
 | ||||
|         std::vector<SurfacesPtr> groups; | ||||
|         layerm.fill_surfaces.group(&groups); | ||||
|          | ||||
|         // merge compatible groups (we can generate continuous infill for them)
 | ||||
|         { | ||||
|             // cache flow widths and patterns used for all solid groups
 | ||||
|             // (we'll use them for comparing compatible groups)
 | ||||
|             std::vector<SurfaceGroupAttrib> group_attrib(groups.size()); | ||||
|             for (size_t i = 0; i < groups.size(); ++ i) { | ||||
|                 // we can only merge solid non-bridge surfaces, so discard
 | ||||
|                 // non-solid surfaces
 | ||||
|                 const Surface &surface = *groups[i].front(); | ||||
|                 if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) { | ||||
|                     group_attrib[i].is_solid = true; | ||||
|                     group_attrib[i].flow_width = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width; | ||||
|                     group_attrib[i].pattern = surface.is_external() ?  | ||||
| 	size_t 				region_id; | ||||
| 	Surface 			surface; | ||||
| 	ExPolygons       	expolygons; | ||||
| 	SurfaceFillParams	params; | ||||
| }; | ||||
| 
 | ||||
| std::vector<SurfaceFill> group_fills(const Layer &layer) | ||||
| { | ||||
| 	std::vector<SurfaceFill> surface_fills; | ||||
| 
 | ||||
| 	// Fill in a map of a region & surface to SurfaceFillParams.
 | ||||
| 	std::set<SurfaceFillParams> 						set_surface_params; | ||||
| 	std::vector<std::vector<const SurfaceFillParams*>> 	region_to_surface_params(layer.regions().size(), std::vector<const SurfaceFillParams*>()); | ||||
|     SurfaceFillParams									params; | ||||
|     bool 												has_internal_voids = false; | ||||
| 	for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) { | ||||
| 		const LayerRegion  &layerm = *layer.regions()[region_id]; | ||||
| 		region_to_surface_params[region_id].assign(layerm.fill_surfaces.size(), nullptr); | ||||
| 	    for (const Surface &surface : layerm.fill_surfaces.surfaces) | ||||
| 	        if (surface.surface_type == stInternalVoid) | ||||
| 	        	has_internal_voids = true; | ||||
| 	        else { | ||||
| 		        FlowRole extrusion_role = (surface.surface_type == stTop) ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill); | ||||
| 		        bool     is_bridge 	    = layerm.layer()->id() > 0 && surface.is_bridge(); | ||||
| 		        params.extruder 	 = layerm.region()->extruder(extrusion_role); | ||||
| 		        params.pattern 		 = layerm.region()->config().fill_pattern.value; | ||||
| 		        params.density       = float(layerm.region()->config().fill_density); | ||||
| 
 | ||||
| 		        if (surface.is_solid()) { | ||||
| 		            params.density = 100.f; | ||||
| 		            params.pattern = (surface.is_external() && ! is_bridge) ?  | ||||
| 						(surface.is_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().bottom_fill_pattern.value) : | ||||
|                         ipRectilinear; | ||||
|                 } | ||||
|             } | ||||
|             // Loop through solid groups, find compatible groups and append them to this one.
 | ||||
|             for (size_t i = 0; i < groups.size(); ++ i) { | ||||
|                 if (! group_attrib[i].is_solid) | ||||
|                     continue; | ||||
|                 for (size_t j = i + 1; j < groups.size();) { | ||||
|                     if (group_attrib[i] == group_attrib[j]) { | ||||
|                         // groups are compatible, merge them
 | ||||
|                         groups[i].insert(groups[i].end(), groups[j].begin(), groups[j].end()); | ||||
|                         groups.erase(groups.begin() + j); | ||||
|                         group_attrib.erase(group_attrib.begin() + j); | ||||
|                     } else | ||||
|                          ++ j; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         // Give priority to bridges. Process the bridges in the first round, the rest of the surfaces in the 2nd round.
 | ||||
|         for (size_t round = 0; round < 2; ++ round) { | ||||
|             for (std::vector<SurfacesPtr>::iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) { | ||||
|                 const SurfacesPtr &group = *it_group; | ||||
|                 bool is_bridge = group.front()->bridge_angle >= 0; | ||||
|                 if (is_bridge != (round == 0)) | ||||
|                     continue; | ||||
|                 // Make a union of polygons defining the infiill regions of a group, use a safety offset.
 | ||||
|                 Polygons union_p = union_(to_polygons(*it_group), true); | ||||
|                 // Subtract surfaces having a defined bridge_angle from any other, use a safety offset.
 | ||||
|                 if (! polygons_bridged.empty() && ! is_bridge) | ||||
|                     union_p = diff(union_p, polygons_bridged, true); | ||||
|                 // subtract any other surface already processed
 | ||||
|                 //FIXME Vojtech: Because the bridge surfaces came first, they are subtracted twice!
 | ||||
|                 // Using group.front() as a template.
 | ||||
|                 surfaces_append(surfaces, diff_ex(union_p, to_polygons(surfaces), true), *group.front()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
| 		                ipRectilinear; | ||||
| 		        } else if (params.density <= 0) | ||||
| 		            continue; | ||||
| 
 | ||||
| 		        params.extrusion_role = | ||||
| 		            is_bridge ? | ||||
| 		                erBridgeInfill : | ||||
| 		                (surface.is_solid() ? | ||||
| 		                    ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) : | ||||
| 		                    erInternalInfill); | ||||
| 		        params.bridge_angle = float(surface.bridge_angle); | ||||
| 		        params.angle 		= float(Geometry::deg2rad(layerm.region()->config().fill_angle.value)); | ||||
| 		         | ||||
| 		        // calculate the actual flow we'll be using for this infill
 | ||||
| 		        params.flow = layerm.region()->flow( | ||||
| 		            extrusion_role, | ||||
| 		            (surface.thickness == -1) ? layerm.layer()->height : surface.thickness, // extrusion height
 | ||||
| 		            is_bridge || Fill::use_bridge_flow(params.pattern), 					// bridge flow?
 | ||||
| 		            layerm.layer()->id() == 0,          									// first layer?
 | ||||
| 		            -1,                                 									// auto width
 | ||||
| 		            *layerm.layer()->object() | ||||
| 		        ); | ||||
| 		         | ||||
| 		        // Calculate flow spacing for infill pattern generation.
 | ||||
| 		        if (! surface.is_solid() && ! is_bridge) { | ||||
| 		            // it's internal infill, so we can calculate a generic flow spacing 
 | ||||
| 		            // for all layers, for avoiding the ugly effect of
 | ||||
| 		            // misaligned infill on first layer because of different extrusion width and
 | ||||
| 		            // layer height
 | ||||
| 		            params.spacing = layerm.region()->flow( | ||||
| 			                frInfill, | ||||
| 			                layerm.layer()->object()->config().layer_height.value,  // TODO: handle infill_every_layers?
 | ||||
| 			                false,  // no bridge
 | ||||
| 			                false,  // no first layer
 | ||||
| 			                -1,     // auto width
 | ||||
| 			                *layer.object() | ||||
| 			            ).spacing(); | ||||
| 		        } else | ||||
| 		            params.spacing = params.flow.spacing(); | ||||
| 
 | ||||
| 		        auto it_params = set_surface_params.find(params); | ||||
| 		        if (it_params == set_surface_params.end()) | ||||
| 		        	it_params = set_surface_params.insert(it_params, params); | ||||
| 		        region_to_surface_params[region_id][&surface - &layerm.fill_surfaces.surfaces.front()] = &(*it_params); | ||||
| 		    } | ||||
| 	} | ||||
| 
 | ||||
| 	surface_fills.reserve(set_surface_params.size()); | ||||
| 	for (const SurfaceFillParams ¶ms : set_surface_params) { | ||||
| 		const_cast<SurfaceFillParams&>(params).idx = surface_fills.size(); | ||||
| 		surface_fills.emplace_back(params); | ||||
| 	} | ||||
| 
 | ||||
| 	for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) { | ||||
| 		const LayerRegion &layerm = *layer.regions()[region_id]; | ||||
| 	    for (const Surface &surface : layerm.fill_surfaces.surfaces) | ||||
| 	        if (surface.surface_type != stInternalVoid) { | ||||
| 	        	const SurfaceFillParams *params = region_to_surface_params[region_id][&surface - &layerm.fill_surfaces.surfaces.front()]; | ||||
| 				if (params != nullptr) { | ||||
| 	        		SurfaceFill &fill = surface_fills[params->idx]; | ||||
|                     if (fill.region_id == size_t(-1)) { | ||||
| 	        			fill.region_id = region_id; | ||||
| 	        			fill.surface = surface; | ||||
| 	        			fill.expolygons.emplace_back(std::move(fill.surface.expolygon)); | ||||
| 	        		} else | ||||
| 	        			fill.expolygons.emplace_back(surface.expolygon); | ||||
| 				} | ||||
| 	        } | ||||
| 	} | ||||
| 
 | ||||
| 	{ | ||||
| 		Polygons all_polygons; | ||||
| 		for (SurfaceFill &fill : surface_fills) | ||||
| 			if (! fill.expolygons.empty() && (fill.expolygons.size() > 1 || ! all_polygons.empty())) { | ||||
| 				Polygons polys = to_polygons(std::move(fill.expolygons)); | ||||
| 	            // Make a union of polygons, use a safety offset, subtract the preceding polygons.
 | ||||
| 			    // Bridges are processed first (see SurfaceFill::operator<())
 | ||||
| 	            fill.expolygons = all_polygons.empty() ? union_ex(polys, true) : diff_ex(polys, all_polygons, true); | ||||
| 				append(all_polygons, std::move(polys)); | ||||
| 	        } | ||||
| 	} | ||||
| 
 | ||||
|     // we need to detect any narrow surfaces that might collapse
 | ||||
|     // when adding spacing below
 | ||||
|     // such narrow surfaces are often generated in sloping walls
 | ||||
|  | @ -119,155 +216,170 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) | |||
|     // we are going to grow such regions by overlapping them with the void (if any)
 | ||||
|     // TODO: detect and investigate whether there could be narrow regions without
 | ||||
|     // any void neighbors
 | ||||
|     { | ||||
|         coord_t distance_between_surfaces = std::max( | ||||
|             std::max(infill_flow.scaled_spacing(), solid_infill_flow.scaled_spacing()), | ||||
|             top_solid_infill_flow.scaled_spacing()); | ||||
|         Polygons surfaces_polygons = to_polygons(surfaces); | ||||
|         Polygons collapsed = diff( | ||||
|             surfaces_polygons, | ||||
|             offset2(surfaces_polygons, (float)-distance_between_surfaces/2, (float)+distance_between_surfaces/2), | ||||
|             true); | ||||
|         Polygons to_subtract; | ||||
|         to_subtract.reserve(collapsed.size() + number_polygons(surfaces)); | ||||
|         for (Surfaces::const_iterator it_surface = surfaces.begin(); it_surface != surfaces.end(); ++ it_surface) | ||||
|             if (it_surface->surface_type == stInternalVoid) | ||||
|                 polygons_append(to_subtract, *it_surface); | ||||
|         polygons_append(to_subtract, collapsed); | ||||
|         surfaces_append( | ||||
|             surfaces, | ||||
|             intersection_ex( | ||||
|                 offset(collapsed, (float)distance_between_surfaces), | ||||
|                 to_subtract, | ||||
|                 true), | ||||
|             stInternalSolid); | ||||
|     if (has_internal_voids) { | ||||
|     	// Internal voids are generated only if "infill_only_where_needed" or "infill_every_layers" are active.
 | ||||
|         coord_t  distance_between_surfaces = 0; | ||||
|         Polygons surfaces_polygons; | ||||
|         Polygons voids; | ||||
| 		int      region_internal_infill = -1; | ||||
| 		int		 region_solid_infill = -1; | ||||
| 		int		 region_some_infill = -1; | ||||
|     	for (SurfaceFill &surface_fill : surface_fills) | ||||
| 			if (! surface_fill.expolygons.empty()) { | ||||
|     			distance_between_surfaces = std::max(distance_between_surfaces, surface_fill.params.flow.scaled_spacing()); | ||||
| 				append((surface_fill.surface.surface_type == stInternalVoid) ? voids : surfaces_polygons, to_polygons(surface_fill.expolygons)); | ||||
| 				if (surface_fill.surface.surface_type == stInternalSolid) | ||||
| 					region_internal_infill = (int)surface_fill.region_id; | ||||
| 				if (surface_fill.surface.is_solid()) | ||||
| 					region_solid_infill = (int)surface_fill.region_id; | ||||
| 				if (surface_fill.surface.surface_type != stInternalVoid) | ||||
| 					region_some_infill = (int)surface_fill.region_id; | ||||
| 			} | ||||
|     	if (! voids.empty() && ! surfaces_polygons.empty()) { | ||||
|     		// First clip voids by the printing polygons, as the voids were ignored by the loop above during mutual clipping.
 | ||||
|     		voids = diff(voids, surfaces_polygons); | ||||
| 	        // Corners of infill regions, which would not be filled with an extrusion path with a radius of distance_between_surfaces/2
 | ||||
| 	        Polygons collapsed = diff( | ||||
| 	            surfaces_polygons, | ||||
| 	            offset2(surfaces_polygons, (float)-distance_between_surfaces/2, (float)+distance_between_surfaces/2), | ||||
| 	            true); | ||||
| 	        //FIXME why the voids are added to collapsed here? First it is expensive, second the result may lead to some unwanted regions being
 | ||||
| 	        // added if two offsetted void regions merge.
 | ||||
| 	        // polygons_append(voids, collapsed);
 | ||||
| 	        ExPolygons extensions = intersection_ex(offset(collapsed, (float)distance_between_surfaces), voids, true); | ||||
| 	        // Now find an internal infill SurfaceFill to add these extrusions to.
 | ||||
| 	        SurfaceFill *internal_solid_fill = nullptr; | ||||
| 			unsigned int region_id = 0; | ||||
| 			if (region_internal_infill != -1) | ||||
| 				region_id = region_internal_infill; | ||||
| 			else if (region_solid_infill != -1) | ||||
| 				region_id = region_solid_infill; | ||||
| 			else if (region_some_infill != -1) | ||||
| 				region_id = region_some_infill; | ||||
| 			const LayerRegion& layerm = *layer.regions()[region_id]; | ||||
| 	        for (SurfaceFill &surface_fill : surface_fills) | ||||
| 	        	if (surface_fill.surface.surface_type == stInternalSolid && std::abs(layerm.layer()->height - surface_fill.params.flow.height) < EPSILON) { | ||||
| 	        		internal_solid_fill = &surface_fill; | ||||
| 	        		break; | ||||
| 	        	} | ||||
| 	        if (internal_solid_fill == nullptr) { | ||||
| 	        	// Produce another solid fill.
 | ||||
| 		        params.extruder 	 = layerm.region()->extruder(frSolidInfill); | ||||
| 	            params.pattern 		 = ipRectilinear; | ||||
| 	            params.density 		 = 100.f; | ||||
| 		        params.extrusion_role = erInternalInfill; | ||||
| 		        params.angle 		= float(Geometry::deg2rad(layerm.region()->config().fill_angle.value)); | ||||
| 		        // calculate the actual flow we'll be using for this infill
 | ||||
| 		        params.flow = layerm.region()->flow( | ||||
| 		            frSolidInfill, | ||||
| 		            layerm.layer()->height, 		// extrusion height
 | ||||
| 		            false, 							// bridge flow?
 | ||||
| 		            layerm.layer()->id() == 0,      // first layer?
 | ||||
| 		            -1,                             // auto width
 | ||||
| 		            *layer.object() | ||||
| 		        ); | ||||
| 		        params.spacing = params.flow.spacing();	         | ||||
| 				surface_fills.emplace_back(params); | ||||
| 				surface_fills.back().surface.surface_type = stInternalSolid; | ||||
| 				surface_fills.back().surface.thickness = layer.height; | ||||
| 				surface_fills.back().expolygons = std::move(extensions); | ||||
| 	        } else { | ||||
| 	        	append(extensions, std::move(internal_solid_fill->expolygons)); | ||||
| 	        	internal_solid_fill->expolygons = union_ex(extensions); | ||||
| 	        } | ||||
| 		} | ||||
|     } | ||||
| 
 | ||||
|     if (0) { | ||||
| //        require "Slic3r/SVG.pm";
 | ||||
| //        Slic3r::SVG::output("fill_" . $layerm->print_z . ".svg",
 | ||||
| //            expolygons      => [ map $_->expolygon, grep !$_->is_solid, @surfaces ],
 | ||||
| //            red_expolygons  => [ map $_->expolygon, grep  $_->is_solid, @surfaces ],
 | ||||
| //        );
 | ||||
|     } | ||||
| 	return surface_fills; | ||||
| } | ||||
| 
 | ||||
| // friend to Layer
 | ||||
| void Layer::make_fills() | ||||
| { | ||||
| 	for (LayerRegion *layerm : m_regions) | ||||
| 		layerm->fills.clear(); | ||||
| 
 | ||||
| 	std::vector<SurfaceFill>  surface_fills = group_fills(*this); | ||||
| 	const Slic3r::BoundingBox bbox = this->object()->bounding_box(); | ||||
| 
 | ||||
|     for (SurfaceFill &surface_fill : surface_fills) { | ||||
|         // Create the filler object.
 | ||||
|         std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(surface_fill.params.pattern)); | ||||
|         f->set_bounding_box(bbox); | ||||
|         f->layer_id = this->id(); | ||||
|         f->z 		= this->print_z; | ||||
|         f->angle 	= surface_fill.params.angle; | ||||
|         f->spacing  = surface_fill.params.spacing; | ||||
| 
 | ||||
|     for (const Surface &surface : surfaces) { | ||||
|         if (surface.surface_type == stInternalVoid) | ||||
|             continue; | ||||
|         InfillPattern  fill_pattern = layerm.region()->config().fill_pattern.value; | ||||
|         double         density      = fill_density; | ||||
|         FlowRole role = (surface.surface_type == stTop) ? frTopSolidInfill : | ||||
|             (surface.is_solid() ? frSolidInfill : frInfill); | ||||
|         bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge(); | ||||
|          | ||||
|         if (surface.is_solid()) { | ||||
|             density = 100.; | ||||
|             fill_pattern = (surface.is_external() && ! is_bridge) ?  | ||||
| 				(surface.is_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().bottom_fill_pattern.value) : | ||||
|                 ipRectilinear; | ||||
|         } else if (density <= 0) | ||||
|             continue; | ||||
|          | ||||
|         // get filler object
 | ||||
|         std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(fill_pattern)); | ||||
|         f->set_bounding_box(layerm.layer()->object()->bounding_box()); | ||||
|          | ||||
|         // calculate the actual flow we'll be using for this infill
 | ||||
|         coordf_t h = (surface.thickness == -1) ? layerm.layer()->height : surface.thickness; | ||||
|         Flow flow = layerm.region()->flow( | ||||
|             role, | ||||
|             h, | ||||
|             is_bridge || f->use_bridge_flow(),  // bridge flow?
 | ||||
|             layerm.layer()->id() == 0,          // first layer?
 | ||||
|             -1,                                 // auto width
 | ||||
|             *layerm.layer()->object() | ||||
|         ); | ||||
|          | ||||
|         // calculate flow spacing for infill pattern generation
 | ||||
|         bool using_internal_flow = false; | ||||
|         if (! surface.is_solid() && ! is_bridge) { | ||||
|             // it's internal infill, so we can calculate a generic flow spacing 
 | ||||
|             // for all layers, for avoiding the ugly effect of
 | ||||
|             // misaligned infill on first layer because of different extrusion width and
 | ||||
|             // layer height
 | ||||
|             Flow internal_flow = layerm.region()->flow( | ||||
|                 frInfill, | ||||
|                 layerm.layer()->object()->config().layer_height.value,  // TODO: handle infill_every_layers?
 | ||||
|                 false,  // no bridge
 | ||||
|                 false,  // no first layer
 | ||||
|                 -1,     // auto width
 | ||||
|                 *layerm.layer()->object() | ||||
|             ); | ||||
|             f->spacing = internal_flow.spacing(); | ||||
|             using_internal_flow = true; | ||||
|         } else { | ||||
|             f->spacing = flow.spacing(); | ||||
|         } | ||||
| 
 | ||||
|         bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge; | ||||
|         double link_max_length = 0.; | ||||
|         if (! is_bridge) { | ||||
|         if (! surface_fill.params.flow.bridge) { | ||||
| #if 0 | ||||
|             link_max_length = layerm.region()->config().get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing()); | ||||
| //            printf("flow spacing: %f,  is_external: %d, link_max_length: %lf\n", flow.spacing(), int(surface.is_external()), link_max_length);
 | ||||
| #else | ||||
|             if (density > 80.) // 80%
 | ||||
|             if (surface_fill.params.density > 80.) // 80%
 | ||||
|                 link_max_length = 3. * f->spacing; | ||||
| #endif | ||||
|         } | ||||
| 
 | ||||
|         f->layer_id = layerm.layer()->id(); | ||||
|         f->z = layerm.layer()->print_z; | ||||
|         f->angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value)); | ||||
|         // Maximum length of the perimeter segment linking two infill lines.
 | ||||
|         f->link_max_length = (coord_t)scale_(link_max_length); | ||||
|         // Used by the concentric infill pattern to clip the loops to create extrusion paths.
 | ||||
|         f->loop_clipping = coord_t(scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER); | ||||
| //        f->layer_height = h;
 | ||||
|         f->loop_clipping = coord_t(scale_(surface_fill.params.flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER); | ||||
| 
 | ||||
|         // apply half spacing using this flow's own spacing and generate infill
 | ||||
|         FillParams params; | ||||
|         params.density = float(0.01 * density); | ||||
| //        params.dont_adjust = true;
 | ||||
|         params.dont_adjust = false; | ||||
|         Polylines polylines = f->fill_surface(&surface, params); | ||||
|         if (polylines.empty()) | ||||
|             continue; | ||||
|         params.density 		= float(0.01 * surface_fill.params.density); | ||||
|         params.dont_adjust 	= surface_fill.params.dont_adjust; // false
 | ||||
| 
 | ||||
|         // calculate actual flow from spacing (which might have been adjusted by the infill
 | ||||
|         // pattern generator)
 | ||||
|         if (using_internal_flow) { | ||||
|             // if we used the internal flow we're not doing a solid infill
 | ||||
|             // so we can safely ignore the slight variation that might have
 | ||||
|             // been applied to $f->flow_spacing
 | ||||
|         } else { | ||||
|             flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, (float)h, is_bridge || f->use_bridge_flow()); | ||||
|         } | ||||
| 
 | ||||
|         // Save into layer.
 | ||||
|         auto *eec = new ExtrusionEntityCollection(); | ||||
|         out.entities.push_back(eec); | ||||
|         // Only concentric fills are not sorted.
 | ||||
|         eec->no_sort = f->no_sort(); | ||||
|         extrusion_entities_append_paths( | ||||
|             eec->entities, std::move(polylines), | ||||
|             is_bridge ? | ||||
|                 erBridgeInfill : | ||||
|                 (surface.is_solid() ? | ||||
|                     ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) : | ||||
|                     erInternalInfill), | ||||
|             flow.mm3_per_mm(), flow.width, flow.height); | ||||
|         for (ExPolygon &expoly : surface_fill.expolygons) { | ||||
|         	surface_fill.surface.expolygon = std::move(expoly); | ||||
| 	        Polylines polylines = f->fill_surface(&surface_fill.surface, params); | ||||
| 	        if (! polylines.empty()) { | ||||
| 		        // calculate actual flow from spacing (which might have been adjusted by the infill
 | ||||
| 		        // pattern generator)
 | ||||
| 		        double flow_mm3_per_mm = surface_fill.params.flow.mm3_per_mm(); | ||||
| 		        double flow_width      = surface_fill.params.flow.width; | ||||
| 		        if (using_internal_flow) { | ||||
| 		            // if we used the internal flow we're not doing a solid infill
 | ||||
| 		            // so we can safely ignore the slight variation that might have
 | ||||
| 		            // been applied to f->spacing
 | ||||
| 		        } else { | ||||
| 		            Flow new_flow = Flow::new_from_spacing(float(f->spacing), surface_fill.params.flow.nozzle_diameter, surface_fill.params.flow.height, surface_fill.params.flow.bridge); | ||||
| 		        	flow_mm3_per_mm = new_flow.mm3_per_mm(); | ||||
| 		        	flow_width      = new_flow.width; | ||||
| 		        } | ||||
| 		        // Save into layer.
 | ||||
| 		        auto *eec = new ExtrusionEntityCollection(); | ||||
| 		        m_regions[surface_fill.region_id]->fills.entities.push_back(eec); | ||||
| 		        // Only concentric fills are not sorted.
 | ||||
| 		        eec->no_sort = f->no_sort(); | ||||
| 		        extrusion_entities_append_paths( | ||||
| 		            eec->entities, std::move(polylines), | ||||
| 		            surface_fill.params.extrusion_role, | ||||
| 		            flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height); | ||||
| 		    } | ||||
| 		} | ||||
|     } | ||||
| 
 | ||||
|     // add thin fill regions
 | ||||
|     // thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection
 | ||||
|     // Unpacks the collection, creates multiple collections per path.
 | ||||
|     // The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection.
 | ||||
|     // Why the paths are unpacked?
 | ||||
|     for (const ExtrusionEntity *thin_fill : layerm.thin_fills.entities) { | ||||
|         ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection()); | ||||
|         out.entities.push_back(&collection); | ||||
|         collection.entities.push_back(thin_fill->clone()); | ||||
|     } | ||||
| 	for (LayerRegion *layerm : m_regions) | ||||
| 	    for (const ExtrusionEntity *thin_fill : layerm->thin_fills.entities) { | ||||
| 	        ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection()); | ||||
| 	        layerm->fills.entities.push_back(&collection); | ||||
| 	        collection.entities.push_back(thin_fill->clone()); | ||||
| 	    } | ||||
| 
 | ||||
| #ifndef NDEBUG | ||||
| 	for (LayerRegion *layerm : m_regions) | ||||
| 	    for (size_t i = 0; i < layerm->fills.entities.size(); ++ i) | ||||
|     	    assert(dynamic_cast<ExtrusionEntityCollection*>(layerm->fills.entities[i]) != nullptr); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -34,7 +34,7 @@ Fill* Fill::new_from_type(const InfillPattern type) | |||
|     case ipArchimedeanChords:   return new FillArchimedeanChords(); | ||||
|     case ipHilbertCurve:        return new FillHilbertCurve(); | ||||
|     case ipOctagramSpiral:      return new FillOctagramSpiral(); | ||||
|     default: throw std::invalid_argument("unknown type");; | ||||
|     default: throw std::invalid_argument("unknown type"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -45,6 +45,24 @@ Fill* Fill::new_from_type(const std::string &type) | |||
|     return (it == enum_keys_map.end()) ? nullptr : new_from_type(InfillPattern(it->second)); | ||||
| } | ||||
| 
 | ||||
| // Force initialization of the Fill::use_bridge_flow() internal static map in a thread safe fashion even on compilers
 | ||||
| // not supporting thread safe non-static data member initializers.
 | ||||
| static bool use_bridge_flow_initializer = Fill::use_bridge_flow(ipGrid); | ||||
| 
 | ||||
| bool Fill::use_bridge_flow(const InfillPattern type) | ||||
| { | ||||
| 	static std::vector<unsigned char> cached; | ||||
| 	if (cached.empty()) { | ||||
| 		cached.assign(size_t(ipCount), 0); | ||||
| 		for (size_t i = 0; i < cached.size(); ++ i) { | ||||
| 			auto *fill = Fill::new_from_type((InfillPattern)i); | ||||
| 			cached[i] = fill->use_bridge_flow(); | ||||
| 			delete fill; | ||||
| 		} | ||||
| 	} | ||||
| 	return cached[type] != 0; | ||||
| } | ||||
| 
 | ||||
| Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms) | ||||
| { | ||||
|     // Perform offset.
 | ||||
|  |  | |||
|  | @ -70,6 +70,7 @@ public: | |||
| 
 | ||||
|     static Fill* new_from_type(const InfillPattern type); | ||||
|     static Fill* new_from_type(const std::string &type); | ||||
|     static bool  use_bridge_flow(const InfillPattern type); | ||||
| 
 | ||||
|     void         set_bounding_box(const Slic3r::BoundingBox &bbox) { bounding_box = bbox; } | ||||
| 
 | ||||
|  |  | |||
|  | @ -56,6 +56,8 @@ public: | |||
|     // Enable some perimeter squish (see INSET_OVERLAP_TOLERANCE).
 | ||||
|     // Here an overlap of 0.2x external perimeter spacing is allowed for by the elephant foot compensation.
 | ||||
|     coord_t scaled_elephant_foot_spacing() const { return coord_t(0.5f * float(this->scaled_width() + 0.6f * this->scaled_spacing())); } | ||||
| 
 | ||||
|     bool operator==(const Flow &rhs) const { return this->width == rhs.width && this->height == rhs.height && this->nozzle_diameter == rhs.nozzle_diameter && this->bridge == rhs.bridge; } | ||||
|      | ||||
|     static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio); | ||||
|     // Create a flow from the spacing of extrusion lines.
 | ||||
|  |  | |||
|  | @ -662,15 +662,19 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ | |||
|         throw std::runtime_error(msg); | ||||
|     } | ||||
| 
 | ||||
|     if (print->config().remaining_times.value) { | ||||
|         BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode" << log_memory_info(); | ||||
|         m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f); | ||||
|     GCodeTimeEstimator::PostProcessData normal_data = m_normal_time_estimator.get_post_process_data(); | ||||
|     GCodeTimeEstimator::PostProcessData silent_data = m_silent_time_estimator.get_post_process_data(); | ||||
| 
 | ||||
|     bool remaining_times_enabled = print->config().remaining_times.value; | ||||
| 
 | ||||
|     BOOST_LOG_TRIVIAL(debug) << "Time estimator post processing" << log_memory_info(); | ||||
|     GCodeTimeEstimator::post_process(path_tmp, 60.0f, remaining_times_enabled ? &normal_data : nullptr, (remaining_times_enabled && m_silent_time_estimator_enabled) ? &silent_data : nullptr); | ||||
| 
 | ||||
|     if (remaining_times_enabled) | ||||
|     { | ||||
|         m_normal_time_estimator.reset(); | ||||
|         if (m_silent_time_estimator_enabled) { | ||||
|             BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode" << log_memory_info(); | ||||
|             m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f); | ||||
|         if (m_silent_time_estimator_enabled) | ||||
|             m_silent_time_estimator.reset(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // starts analyzer calculations
 | ||||
|  | @ -1738,13 +1742,9 @@ void GCode::process_layer( | |||
| 
 | ||||
|                         // This extrusion is part of certain Region, which tells us which extruder should be used for it:
 | ||||
|                         int correct_extruder_id = Print::get_extruder(*fill, region); | ||||
|                         //FIXME what is this?
 | ||||
|                         entity_type=="infills" ?  | ||||
|                             std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) - 1) : | ||||
|                             std::max<int>(region.config().perimeter_extruder.value - 1, 0); | ||||
| 
 | ||||
|                         // Let's recover vector of extruder overrides:
 | ||||
|                         const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, (int)layer_to_print.object()->copies().size()); | ||||
|                         const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->copies().size()); | ||||
| 
 | ||||
|                         // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
 | ||||
|                         for (unsigned int extruder : layer_tools.extruders) | ||||
|  | @ -3027,7 +3027,7 @@ const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtru | |||
| 
 | ||||
| // This function takes the eec and appends its entities to either perimeters or infills of this Region (depending on the first parameter)
 | ||||
| // It also saves pointer to ExtruderPerCopy struct (for each entity), that holds information about which extruders should be used for which copy.
 | ||||
| void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copies_extruder, unsigned int object_copies_num) | ||||
| void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copies_extruder, size_t object_copies_num) | ||||
| { | ||||
|     // We are going to manipulate either perimeters or infills, exactly in the same way. Let's create pointers to the proper structure to not repeat ourselves:
 | ||||
|     ExtrusionEntityCollection* perimeters_or_infills = &infills; | ||||
|  |  | |||
|  | @ -246,7 +246,7 @@ protected: | |||
|                 std::vector<const ExtruderPerCopy*> perimeters_overrides; | ||||
| 
 | ||||
|                 // Appends perimeter/infill entities and writes don't indices of those that are not to be extruder as part of perimeter/infill wiping
 | ||||
|                 void append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copy_extruders, unsigned int object_copies_num); | ||||
|                 void append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copy_extruders, size_t object_copies_num); | ||||
|             }; | ||||
| 
 | ||||
|             std::vector<Region> by_region;                                    // all extrusions for this island, grouped by regions
 | ||||
|  |  | |||
|  | @ -458,14 +458,14 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int | |||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         const auto& object = object_list[i]; | ||||
|         const PrintObject* object = object_list[i]; | ||||
| 
 | ||||
|         // Finds this layer:
 | ||||
|         auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; }); | ||||
|         if (this_layer_it == object->layers().end()) | ||||
|             continue; | ||||
|         const Layer* this_layer = *this_layer_it; | ||||
|         unsigned int num_of_copies = object->copies().size(); | ||||
|         size_t num_of_copies = object->copies().size(); | ||||
| 
 | ||||
|         for (unsigned int copy = 0; copy < num_of_copies; ++copy) {    // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
 | ||||
| 
 | ||||
|  | @ -494,7 +494,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int | |||
| 
 | ||||
|                         if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {     // this infill will be used to wipe this extruder
 | ||||
|                             set_extruder_override(fill, copy, new_extruder, num_of_copies); | ||||
|                             volume_to_wipe -= fill->total_volume(); | ||||
|                             volume_to_wipe -= float(fill->total_volume()); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | @ -512,7 +512,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int | |||
| 
 | ||||
|                         if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { | ||||
|                             set_extruder_override(fill, copy, new_extruder, num_of_copies); | ||||
|                             volume_to_wipe -= fill->total_volume(); | ||||
|                             volume_to_wipe -= float(fill->total_volume()); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | @ -540,9 +540,9 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) | |||
|         if (this_layer_it == object->layers().end()) | ||||
|             continue; | ||||
|         const Layer* this_layer = *this_layer_it; | ||||
|         unsigned int num_of_copies = object->copies().size(); | ||||
|         size_t num_of_copies = object->copies().size(); | ||||
| 
 | ||||
|         for (unsigned int copy = 0; copy < num_of_copies; ++copy) {    // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
 | ||||
|         for (size_t copy = 0; copy < num_of_copies; ++copy) {    // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
 | ||||
|             for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { | ||||
|                 const auto& region = *object->print()->regions()[region_id]; | ||||
| 
 | ||||
|  | @ -598,7 +598,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) | |||
| // so -1 was used as "print as usual".
 | ||||
| // The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden,
 | ||||
| // its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero).
 | ||||
| const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies) | ||||
| const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies) | ||||
| { | ||||
|     auto entity_map_it = entity_map.find(entity); | ||||
|     if (entity_map_it == entity_map.end()) | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ public: | |||
|     } | ||||
| 
 | ||||
|     // This is called from GCode::process_layer - see implementation for further comments:
 | ||||
|     const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies); | ||||
|     const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies); | ||||
| 
 | ||||
|     // This function goes through all infill entities, decides which ones will be used for wiping and
 | ||||
|     // marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower:
 | ||||
|  | @ -44,7 +44,7 @@ private: | |||
|     void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies); | ||||
| 
 | ||||
|     // Returns true in case that entity is not printed with its usual extruder for a given copy:
 | ||||
|     bool is_entity_overridden(const ExtrusionEntity* entity, int copy_id) const { | ||||
|     bool is_entity_overridden(const ExtrusionEntity* entity, size_t copy_id) const { | ||||
|         return (entity_map.find(entity) == entity_map.end() ? false : entity_map.at(entity).at(copy_id) != -1); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -151,7 +151,7 @@ public: | |||
| 
 | ||||
| 		float dx = x - m_current_pos.x(); | ||||
| 		float dy = y - m_current_pos.y(); | ||||
| 		double len = sqrt(dx*dx+dy*dy); | ||||
|         float len = std::sqrt(dx*dx+dy*dy); | ||||
|         if (record_length) | ||||
|             m_used_filament_length += e; | ||||
| 
 | ||||
|  | @ -159,11 +159,11 @@ public: | |||
| 		Vec2f rotated_current_pos(rotate(m_current_pos + Vec2f(0.f,m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we are
 | ||||
| 		Vec2f rot(rotate(Vec2f(x,y+m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle));                               // this is where we want to go
 | ||||
| 
 | ||||
| 		if (! m_preview_suppressed && e > 0.f && len > 0.) { | ||||
|         if (! m_preview_suppressed && e > 0.f && len > 0.f) { | ||||
|             change_analyzer_mm3_per_mm(len, e); | ||||
| 			// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
 | ||||
| 			// This is left zero if it is a travel move.
 | ||||
| 			float width = float(double(e) * m_filpar[0].filament_area / (len * m_layer_height)); | ||||
|             float width = e * m_filpar[0].filament_area / (len * m_layer_height); | ||||
| 			// Correct for the roundings of a squished extrusion.
 | ||||
| 			width += m_layer_height * float(1. - M_PI / 4.); | ||||
| 			if (m_extrusions.empty() || m_extrusions.back().pos != rotated_current_pos) | ||||
|  | @ -172,10 +172,10 @@ public: | |||
| 		} | ||||
| 
 | ||||
| 		m_gcode += "G1"; | ||||
| 		if (std::abs(rot.x() - rotated_current_pos.x()) > EPSILON) | ||||
|         if (std::abs(rot.x() - rotated_current_pos.x()) > (float)EPSILON) | ||||
| 			m_gcode += set_format_X(rot.x()); | ||||
| 
 | ||||
| 		if (std::abs(rot.y() - rotated_current_pos.y()) > EPSILON) | ||||
|         if (std::abs(rot.y() - rotated_current_pos.y()) > (float)EPSILON) | ||||
| 			m_gcode += set_format_Y(rot.y()); | ||||
| 
 | ||||
| 
 | ||||
|  | @ -184,7 +184,7 @@ public: | |||
| 
 | ||||
| 		if (f != 0.f && f != m_current_feedrate) { | ||||
|             if (limit_volumetric_flow) { | ||||
|                 float e_speed = e / (((len == 0) ? std::abs(e) : len) / f * 60.f); | ||||
|                 float e_speed = e / (((len == 0.f) ? std::abs(e) : len) / f * 60.f); | ||||
|                 f /= std::max(1.f, e_speed / m_filpar[m_current_tool].max_e_speed); | ||||
|             } | ||||
| 			m_gcode += set_format_F(f); | ||||
|  | @ -194,7 +194,7 @@ public: | |||
|         m_current_pos.y() = y; | ||||
| 
 | ||||
| 		// Update the elapsed time with a rough estimate.
 | ||||
| 		m_elapsed_time += ((len == 0) ? std::abs(e) : len) / m_current_feedrate * 60.f; | ||||
|         m_elapsed_time += ((len == 0.f) ? std::abs(e) : len) / m_current_feedrate * 60.f; | ||||
| 		m_gcode += "\n"; | ||||
| 		return *this; | ||||
| 	} | ||||
|  | @ -214,7 +214,7 @@ public: | |||
| 	{ | ||||
| 		float dx = x - m_current_pos.x(); | ||||
| 		float dy = y - m_current_pos.y(); | ||||
| 		return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true); | ||||
|         return extrude_explicit(x, y, std::sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true); | ||||
| 	} | ||||
| 
 | ||||
| 	WipeTowerWriter& extrude(const Vec2f &dest, const float f = 0.f)  | ||||
|  | @ -311,7 +311,7 @@ public: | |||
| 		return *this; | ||||
| 	} | ||||
| 
 | ||||
| 	WipeTowerWriter& set_tool(int tool) | ||||
|     WipeTowerWriter& set_tool(unsigned tool) | ||||
| 	{ | ||||
| 		m_current_tool = tool; | ||||
| 		return *this; | ||||
|  | @ -320,57 +320,52 @@ public: | |||
| 	// Set extruder temperature, don't wait by default.
 | ||||
| 	WipeTowerWriter& set_extruder_temp(int temperature, bool wait = false) | ||||
| 	{ | ||||
|         char buf[128]; | ||||
|         sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature); | ||||
|         m_gcode += buf; | ||||
|         m_gcode += "M" + std::to_string(wait ? 109 : 104) + " S" + std::to_string(temperature) + "\n"; | ||||
|         return *this; | ||||
| 	}; | ||||
|     } | ||||
| 
 | ||||
|     // Wait for a period of time (seconds).
 | ||||
| 	WipeTowerWriter& wait(float time) | ||||
| 	{ | ||||
|         if (time==0) | ||||
|         if (time==0.f) | ||||
|             return *this; | ||||
| 		char buf[128]; | ||||
| 		sprintf(buf, "G4 S%.3f\n", time); | ||||
| 		m_gcode += buf; | ||||
| 		return *this; | ||||
| 	}; | ||||
|     } | ||||
| 
 | ||||
| 	// Set speed factor override percentage.
 | ||||
| 	WipeTowerWriter& speed_override(int speed) | ||||
| 	{ | ||||
| 		char buf[128]; | ||||
| 		sprintf(buf, "M220 S%d\n", speed); | ||||
| 		m_gcode += buf; | ||||
|         m_gcode += "M220 S" + std::to_string(speed) + "\n"; | ||||
| 		return *this; | ||||
| 	}; | ||||
|     } | ||||
| 
 | ||||
| 	// Let the firmware back up the active speed override value.
 | ||||
| 	WipeTowerWriter& speed_override_backup() | ||||
| 	{ | ||||
| 		m_gcode += "M220 B\n"; | ||||
| 		return *this; | ||||
| 	}; | ||||
|     } | ||||
| 
 | ||||
| 	// Let the firmware restore the active speed override value.
 | ||||
| 	WipeTowerWriter& speed_override_restore() | ||||
| 	{ | ||||
| 		m_gcode += "M220 R\n"; | ||||
| 		return *this; | ||||
| 	}; | ||||
|     } | ||||
| 
 | ||||
| 	// Set digital trimpot motor
 | ||||
| 	WipeTowerWriter& set_extruder_trimpot(int current) | ||||
| 	{ | ||||
| 		char buf[128]; | ||||
|         if (m_gcode_flavor == gcfRepRap) | ||||
|             sprintf(buf, "M906 E%d\n", current); | ||||
|             m_gcode += "M906 E"; | ||||
|         else | ||||
|             sprintf(buf, "M907 E%d\n", current); | ||||
| 		m_gcode += buf; | ||||
|             m_gcode += "M907 E"; | ||||
|         m_gcode += std::to_string(current) + "\n"; | ||||
| 		return *this; | ||||
| 	}; | ||||
|     } | ||||
| 
 | ||||
| 	WipeTowerWriter& flush_planner_queue() | ||||
| 	{  | ||||
|  | @ -386,28 +381,20 @@ public: | |||
| 	} | ||||
| 
 | ||||
| 	WipeTowerWriter& comment_with_value(const char *comment, int value) | ||||
| 	{ | ||||
| 		char strvalue[64]; | ||||
| 		sprintf(strvalue, "%d", value); | ||||
| 		m_gcode += std::string(";") + comment + strvalue + "\n"; | ||||
|     { | ||||
|         m_gcode += std::string(";") + comment + std::to_string(value) + "\n"; | ||||
| 		return *this; | ||||
| 	}; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 	WipeTowerWriter& set_fan(unsigned int speed) | ||||
|     WipeTowerWriter& set_fan(unsigned speed) | ||||
| 	{ | ||||
| 		if (speed == m_last_fan_speed) | ||||
| 			return *this; | ||||
| 				 | ||||
| 		if (speed == 0) | ||||
| 			m_gcode += "M107\n"; | ||||
| 		else | ||||
| 		{ | ||||
| 			m_gcode += "M106 S"; | ||||
| 			char buf[128]; | ||||
| 			sprintf(buf,"%u\n",(unsigned int)(255.0 * speed / 100.0)); | ||||
| 			m_gcode += buf; | ||||
| 		} | ||||
|         else | ||||
|             m_gcode += "M106 S" + std::to_string(unsigned(255.0 * speed / 100.0)) + "\n"; | ||||
| 		m_last_fan_speed = speed; | ||||
| 		return *this; | ||||
| 	} | ||||
|  | @ -430,7 +417,7 @@ private: | |||
| 	float		  m_y_shift = 0.f; | ||||
| 	float 		  m_wipe_tower_width = 0.f; | ||||
| 	float		  m_wipe_tower_depth = 0.f; | ||||
| 	float		  m_last_fan_speed = 0.f; | ||||
|     unsigned      m_last_fan_speed = 0.f; | ||||
|     int           current_temp = -1; | ||||
|     const float   m_default_analyzer_line_width; | ||||
|     float         m_used_filament_length = 0.f; | ||||
|  | @ -479,12 +466,12 @@ private: | |||
| WipeTower::WipeTower(const PrintConfig& config, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool) : | ||||
|     m_semm(config.single_extruder_multi_material.value), | ||||
|     m_wipe_tower_pos(config.wipe_tower_x, config.wipe_tower_y), | ||||
|     m_wipe_tower_width(config.wipe_tower_width), | ||||
|     m_wipe_tower_rotation_angle(config.wipe_tower_rotation_angle), | ||||
|     m_wipe_tower_width(float(config.wipe_tower_width)), | ||||
|     m_wipe_tower_rotation_angle(float(config.wipe_tower_rotation_angle)), | ||||
|     m_y_shift(0.f), | ||||
|     m_z_pos(0.f), | ||||
|     m_is_first_layer(false), | ||||
|     m_bridging(config.wipe_tower_bridging), | ||||
|     m_bridging(float(config.wipe_tower_bridging)), | ||||
|     m_gcode_flavor(config.gcode_flavor), | ||||
|     m_current_tool(initial_tool), | ||||
|     wipe_volumes(wiping_matrix) | ||||
|  | @ -492,16 +479,16 @@ WipeTower::WipeTower(const PrintConfig& config, const std::vector<std::vector<fl | |||
|     // If this is a single extruder MM printer, we will use all the SE-specific config values.
 | ||||
|     // Otherwise, the defaults will be used to turn off the SE stuff.
 | ||||
|     if (m_semm) { | ||||
|         m_cooling_tube_retraction = config.cooling_tube_retraction; | ||||
|         m_cooling_tube_length = config.cooling_tube_length; | ||||
|         m_parking_pos_retraction = config.parking_pos_retraction; | ||||
|         m_extra_loading_move = config.extra_loading_move; | ||||
|         m_set_extruder_trimpot = config.high_current_on_filament_swap; | ||||
|         m_cooling_tube_retraction = float(config.cooling_tube_retraction); | ||||
|         m_cooling_tube_length     = float(config.cooling_tube_length); | ||||
|         m_parking_pos_retraction  = float(config.parking_pos_retraction); | ||||
|         m_extra_loading_move      = float(config.extra_loading_move); | ||||
|         m_set_extruder_trimpot    = config.high_current_on_filament_swap; | ||||
|     } | ||||
|     // Calculate where the priming lines should be - very naive test not detecting parallelograms or custom shapes
 | ||||
|     const std::vector<Vec2d>& bed_points = config.bed_shape.values; | ||||
|     m_bed_shape = (bed_points.size() == 4 ? RectangularBed : CircularBed); | ||||
|     m_bed_width = BoundingBoxf(bed_points).size().x(); | ||||
|     m_bed_width = float(BoundingBoxf(bed_points).size().x()); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -518,21 +505,21 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config) | |||
|     // If this is a single extruder MM printer, we will use all the SE-specific config values.
 | ||||
|     // Otherwise, the defaults will be used to turn off the SE stuff.
 | ||||
|     if (m_semm) { | ||||
|         m_filpar[idx].loading_speed           = config.filament_loading_speed.get_at(idx); | ||||
|         m_filpar[idx].loading_speed_start     = config.filament_loading_speed_start.get_at(idx); | ||||
|         m_filpar[idx].unloading_speed         = config.filament_unloading_speed.get_at(idx); | ||||
|         m_filpar[idx].unloading_speed_start   = config.filament_unloading_speed_start.get_at(idx); | ||||
|         m_filpar[idx].delay                   = config.filament_toolchange_delay.get_at(idx); | ||||
|         m_filpar[idx].loading_speed           = float(config.filament_loading_speed.get_at(idx)); | ||||
|         m_filpar[idx].loading_speed_start     = float(config.filament_loading_speed_start.get_at(idx)); | ||||
|         m_filpar[idx].unloading_speed         = float(config.filament_unloading_speed.get_at(idx)); | ||||
|         m_filpar[idx].unloading_speed_start   = float(config.filament_unloading_speed_start.get_at(idx)); | ||||
|         m_filpar[idx].delay                   = float(config.filament_toolchange_delay.get_at(idx)); | ||||
|         m_filpar[idx].cooling_moves           = config.filament_cooling_moves.get_at(idx); | ||||
|         m_filpar[idx].cooling_initial_speed   = config.filament_cooling_initial_speed.get_at(idx); | ||||
|         m_filpar[idx].cooling_final_speed     = config.filament_cooling_final_speed.get_at(idx); | ||||
|         m_filpar[idx].cooling_initial_speed   = float(config.filament_cooling_initial_speed.get_at(idx)); | ||||
|         m_filpar[idx].cooling_final_speed     = float(config.filament_cooling_final_speed.get_at(idx)); | ||||
|     } | ||||
| 
 | ||||
|     m_filpar[idx].filament_area = float((M_PI/4.f) * pow(config.filament_diameter.get_at(idx), 2)); // all extruders are assumed to have the same filament diameter at this point
 | ||||
|     float nozzle_diameter = config.nozzle_diameter.get_at(idx); | ||||
|     float nozzle_diameter = float(config.nozzle_diameter.get_at(idx)); | ||||
|     m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
 | ||||
| 
 | ||||
|     float max_vol_speed = config.filament_max_volumetric_speed.get_at(idx); | ||||
|     float max_vol_speed = float(config.filament_max_volumetric_speed.get_at(idx)); | ||||
|     if (max_vol_speed!= 0.f) | ||||
|         m_filpar[idx].max_e_speed = (max_vol_speed / filament_area()); | ||||
| 
 | ||||
|  | @ -561,7 +548,7 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime( | |||
| 	const std::vector<unsigned int> &tools, | ||||
| 	// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
 | ||||
| 	// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
 | ||||
| 	bool 						last_wipe_inside_wipe_tower) | ||||
|     bool 						/*last_wipe_inside_wipe_tower*/) | ||||
| { | ||||
| 	this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false); | ||||
| 	this->m_current_tool 		= tools.front(); | ||||
|  | @ -696,7 +683,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(unsigned int tool, bool last_ | |||
| 	box_coordinates cleaning_box( | ||||
| 		Vec2f(m_perimeter_width / 2.f, m_perimeter_width / 2.f), | ||||
| 		m_wipe_tower_width - m_perimeter_width, | ||||
| 		(tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5*m_perimeter_width | ||||
|         (tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5f*m_perimeter_width | ||||
|                                     : m_wipe_tower_depth-m_perimeter_width)); | ||||
| 
 | ||||
| 	WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); | ||||
|  | @ -802,7 +789,7 @@ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_of | |||
|     // Extrude 4 rounds of a brim around the future wipe tower.
 | ||||
|     box_coordinates box(wipeTower_box); | ||||
|     for (size_t i = 0; i < 4; ++ i) { | ||||
|         box.expand(m_perimeter_width - m_layer_height*(1.f-M_PI_4)); // the brim shall have 'normal' spacing with no extra void space
 | ||||
|         box.expand(m_perimeter_width - m_layer_height*float(1.-M_PI_4)); // the brim shall have 'normal' spacing with no extra void space
 | ||||
|         writer.travel (box.ld, 7000) | ||||
|                 .extrude(box.lu, 2100).extrude(box.ru) | ||||
|                 .extrude(box.rd      ).extrude(box.ld); | ||||
|  | @ -911,8 +898,8 @@ void WipeTower::toolchange_Unload( | |||
|         const float x = volume_to_length(m_filpar[m_current_tool].ramming_speed[i] * 0.25f, line_width, m_layer_height); | ||||
|         const float e = m_filpar[m_current_tool].ramming_speed[i] * 0.25f / filament_area(); // transform volume per sec to E move;
 | ||||
|         const float dist = std::min(x - e_done, remaining);		  // distance to travel for either the next 0.25s, or to the next turnaround
 | ||||
|         const float actual_time = dist/x * 0.25; | ||||
|         writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), dist / (actual_time / 60.)); | ||||
|         const float actual_time = dist/x * 0.25f; | ||||
|         writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0.f, 0.f, e * (dist / x), dist / (actual_time / 60.f)); | ||||
|         remaining -= dist; | ||||
| 
 | ||||
| 		if (remaining < WT_EPSILON)	{ // we reached a turning point
 | ||||
|  | @ -995,8 +982,8 @@ void WipeTower::toolchange_Unload( | |||
| // Change the tool, set a speed override for soluble and flex materials.
 | ||||
| void WipeTower::toolchange_Change( | ||||
| 	WipeTowerWriter &writer, | ||||
| 	const unsigned int 	new_tool,  | ||||
| 	const std::string&  new_material) | ||||
|     const size_t 	new_tool, | ||||
|     const std::string&  new_material) | ||||
| { | ||||
|     // Ask the writer about how much of the old filament we consumed:
 | ||||
|     if (m_current_tool < m_used_filament_length.size()) | ||||
|  | @ -1091,21 +1078,21 @@ void WipeTower::toolchange_Wipe( | |||
| 
 | ||||
| 		float traversed_x = writer.x(); | ||||
| 		if (m_left_to_right) | ||||
| 			writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff); | ||||
|             writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5f*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff); | ||||
| 		else | ||||
| 			writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff); | ||||
|             writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5f*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff); | ||||
| 
 | ||||
|         if (writer.y()+EPSILON > cleaning_box.lu.y()-0.5f*m_perimeter_width) | ||||
|         if (writer.y()+float(EPSILON) > cleaning_box.lu.y()-0.5f*m_perimeter_width) | ||||
|             break;		// in case next line would not fit
 | ||||
| 
 | ||||
| 		traversed_x -= writer.x(); | ||||
| 		x_to_wipe -= fabs(traversed_x); | ||||
|         x_to_wipe -= std::abs(traversed_x); | ||||
| 		if (x_to_wipe < WT_EPSILON) { | ||||
| 			writer.travel(m_left_to_right ? xl + 1.5*m_perimeter_width : xr - 1.5*m_perimeter_width, writer.y(), 7200); | ||||
|             writer.travel(m_left_to_right ? xl + 1.5f*m_perimeter_width : xr - 1.5f*m_perimeter_width, writer.y(), 7200); | ||||
| 			break; | ||||
| 		} | ||||
| 		// stepping to the next line:
 | ||||
| 		writer.extrude(writer.x() + (i % 4 == 0 ? -1.f : (i % 4 == 1 ? 1.f : 0.f)) * 1.5*m_perimeter_width, writer.y() + dy); | ||||
|         writer.extrude(writer.x() + (i % 4 == 0 ? -1.f : (i % 4 == 1 ? 1.f : 0.f)) * 1.5f*m_perimeter_width, writer.y() + dy); | ||||
| 		m_left_to_right = !m_left_to_right; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1188,7 +1175,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() | |||
|             writer.travel(fill_box.ld + Vec2f(m_perimeter_width * 2, 0.f)) | ||||
|                   .extrude(fill_box.lu + Vec2f(m_perimeter_width * 2, 0.f), 2900 * speed_factor); | ||||
| 
 | ||||
|             const int n = 1+(right-left)/(m_bridging); | ||||
|             const int n = 1+int((right-left)/m_bridging); | ||||
|             const float dx = (right-left)/n; | ||||
|             for (int i=1;i<=n;++i) { | ||||
|                 float x=left+dx*i; | ||||
|  | @ -1267,7 +1254,7 @@ void WipeTower::plan_tower() | |||
| 	for (auto& layer : m_plan) | ||||
| 		layer.depth = 0.f; | ||||
| 	 | ||||
| 	for (int layer_index = m_plan.size() - 1; layer_index >= 0; --layer_index) | ||||
|     for (int layer_index = int(m_plan.size()) - 1; layer_index >= 0; --layer_index) | ||||
| 	{ | ||||
| 		float this_layer_depth = std::max(m_plan[layer_index].depth, m_plan[layer_index].toolchanges_depth()); | ||||
| 		m_plan[layer_index].depth = this_layer_depth; | ||||
|  |  | |||
|  | @ -7,21 +7,21 @@ | |||
| #include <utility> | ||||
| #include <algorithm> | ||||
| 
 | ||||
| #include "libslic3r/PrintConfig.hpp" | ||||
| 
 | ||||
| #include "libslic3r/Point.hpp" | ||||
| 
 | ||||
| namespace Slic3r | ||||
| { | ||||
| 
 | ||||
| class WipeTowerWriter; | ||||
| 
 | ||||
| class PrintConfig; | ||||
| enum GCodeFlavor : unsigned char; | ||||
| 
 | ||||
| 
 | ||||
| class WipeTower | ||||
| { | ||||
| public: | ||||
|     struct Extrusion | ||||
| 	{ | ||||
|     { | ||||
| 		Extrusion(const Vec2f &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {} | ||||
| 		// End position of this extrusion.
 | ||||
| 		Vec2f				pos; | ||||
|  | @ -79,7 +79,6 @@ public: | |||
| 	// width		-- width of wipe tower in mm ( default 60 mm - leave as it is )
 | ||||
| 	// wipe_area	-- space available for one toolchange in mm
 | ||||
|     WipeTower(const PrintConfig& config, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool); | ||||
| 	virtual ~WipeTower() {} | ||||
| 
 | ||||
| 
 | ||||
| 	// Set the extruder properties.
 | ||||
|  | @ -244,7 +243,7 @@ private: | |||
| 	bool m_print_brim = true; | ||||
| 	// A fill-in direction (positive Y, negative Y) alternates with each layer.
 | ||||
| 	wipe_shape   	m_current_shape = SHAPE_NORMAL; | ||||
| 	unsigned int 	m_current_tool  = 0; | ||||
|     size_t 	m_current_tool  = 0; | ||||
|     const std::vector<std::vector<float>> wipe_volumes; | ||||
| 
 | ||||
| 	float           m_depth_traversed = 0.f; // Current y position at the wipe tower.
 | ||||
|  | @ -309,13 +308,13 @@ private: | |||
| 	// to store information about tool changes for a given layer
 | ||||
| 	struct WipeTowerInfo{ | ||||
| 		struct ToolChange { | ||||
| 			unsigned int old_tool; | ||||
| 			unsigned int new_tool; | ||||
|             size_t old_tool; | ||||
|             size_t new_tool; | ||||
| 			float required_depth; | ||||
|             float ramming_depth; | ||||
|             float first_wipe_line; | ||||
|             float wipe_volume; | ||||
| 			ToolChange(unsigned int old, unsigned int newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f) | ||||
|             ToolChange(size_t old, size_t newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f) | ||||
|             : old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv} {} | ||||
| 		}; | ||||
| 		float z;		// z position of the layer
 | ||||
|  | @ -350,7 +349,7 @@ private: | |||
| 
 | ||||
| 	void toolchange_Change( | ||||
| 		WipeTowerWriter &writer, | ||||
| 		const unsigned int		new_tool, | ||||
|         const size_t		new_tool, | ||||
| 		const std::string& 		new_material); | ||||
| 	 | ||||
| 	void toolchange_Load( | ||||
|  |  | |||
|  | @ -173,9 +173,7 @@ namespace Slic3r { | |||
|     const std::string GCodeTimeEstimator::Normal_Last_M73_Output_Placeholder_Tag = "; _TE_NORMAL_LAST_M73_OUTPUT_PLACEHOLDER"; | ||||
|     const std::string GCodeTimeEstimator::Silent_Last_M73_Output_Placeholder_Tag = "; _TE_SILENT_LAST_M73_OUTPUT_PLACEHOLDER"; | ||||
| 
 | ||||
|     // temporary human readable form to use until not removed from gcode by the new post-process method
 | ||||
|     const std::string GCodeTimeEstimator::Color_Change_Tag = "PRINT_COLOR_CHANGE"; | ||||
| //    const std::string GCodeTimeEstimator::Color_Change_Tag = "_TE_COLOR_CHANGE";
 | ||||
| 
 | ||||
|     GCodeTimeEstimator::GCodeTimeEstimator(EMode mode) | ||||
|         : m_mode(mode) | ||||
|  | @ -273,130 +271,138 @@ namespace Slic3r { | |||
| #endif // ENABLE_MOVE_STATS
 | ||||
|     } | ||||
| 
 | ||||
|     bool GCodeTimeEstimator::post_process_remaining_times(const std::string& filename, float interval) | ||||
|     bool GCodeTimeEstimator::post_process(const std::string& filename, float interval_sec, const PostProcessData* const normal_mode, const PostProcessData* const silent_mode) | ||||
|     { | ||||
|         boost::nowide::ifstream in(filename); | ||||
|         if (!in.good()) | ||||
|             throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for reading.\n")); | ||||
|             throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for reading.\n")); | ||||
| 
 | ||||
|         std::string path_tmp = filename + ".times"; | ||||
|         std::string path_tmp = filename + ".postprocess"; | ||||
| 
 | ||||
|         FILE* out = boost::nowide::fopen(path_tmp.c_str(), "wb"); | ||||
|         if (out == nullptr) | ||||
|             throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for writing.\n")); | ||||
|             throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for writing.\n")); | ||||
| 
 | ||||
|         std::string time_mask; | ||||
|         switch (m_mode) | ||||
|         { | ||||
|         default: | ||||
|         case Normal: | ||||
|         { | ||||
|             time_mask = "M73 P%s R%s\n"; | ||||
|             break; | ||||
|         } | ||||
|         case Silent: | ||||
|         { | ||||
|             time_mask = "M73 Q%s S%s\n"; | ||||
|             break; | ||||
|         } | ||||
|         } | ||||
|         std::string normal_time_mask = "M73 P%s R%s\n"; | ||||
|         std::string silent_time_mask = "M73 Q%s S%s\n"; | ||||
|         char line_M73[64]; | ||||
| 
 | ||||
|         unsigned int g1_lines_count = 0; | ||||
|         float last_recorded_time = 0.0f; | ||||
|         std::string gcode_line; | ||||
|         // buffer line to export only when greater than 64K to reduce writing calls
 | ||||
|         std::string export_line; | ||||
|         char time_line[64]; | ||||
| 		G1LineIdToBlockIdMap::const_iterator it_line_id = m_g1_line_ids.begin(); | ||||
| 		while (std::getline(in, gcode_line)) | ||||
|         { | ||||
|             if (!in.good()) | ||||
|             { | ||||
|                 fclose(out); | ||||
|                 throw std::runtime_error(std::string("Remaining times export failed.\nError while reading from file.\n")); | ||||
|             } | ||||
| 
 | ||||
|             // replaces placeholders for initial line M73 with the real lines
 | ||||
|             if (((m_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) || | ||||
|                 ((m_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag))) | ||||
|             { | ||||
|                 sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(m_time).c_str()); | ||||
|                 gcode_line = time_line; | ||||
|             } | ||||
|             // replaces placeholders for final line M73 with the real lines
 | ||||
|             else if (((m_mode == Normal) && (gcode_line == Normal_Last_M73_Output_Placeholder_Tag)) || | ||||
|                      ((m_mode == Silent) && (gcode_line == Silent_Last_M73_Output_Placeholder_Tag))) | ||||
|             { | ||||
|                 sprintf(time_line, time_mask.c_str(), "100", "0"); | ||||
|                 gcode_line = time_line; | ||||
|             } | ||||
|             else | ||||
|                gcode_line += "\n"; | ||||
| 
 | ||||
| 
 | ||||
|             // add remaining time lines where needed
 | ||||
|             m_parser.parse_line(gcode_line, | ||||
|                 [this, &it_line_id, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line) | ||||
|             { | ||||
|                 if (line.cmd_is("G1")) | ||||
|                 { | ||||
|                     ++g1_lines_count; | ||||
| 
 | ||||
| 					assert(it_line_id == m_g1_line_ids.end() || it_line_id->first >= g1_lines_count); | ||||
| 
 | ||||
| 					const Block *block = nullptr; | ||||
| 					if (it_line_id != m_g1_line_ids.end() && it_line_id->first == g1_lines_count) { | ||||
| 						if (line.has_e() && it_line_id->second < (unsigned int)m_blocks.size()) | ||||
| 							block = &m_blocks[it_line_id->second]; | ||||
| 						++it_line_id; | ||||
| 					} | ||||
| 
 | ||||
| 					if (block != nullptr && block->elapsed_time != -1.0f) { | ||||
|                         float block_remaining_time = m_time - block->elapsed_time; | ||||
|                         if (std::abs(last_recorded_time - block_remaining_time) > interval) | ||||
|                         { | ||||
|                             sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / m_time)).c_str(), _get_time_minutes(block_remaining_time).c_str()); | ||||
|                             gcode_line += time_line; | ||||
| 
 | ||||
|                             last_recorded_time = block_remaining_time; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|             export_line += gcode_line; | ||||
|             if (export_line.length() > 65535) | ||||
|             { | ||||
|                 fwrite((const void*)export_line.c_str(), 1, export_line.length(), out); | ||||
|                 if (ferror(out)) | ||||
|                 { | ||||
|                     in.close(); | ||||
|                     fclose(out); | ||||
|                     boost::nowide::remove(path_tmp.c_str()); | ||||
|                     throw std::runtime_error(std::string("Remaining times export failed.\nIs the disk full?\n")); | ||||
|                 } | ||||
|                 export_line.clear(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (export_line.length() > 0) | ||||
|         { | ||||
|         // helper function to write to disk
 | ||||
|         auto write_string = [&](const std::string& str) { | ||||
|             fwrite((const void*)export_line.c_str(), 1, export_line.length(), out); | ||||
|             if (ferror(out)) | ||||
|             { | ||||
|                 in.close(); | ||||
|                 fclose(out); | ||||
|                 boost::nowide::remove(path_tmp.c_str()); | ||||
|                 throw std::runtime_error(std::string("Remaining times export failed.\nIs the disk full?\n")); | ||||
|                 throw std::runtime_error(std::string("Time estimator post process export failed.\nIs the disk full?\n")); | ||||
|             } | ||||
|             export_line.clear(); | ||||
|         }; | ||||
| 
 | ||||
|         GCodeReader parser; | ||||
|         unsigned int g1_lines_count = 0; | ||||
|         int normal_g1_line_id = 0; | ||||
|         float normal_last_recorded_time = 0.0f; | ||||
|         int silent_g1_line_id = 0; | ||||
|         float silent_last_recorded_time = 0.0f; | ||||
| 
 | ||||
|         // helper function to process g1 lines
 | ||||
|         auto process_g1_line = [&](const PostProcessData* const data, const GCodeReader::GCodeLine& line, int& g1_line_id, float& last_recorded_time, const std::string& time_mask) { | ||||
|             if (data == nullptr) | ||||
|                 return; | ||||
| 
 | ||||
|             assert((g1_line_id >= (int)data->g1_line_ids.size()) || (data->g1_line_ids[g1_line_id].first >= g1_lines_count)); | ||||
|             const Block* block = nullptr; | ||||
|             const G1LineIdToBlockId& map_item = data->g1_line_ids[g1_line_id]; | ||||
|             if ((g1_line_id < (int)data->g1_line_ids.size()) && (map_item.first == g1_lines_count)) | ||||
|             { | ||||
|                 if (line.has_e() && (map_item.second < (unsigned int)data->blocks.size())) | ||||
|                     block = &data->blocks[map_item.second]; | ||||
|                 ++g1_line_id; | ||||
|             } | ||||
| 
 | ||||
|             if ((block != nullptr) && (block->elapsed_time != -1.0f)) | ||||
|             { | ||||
|                 float block_remaining_time = data->time - block->elapsed_time; | ||||
|                 if (std::abs(last_recorded_time - block_remaining_time) > interval_sec) | ||||
|                 { | ||||
|                     sprintf(line_M73, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / data->time)).c_str(), _get_time_minutes(block_remaining_time).c_str()); | ||||
|                     gcode_line += line_M73; | ||||
| 
 | ||||
|                     last_recorded_time = block_remaining_time; | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         while (std::getline(in, gcode_line)) | ||||
|         { | ||||
|             if (!in.good()) | ||||
|             { | ||||
|                 fclose(out); | ||||
|                 throw std::runtime_error(std::string("Time estimator post process export failed.\nError while reading from file.\n")); | ||||
|             } | ||||
| 
 | ||||
|             // check tags
 | ||||
|             // remove color change tag
 | ||||
|             if (gcode_line == "; " + Color_Change_Tag) | ||||
|                 continue; | ||||
| 
 | ||||
|             // replaces placeholders for initial line M73 with the real lines
 | ||||
|             if ((normal_mode != nullptr) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) | ||||
|             { | ||||
|                 sprintf(line_M73, normal_time_mask.c_str(), "0", _get_time_minutes(normal_mode->time).c_str()); | ||||
|                 gcode_line = line_M73; | ||||
|             } | ||||
|             else if ((silent_mode != nullptr) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag)) | ||||
|             { | ||||
|                 sprintf(line_M73, silent_time_mask.c_str(), "0", _get_time_minutes(silent_mode->time).c_str()); | ||||
|                 gcode_line = line_M73; | ||||
|             } | ||||
|             // replaces placeholders for final line M73 with the real lines
 | ||||
|             else if ((normal_mode != nullptr) && (gcode_line == Normal_Last_M73_Output_Placeholder_Tag)) | ||||
|             { | ||||
|                 sprintf(line_M73, normal_time_mask.c_str(), "100", "0"); | ||||
|                 gcode_line = line_M73; | ||||
|             } | ||||
|             else if ((silent_mode != nullptr) && (gcode_line == Silent_Last_M73_Output_Placeholder_Tag)) | ||||
|             { | ||||
|                 sprintf(line_M73, silent_time_mask.c_str(), "100", "0"); | ||||
|                 gcode_line = line_M73; | ||||
|             } | ||||
|             else | ||||
|                 gcode_line += "\n"; | ||||
| 
 | ||||
|             // add remaining time lines where needed
 | ||||
|             parser.parse_line(gcode_line, | ||||
|                 [&](GCodeReader& reader, const GCodeReader::GCodeLine& line) | ||||
|                 { | ||||
|                     if (line.cmd_is("G1")) | ||||
|                     { | ||||
|                         ++g1_lines_count; | ||||
|                         process_g1_line(silent_mode, line, silent_g1_line_id, silent_last_recorded_time, silent_time_mask); | ||||
|                         process_g1_line(normal_mode, line, normal_g1_line_id, normal_last_recorded_time, normal_time_mask); | ||||
|                     } | ||||
|                 }); | ||||
| 
 | ||||
|             export_line += gcode_line; | ||||
|             if (export_line.length() > 65535) | ||||
|                 write_string(export_line); | ||||
|         } | ||||
| 
 | ||||
|         if (!export_line.empty()) | ||||
|             write_string(export_line); | ||||
| 
 | ||||
|         fclose(out); | ||||
|         in.close(); | ||||
| 
 | ||||
|         if (rename_file(path_tmp, filename)) | ||||
|             throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' + | ||||
|             "Is " + path_tmp + " locked?" + '\n'); | ||||
|                 "Is " + path_tmp + " locked?" + '\n'); | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
|  |  | |||
|  | @ -213,9 +213,19 @@ namespace Slic3r { | |||
|         typedef std::map<Block::EMoveType, MoveStats> MovesStatsMap; | ||||
| #endif // ENABLE_MOVE_STATS
 | ||||
| 
 | ||||
|     public: | ||||
|         typedef std::pair<unsigned int, unsigned int> G1LineIdToBlockId; | ||||
|         typedef std::vector<G1LineIdToBlockId> G1LineIdToBlockIdMap; | ||||
| 
 | ||||
|         struct PostProcessData | ||||
|         { | ||||
|             const G1LineIdToBlockIdMap& g1_line_ids; | ||||
|             const BlocksList& blocks; | ||||
|             float time; | ||||
| 
 | ||||
|             PostProcessData(const G1LineIdToBlockIdMap& g1_line_ids, const BlocksList& blocks, float time) : g1_line_ids(g1_line_ids), blocks(blocks), time(time) {} | ||||
|         }; | ||||
| 
 | ||||
|     private: | ||||
|         EMode m_mode; | ||||
|         GCodeReader m_parser; | ||||
|  | @ -263,11 +273,12 @@ namespace Slic3r { | |||
|         void calculate_time_from_lines(const std::vector<std::string>& gcode_lines); | ||||
| 
 | ||||
|         // Process the gcode contained in the file with the given filename, 
 | ||||
|         // placing in it new lines (M73) containing the remaining time, at the given interval in seconds
 | ||||
|         // and saving the result back in the same file
 | ||||
|         // This time estimator should have been already used to calculate the time estimate for the gcode
 | ||||
|         // contained in the given file before to call this method
 | ||||
|         bool post_process_remaining_times(const std::string& filename, float interval_sec); | ||||
|         // replacing placeholders with correspondent new lines M73
 | ||||
|         // placing new lines M73 (containing the remaining time) where needed (in dependence of the given interval in seconds)
 | ||||
|         // and removing working tags (as those used for color changes)
 | ||||
|         // if normal_mode == nullptr no M73 line will be added for normal mode
 | ||||
|         // if silent_mode == nullptr no M73 line will be added for silent mode
 | ||||
|         static bool post_process(const std::string& filename, float interval_sec, const PostProcessData* const normal_mode, const PostProcessData* const silent_mode); | ||||
| 
 | ||||
|         // Set current position on the given axis with the given value
 | ||||
|         void set_axis_position(EAxis axis, float position); | ||||
|  | @ -362,6 +373,8 @@ namespace Slic3r { | |||
|         // Return an estimate of the memory consumed by the time estimator.
 | ||||
|         size_t memory_used() const; | ||||
| 
 | ||||
|         PostProcessData get_post_process_data() const { return PostProcessData(m_g1_line_ids, m_blocks, m_time); } | ||||
| 
 | ||||
|     private: | ||||
|         void _reset(); | ||||
|         void _reset_time(); | ||||
|  |  | |||
|  | @ -171,21 +171,6 @@ void Layer::make_perimeters() | |||
|     BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << " - Done"; | ||||
| } | ||||
| 
 | ||||
| void Layer::make_fills() | ||||
| { | ||||
|     #ifdef SLIC3R_DEBUG | ||||
|     printf("Making fills for layer " PRINTF_ZU "\n", this->id()); | ||||
|     #endif | ||||
|     for (LayerRegion *layerm : m_regions) { | ||||
|         layerm->fills.clear(); | ||||
|         make_fill(*layerm, layerm->fills); | ||||
| #ifndef NDEBUG | ||||
|         for (size_t i = 0; i < layerm->fills.entities.size(); ++ i) | ||||
|             assert(dynamic_cast<ExtrusionEntityCollection*>(layerm->fills.entities[i]) != NULL); | ||||
| #endif | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Layer::export_region_slices_to_svg(const char *path) const | ||||
| { | ||||
|     BoundingBox bbox; | ||||
|  |  | |||
|  | @ -74,7 +74,7 @@ public: | |||
|             config(config), object_config(object_config), print_config(print_config), | ||||
|             loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces), | ||||
|             _ext_mm3_per_mm(-1), _mm3_per_mm(-1), _mm3_per_mm_overhang(-1) | ||||
|         {}; | ||||
|         {} | ||||
|     void process(); | ||||
| 
 | ||||
| private: | ||||
|  |  | |||
|  | @ -108,7 +108,6 @@ static inline bool opts_equal(const DynamicConfig &config_old, const DynamicConf | |||
| 
 | ||||
| std::vector<std::string> PlaceholderParser::config_diff(const DynamicPrintConfig &rhs) | ||||
| { | ||||
|     const ConfigDef *def = rhs.def(); | ||||
|     std::vector<std::string> diff_keys; | ||||
|     for (const t_config_option_key &opt_key : rhs.keys()) | ||||
|         if (! opts_equal(m_config, rhs, opt_key)) | ||||
|  | @ -124,7 +123,6 @@ std::vector<std::string> PlaceholderParser::config_diff(const DynamicPrintConfig | |||
| // a current extruder ID is used.
 | ||||
| bool PlaceholderParser::apply_config(const DynamicPrintConfig &rhs) | ||||
| { | ||||
|     const ConfigDef *def = rhs.def(); | ||||
|     bool modified = false; | ||||
|     for (const t_config_option_key &opt_key : rhs.keys()) { | ||||
|         if (! opts_equal(m_config, rhs, opt_key)) { | ||||
|  |  | |||
|  | @ -1228,24 +1228,35 @@ std::string Print::validate() const | |||
|             if (has_custom_layering) { | ||||
|                 const std::vector<coordf_t> &layer_height_profile_tallest = layer_height_profiles[tallest_object_idx]; | ||||
|                 for (size_t idx_object = 0; idx_object < m_objects.size(); ++ idx_object) { | ||||
|                     if (idx_object == tallest_object_idx) | ||||
|                         continue; | ||||
|                     const std::vector<coordf_t> &layer_height_profile = layer_height_profiles[idx_object]; | ||||
|                     bool                         failed               = false; | ||||
|                     if (layer_height_profile_tallest.size() >= layer_height_profile.size()) { | ||||
|                         size_t i = 0; | ||||
|                         while (i < layer_height_profile.size() && i < layer_height_profile_tallest.size()) { | ||||
|                             if (std::abs(layer_height_profile_tallest[i] - layer_height_profile[i])) { | ||||
|                                 failed = true; | ||||
|                                 break; | ||||
|                             } | ||||
|                             ++ i; | ||||
|                             if (i == layer_height_profile.size() - 2) // this element contains this objects max z
 | ||||
|                                 if (layer_height_profile_tallest[i] > layer_height_profile[i]) // the difference does not matter in this case
 | ||||
|                                     ++ i; | ||||
| 
 | ||||
|                     // The comparison of the profiles is not just about element-wise equality, some layers may not be
 | ||||
|                     // explicitely included. Always remember z and height of last reference layer that in the vector
 | ||||
|                     // and compare to that.
 | ||||
|                     size_t i = 0; // index into tested profile
 | ||||
|                     size_t j = 0; // index into reference profile
 | ||||
|                     coordf_t ref_z = -1.; | ||||
|                     coordf_t next_ref_z = layer_height_profile_tallest[0]; | ||||
|                     coordf_t ref_height = -1.; | ||||
|                     while (i < layer_height_profile.size()) { | ||||
|                         coordf_t this_z = layer_height_profile[i]; | ||||
|                         coordf_t this_height = layer_height_profile[i+1]; | ||||
|                         if (next_ref_z < this_z + EPSILON) { | ||||
|                             ref_z = next_ref_z; | ||||
|                             do { // one layer can be in the vector several times
 | ||||
|                                 ref_height = layer_height_profile_tallest[j+1]; | ||||
|                                 if (j+2 >= layer_height_profile_tallest.size()) | ||||
|                                     break; | ||||
|                                 j += 2; | ||||
|                                 next_ref_z = layer_height_profile_tallest[j]; | ||||
|                             } while (ref_z == next_ref_z); | ||||
|                         } | ||||
|                     } else | ||||
|                         failed = true; | ||||
|                     if (failed) | ||||
|                         return L("The Wipe tower is only supported if all objects have the same layer height profile"); | ||||
|                         if (std::abs(this_height - ref_height) > EPSILON) | ||||
|                             return L("The Wipe tower is only supported if all objects have the same layer height profile"); | ||||
|                         i += 2; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  |  | |||
|  | @ -39,6 +39,8 @@ class PrintRegion | |||
| public: | ||||
|     const Print*                print() const { return m_print; } | ||||
|     const PrintRegionConfig&    config() const { return m_config; } | ||||
| 	// 1-based extruder identifier for this region and role.
 | ||||
| 	unsigned int 				extruder(FlowRole role) const; | ||||
|     Flow                        flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const; | ||||
|     // Average diameter of nozzles participating on extruding this region.
 | ||||
|     coordf_t                    nozzle_dmr_avg(const PrintConfig &print_config) const; | ||||
|  |  | |||
|  | @ -2049,7 +2049,7 @@ void PrintConfigDef::init_fff_params() | |||
|     { | ||||
|         int threads = (unsigned int)boost::thread::hardware_concurrency(); | ||||
|         def->set_default_value(new ConfigOptionInt(threads > 0 ? threads : 2)); | ||||
|         def->cli == ConfigOptionDef::nocli; | ||||
|         def->cli = ConfigOptionDef::nocli; | ||||
|     } | ||||
| 
 | ||||
|     def = this->add("toolchange_gcode", coString); | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ | |||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| enum GCodeFlavor { | ||||
| enum GCodeFlavor : unsigned char { | ||||
|     gcfRepRap, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlin, gcfSailfish, gcfMach3, gcfMachinekit, | ||||
|     gcfSmoothie, gcfNoExtrusion, | ||||
| }; | ||||
|  | @ -35,7 +35,7 @@ enum PrintHostType { | |||
| 
 | ||||
| enum InfillPattern { | ||||
|     ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, | ||||
|     ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, | ||||
|     ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipCount, | ||||
| }; | ||||
| 
 | ||||
| enum SupportMaterialPattern { | ||||
|  |  | |||
|  | @ -1443,11 +1443,8 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c | |||
|         layer_height_profile.clear(); | ||||
| 
 | ||||
|     if (layer_height_profile.empty()) { | ||||
|     	if (0) | ||||
| //        if (this->layer_height_profile.empty())
 | ||||
|         	layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); | ||||
|         else | ||||
|         	layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges);     // #ys_FIXME_experiment
 | ||||
|             //layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
 | ||||
|             layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); | ||||
|        	updated = true; | ||||
|     } | ||||
|     return updated; | ||||
|  | @ -1870,12 +1867,13 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std | |||
| 			                	merge.assign(out.size(), false); | ||||
| 			                } else { | ||||
| 			                	for (size_t i = 0; i < out.size(); ++ i) | ||||
| 			                		if (! this_slices[i].empty()) | ||||
|                                     if (! this_slices[i].empty()) { | ||||
| 			                			if (! out[i].empty()) { | ||||
| 			                				append(out[i], this_slices[i]); | ||||
| 			                				merge[i] = true; | ||||
| 			                			} else | ||||
| 			                				out[i] = std::move(this_slices[i]); | ||||
|                                     } | ||||
| 			                } | ||||
| 							i = j; | ||||
| 						} else | ||||
|  |  | |||
|  | @ -2,6 +2,21 @@ | |||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| // 1-based extruder identifier for this region and role.
 | ||||
| unsigned int PrintRegion::extruder(FlowRole role) const | ||||
| { | ||||
|     size_t extruder = 0; | ||||
|     if (role == frPerimeter || role == frExternalPerimeter) | ||||
|         extruder = m_config.perimeter_extruder; | ||||
|     else if (role == frInfill) | ||||
|         extruder = m_config.infill_extruder; | ||||
|     else if (role == frSolidInfill || role == frTopSolidInfill) | ||||
|         extruder = m_config.solid_infill_extruder; | ||||
|     else | ||||
|         throw std::invalid_argument("Unknown role"); | ||||
|     return extruder; | ||||
| } | ||||
| 
 | ||||
| Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const | ||||
| { | ||||
|     ConfigOptionFloatOrPercent config_width; | ||||
|  | @ -28,24 +43,13 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir | |||
|             throw std::invalid_argument("Unknown role"); | ||||
|         } | ||||
|     } | ||||
|     if (config_width.value == 0) { | ||||
| 
 | ||||
|     if (config_width.value == 0) | ||||
|         config_width = object.config().extrusion_width; | ||||
|     } | ||||
|      | ||||
|     // get the configured nozzle_diameter for the extruder associated
 | ||||
|     // to the flow role requested
 | ||||
|     size_t extruder = 0;    // 1-based
 | ||||
|     if (role == frPerimeter || role == frExternalPerimeter) { | ||||
|         extruder = m_config.perimeter_extruder; | ||||
|     } else if (role == frInfill) { | ||||
|         extruder = m_config.infill_extruder; | ||||
|     } else if (role == frSolidInfill || role == frTopSolidInfill) { | ||||
|         extruder = m_config.solid_infill_extruder; | ||||
|     } else { | ||||
|         throw std::invalid_argument("Unknown role"); | ||||
|     } | ||||
|     double nozzle_diameter = m_print->config().nozzle_diameter.get_at(extruder-1); | ||||
|      | ||||
|     // Get the configured nozzle_diameter for the extruder associated to the flow role requested.
 | ||||
|     // Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
 | ||||
|     double nozzle_diameter = m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1); | ||||
|     return Flow::new_from_config_width(role, config_width, (float)nozzle_diameter, (float)layer_height, bridge ? (float)m_config.bridge_flow_ratio : 0.0f); | ||||
| } | ||||
| 
 | ||||
|  | @ -79,12 +83,14 @@ void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_con | |||
| 
 | ||||
| void PrintRegion::collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const | ||||
| { | ||||
|     auto num_extruders = (int)print()->config().nozzle_diameter.size(); | ||||
|     // PrintRegion, if used by some PrintObject, shall have all the extruders set to an existing printer extruder.
 | ||||
|     // If not, then there must be something wrong with the Print::apply() function.
 | ||||
| #ifndef NDEBUG | ||||
|     auto num_extruders = (int)print()->config().nozzle_diameter.size(); | ||||
|     assert(this->config().perimeter_extruder    <= num_extruders); | ||||
|     assert(this->config().infill_extruder       <= num_extruders); | ||||
|     assert(this->config().solid_infill_extruder <= num_extruders); | ||||
| #endif | ||||
|     collect_object_printing_extruders(print()->config(), this->config(), object_extruders); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -185,8 +185,6 @@ private: | |||
| 
 | ||||
|     SLAAutoSupports::Config m_config; | ||||
| 
 | ||||
|     float m_supports_force_total = 0.f; | ||||
| 
 | ||||
|     void process(const std::vector<ExPolygons>& slices, const std::vector<float>& heights); | ||||
|     void uniformly_cover(const ExPolygons& islands, Structure& structure, PointGrid3D &grid3d, bool is_new_island = false, bool just_one = false); | ||||
|     void project_onto_mesh(std::vector<sla::SupportPoint>& points) const; | ||||
|  |  | |||
|  | @ -676,7 +676,7 @@ std::string SLAPrint::validate() const | |||
|         if(supports_en && !builtinpad.enabled && elv < pinhead_width ) | ||||
|             return L( | ||||
|                 "Elevation is too low for object. Use the \"Pad around " | ||||
|                 "obect\" feature to print the object without elevation."); | ||||
|                 "object\" feature to print the object without elevation."); | ||||
|          | ||||
|         if(supports_en && builtinpad.enabled && | ||||
|            cfg.pillar_base_safety_distance_mm < builtinpad.object_gap_mm) { | ||||
|  |  | |||
|  | @ -24,9 +24,8 @@ enum SurfaceType { | |||
|     stInternalVoid, | ||||
|     // Inner/outer perimeters.
 | ||||
|     stPerimeter, | ||||
|     // Last surface type, if the SurfaceType is used as an index into a vector.
 | ||||
|     stLast, | ||||
|     stCount = stLast + 1 | ||||
|     // Number of SurfaceType enums.
 | ||||
|     stCount, | ||||
| }; | ||||
| 
 | ||||
| class Surface | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ public: | |||
| 
 | ||||
|     void clear() { surfaces.clear(); } | ||||
|     bool empty() const { return surfaces.empty(); } | ||||
| 	size_t size() const { return surfaces.size(); } | ||||
|     bool has(SurfaceType type) const {  | ||||
|         for (const Surface &surface : this->surfaces)  | ||||
|             if (surface.surface_type == type) return true; | ||||
|  |  | |||
|  | @ -1142,7 +1142,7 @@ void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const | |||
|         Color color; | ||||
|         ::memcpy((void*)color.data(), (const void*)volume->color, 4 * sizeof(float)); | ||||
|         fprintf(fp, "\n# material volume %d\n", volumes_count); | ||||
|         fprintf(fp, "usemtl material_%lld\n", 1 + std::distance(colors.begin(), colors.find(color))); | ||||
|         fprintf(fp, "usemtl material_%lld\n", (long long)(1 + std::distance(colors.begin(), colors.find(color)))); | ||||
| 
 | ||||
|         int base_vertex_id = vertices_count + 1; | ||||
|         int base_normal_id = normals_count + 1; | ||||
|  | @ -1242,8 +1242,8 @@ static void thick_lines_to_indexed_vertex_array( | |||
|         // calculate new XY normals
 | ||||
|         Vec2d xy_right_normal = unscale(line.normal()).normalized(); | ||||
| 
 | ||||
|         int idx_a[4]; | ||||
|         int idx_b[4]; | ||||
|         int idx_a[4] = { 0, 0, 0, 0 }; // initialized to avoid warnings
 | ||||
|         int idx_b[4] = { 0, 0, 0, 0 }; // initialized to avoid warnings
 | ||||
|         int idx_last = int(volume.vertices_and_normals_interleaved.size() / 6); | ||||
| 
 | ||||
|         bool bottom_z_different = bottom_z_prev != bottom_z; | ||||
|  |  | |||
|  | @ -446,7 +446,7 @@ void BedShapePanel::update_shape() | |||
| 		auto twopi = 2 * PI; | ||||
|         auto edges = 72; | ||||
|         std::vector<Vec2d> points; | ||||
|         for (size_t i = 1; i <= edges; ++i) { | ||||
|         for (int i = 1; i <= edges; ++i) { | ||||
|             auto angle = i * twopi / edges; | ||||
| 			points.push_back(Vec2d(r*cos(angle), r*sin(angle))); | ||||
| 		} | ||||
|  |  | |||
|  | @ -197,7 +197,7 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true | |||
|                 show_error(m_parent, _(L("Invalid numeric input."))); | ||||
|                 set_value(double_to_string(val), true); | ||||
|             } | ||||
|             else if (check_value && (m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max || | ||||
|             else if (check_value && ((m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max) || | ||||
|                      m_opt.sidetext.rfind("mm ") != std::string::npos && val > 1) &&  | ||||
|                      (m_value.empty() || std::string(str.ToUTF8().data()) != boost::any_cast<std::string>(m_value))) | ||||
|             { | ||||
|  | @ -444,7 +444,7 @@ void TextCtrl::disable() { dynamic_cast<wxTextCtrl*>(window)->Disable(); dynamic | |||
| #ifdef __WXGTK__ | ||||
| void TextCtrl::change_field_value(wxEvent& event) | ||||
| { | ||||
| 	if (bChangedValueEvent = event.GetEventType()==wxEVT_KEY_UP) | ||||
| 	if (bChangedValueEvent = (event.GetEventType()==wxEVT_KEY_UP)) | ||||
| 		on_change_field(); | ||||
|     event.Skip(); | ||||
| }; | ||||
|  | @ -768,7 +768,7 @@ void Choice::set_selection() | |||
| 		size_t idx = 0; | ||||
| 		for (auto el : m_opt.enum_values) | ||||
| 		{ | ||||
| 			if (el.compare(text_value) == 0) | ||||
| 			if (el == text_value) | ||||
| 				break; | ||||
| 			++idx; | ||||
| 		} | ||||
|  | @ -789,7 +789,7 @@ void Choice::set_selection() | |||
| 		size_t idx = 0; | ||||
| 		for (auto el : m_opt.enum_values) | ||||
| 		{ | ||||
| 			if (el.compare(text_value) == 0) | ||||
| 			if (el == text_value) | ||||
| 				break; | ||||
| 			++idx; | ||||
| 		} | ||||
|  | @ -804,7 +804,7 @@ void Choice::set_selection() | |||
| 		size_t idx = 0; | ||||
| 		for (auto el : m_opt.enum_values) | ||||
| 		{ | ||||
| 			if (el.compare(text_value) == 0) | ||||
| 			if (el == text_value) | ||||
| 				break; | ||||
| 			++idx; | ||||
| 		} | ||||
|  | @ -813,6 +813,7 @@ void Choice::set_selection() | |||
| 			field->SetSelection(idx); | ||||
| 		break; | ||||
| 	} | ||||
|     default: break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -823,7 +824,7 @@ void Choice::set_value(const std::string& value, bool change_event)  //! Redunda | |||
| 	size_t idx=0; | ||||
| 	for (auto el : m_opt.enum_values) | ||||
| 	{ | ||||
| 		if (el.compare(value) == 0) | ||||
| 		if (el == value) | ||||
| 			break; | ||||
| 		++idx; | ||||
| 	} | ||||
|  | @ -856,7 +857,7 @@ void Choice::set_value(const boost::any& value, bool change_event) | |||
| 		auto idx = 0; | ||||
| 		for (auto el : m_opt.enum_values) | ||||
| 		{ | ||||
| 			if (el.compare(text_value) == 0) | ||||
| 			if (el == text_value) | ||||
| 				break; | ||||
| 			++idx; | ||||
| 		} | ||||
|  | @ -887,7 +888,7 @@ void Choice::set_value(const boost::any& value, bool change_event) | |||
| 				size_t idx = 0; | ||||
| 				for (auto el : m_opt.enum_values) | ||||
| 				{ | ||||
| 					if (el.compare(key) == 0) | ||||
| 					if (el == key) | ||||
| 						break; | ||||
| 					++idx; | ||||
| 				} | ||||
|  |  | |||
|  | @ -845,22 +845,23 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_values(const GCodePrevie | |||
|     { | ||||
|         auto& config = wxGetApp().preset_bundle->project_config; | ||||
|         const std::vector<double>& color_print_values = config.option<ConfigOptionFloats>("colorprint_heights")->values; | ||||
|         const size_t values_cnt = color_print_values.size(); | ||||
|         if (values_cnt > 0) { | ||||
|          | ||||
|         if (!color_print_values.empty()) { | ||||
|             std::vector<double> print_zs = canvas.get_current_print_zs(true); | ||||
|             size_t z = 0; | ||||
|             for (size_t i = 0; i < values_cnt; ++i) | ||||
|             for (auto cp_value : color_print_values) | ||||
|             { | ||||
|                 double prev_z = -1.0; | ||||
|                 for ( ; z < print_zs.size(); ++z) | ||||
|                     if (fabs(color_print_values[i] - print_zs[z]) < EPSILON) { | ||||
|                         prev_z = z > 0 ? print_zs[z - 1] : 0.; | ||||
|                         break; | ||||
|                     } | ||||
|                 if (prev_z < 0) | ||||
|                 auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), cp_value - DoubleSlider::epsilon()); | ||||
| 
 | ||||
|                 if (lower_b == print_zs.end()) | ||||
|                     continue; | ||||
| 
 | ||||
|                 cp_legend_values.push_back(std::pair<double, double>(prev_z, color_print_values[i])); | ||||
|                 double current_z    = *lower_b; | ||||
|                 double previous_z   = lower_b == print_zs.begin() ? 0.0 : *(--lower_b); | ||||
| 
 | ||||
|                 // to avoid duplicate values, check adding values
 | ||||
|                 if (cp_legend_values.empty() ||  | ||||
|                     !(cp_legend_values.back().first == previous_z && cp_legend_values.back().second == current_z) ) | ||||
|                     cp_legend_values.push_back(std::pair<double, double>(previous_z, current_z)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -901,11 +902,11 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c | |||
| 
 | ||||
|     // Disabling ClearType works, but the font returned is very different (much thicker) from the default.
 | ||||
| //    msw_disable_cleartype(font);
 | ||||
|     bool cleartype = is_font_cleartype(font); | ||||
| //    bool cleartype = is_font_cleartype(font);
 | ||||
| #else | ||||
|     // select default font
 | ||||
|     wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Scale(scale_gl); | ||||
|     bool cleartype = false; | ||||
| //    bool cleartype = false;
 | ||||
| #endif /* __WXMSW__ */ | ||||
| 
 | ||||
|     memDC.SetFont(font); | ||||
|  | @ -1330,13 +1331,13 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject | |||
| void GLCanvas3D::update_instance_printable_state_for_object(const size_t obj_idx) | ||||
| { | ||||
|     ModelObject* model_object = m_model->objects[obj_idx]; | ||||
|     for (int inst_idx = 0; inst_idx < model_object->instances.size(); inst_idx++) | ||||
|     for (int inst_idx = 0; inst_idx < (int)model_object->instances.size(); ++inst_idx) | ||||
|     { | ||||
|         ModelInstance* instance = model_object->instances[inst_idx]; | ||||
| 
 | ||||
|         for (GLVolume* volume : m_volumes.volumes) | ||||
|         { | ||||
|             if ((volume->object_idx() == obj_idx) && (volume->instance_idx() == inst_idx)) | ||||
|             if ((volume->object_idx() == (int)obj_idx) && (volume->instance_idx() == inst_idx)) | ||||
|                 volume->printable = instance->printable; | ||||
|         } | ||||
|     } | ||||
|  | @ -1672,7 +1673,7 @@ void GLCanvas3D::ensure_on_bed(unsigned int object_idx) | |||
| 
 | ||||
|     for (GLVolume* volume : m_volumes.volumes) | ||||
|     { | ||||
|         if ((volume->object_idx() == object_idx) && !volume->is_modifier) | ||||
|         if ((volume->object_idx() == (int)object_idx) && !volume->is_modifier) | ||||
|         { | ||||
|             double min_z = volume->transformed_convex_hull_bounding_box().min(2); | ||||
|             std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx()); | ||||
|  | @ -1968,8 +1969,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
|                     } | ||||
|             } | ||||
| 
 | ||||
|             // stores the current volumes count
 | ||||
|             size_t volumes_count = m_volumes.volumes.size(); | ||||
| //            // stores the current volumes count
 | ||||
| //            size_t volumes_count = m_volumes.volumes.size();
 | ||||
| 
 | ||||
|             for (size_t istep = 0; istep < sla_steps.size(); ++istep) | ||||
|                 if (!instances[istep].empty()) | ||||
|  | @ -1978,7 +1979,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
| 
 | ||||
| 		// Shift-up all volumes of the object so that it has the right elevation with respect to the print bed
 | ||||
| 		for (GLVolume* volume : m_volumes.volumes) | ||||
| 			if (volume->object_idx() < m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) | ||||
| 			if (volume->object_idx() < (int)m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) | ||||
| 				volume->set_sla_shift_z(shift_zs[volume->object_idx()]); | ||||
|     } | ||||
| 
 | ||||
|  | @ -3733,7 +3734,7 @@ bool GLCanvas3D::_init_undoredo_toolbar() | |||
|     item.right.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(true, 0.5f * (left + right)); }; | ||||
|     item.enabling_callback = [this]()->bool { | ||||
|         bool can_undo = wxGetApp().plater()->can_undo(); | ||||
|         unsigned int id = m_undoredo_toolbar.get_item_id("undo"); | ||||
|         int id = m_undoredo_toolbar.get_item_id("undo"); | ||||
| 
 | ||||
|         std::string curr_additional_tooltip; | ||||
|         m_undoredo_toolbar.get_additional_tooltip(id, curr_additional_tooltip); | ||||
|  | @ -3765,7 +3766,7 @@ bool GLCanvas3D::_init_undoredo_toolbar() | |||
|     item.right.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(false, 0.5f * (left + right)); }; | ||||
|     item.enabling_callback = [this]()->bool { | ||||
|         bool can_redo = wxGetApp().plater()->can_redo(); | ||||
|         unsigned int id = m_undoredo_toolbar.get_item_id("redo"); | ||||
|         int id = m_undoredo_toolbar.get_item_id("redo"); | ||||
| 
 | ||||
|         std::string curr_additional_tooltip; | ||||
|         m_undoredo_toolbar.get_additional_tooltip(id, curr_additional_tooltip); | ||||
|  | @ -3908,7 +3909,7 @@ void GLCanvas3D::_picking_pass() const | |||
|             m_gizmos.set_hover_id(-1); | ||||
|         } | ||||
|         else | ||||
|             m_gizmos.set_hover_id(inside && volume_id <= GLGizmoBase::BASE_ID ? (GLGizmoBase::BASE_ID - volume_id) : -1); | ||||
|             m_gizmos.set_hover_id(inside && volume_id <= (int)GLGizmoBase::BASE_ID ? ((int)GLGizmoBase::BASE_ID - volume_id) : -1); | ||||
| 
 | ||||
|         _update_volumes_hover_state(); | ||||
|     } | ||||
|  | @ -4858,7 +4859,7 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_ | |||
|     ctxt.print = print; | ||||
|     ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; | ||||
|     if (print->wipe_tower_data().priming && print->config().single_extruder_multi_material_priming) | ||||
|         for (int i=0; i<print->wipe_tower_data().priming.get()->size(); ++i) | ||||
|         for (int i=0; i<(int)print->wipe_tower_data().priming.get()->size(); ++i) | ||||
|             ctxt.priming.emplace_back(print->wipe_tower_data().priming.get()->at(i)); | ||||
|     if (print->wipe_tower_data().final_purge) | ||||
|         ctxt.final.emplace_back(*print->wipe_tower_data().final_purge.get()); | ||||
|  | @ -5037,7 +5038,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat | |||
|             } | ||||
|             case GCodePreviewData::Extrusion::ColorPrint: | ||||
|             { | ||||
|                 const size_t color_cnt = tool_colors.size() / 4; | ||||
|                 int color_cnt = (int)tool_colors.size() / 4; | ||||
| 
 | ||||
|                 int val = int(value); | ||||
|                 while (val >= color_cnt) | ||||
|  |  | |||
|  | @ -641,11 +641,9 @@ public: | |||
|     void start_keeping_dirty() { m_keep_dirty = true; } | ||||
|     void stop_keeping_dirty() { m_keep_dirty = false; } | ||||
| 
 | ||||
|     unsigned int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); } | ||||
|     void force_main_toolbar_left_action(unsigned int item_id) { m_main_toolbar.force_left_action(item_id, *this); } | ||||
|     void force_main_toolbar_right_action(unsigned int item_id) { m_main_toolbar.force_right_action(item_id, *this); } | ||||
|     void get_undoredo_toolbar_additional_tooltip(unsigned int item_id, std::string& text) { return m_undoredo_toolbar.get_additional_tooltip(item_id, text); } | ||||
|     void set_undoredo_toolbar_additional_tooltip(unsigned int item_id, const std::string& text) { m_undoredo_toolbar.set_additional_tooltip(item_id, text); } | ||||
|     int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); } | ||||
|     void force_main_toolbar_left_action(int item_id) { m_main_toolbar.force_left_action(item_id, *this); } | ||||
|     void force_main_toolbar_right_action(int item_id) { m_main_toolbar.force_right_action(item_id, *this); } | ||||
| 
 | ||||
|     bool has_toolpaths_to_export() const; | ||||
|     void export_toolpaths_to_obj(const char* filename) const; | ||||
|  |  | |||
|  | @ -359,9 +359,7 @@ bool GLCanvas3DManager::init(GLCanvas3D& canvas) | |||
| void GLCanvas3DManager::detect_multisample(int* attribList) | ||||
| { | ||||
|     int wxVersion = wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + wxRELEASE_NUMBER; | ||||
|     const AppConfig* app_config = GUI::get_app_config(); | ||||
|     bool enable_multisample = wxVersion >= 30003; | ||||
| 
 | ||||
|     s_multisample = (enable_multisample && wxGLCanvas::IsDisplaySupported(attribList)) ? MS_Enabled : MS_Disabled; | ||||
|     // Alternative method: it was working on previous version of wxWidgets but not with the latest, at least on Windows
 | ||||
|     // s_multisample = enable_multisample && wxGLCanvas::IsExtensionSupported("WGL_ARB_multisample");
 | ||||
|  |  | |||
|  | @ -56,7 +56,7 @@ bool GLTexture::Compressor::unsent_compressed_data_available() const | |||
| { | ||||
| 	if (m_levels.empty()) | ||||
| 		return false; | ||||
| 	// Querying the atomic m_num_levels_compressed value synchronizes processor caches, so that the dat of m_levels modified by the worker thread are accessible to the calling thread.
 | ||||
| 	// Querying the atomic m_num_levels_compressed value synchronizes processor caches, so that the data of m_levels modified by the worker thread are accessible to the calling thread.
 | ||||
| 	unsigned int num_compressed = m_num_levels_compressed; | ||||
| 	for (unsigned int i = 0; i < num_compressed; ++ i) | ||||
|         if (! m_levels[i].sent_to_gpu && ! m_levels[i].compressed_data.empty()) | ||||
|  | @ -89,8 +89,8 @@ void GLTexture::Compressor::send_compressed_data_to_gpu() | |||
|     } | ||||
|     glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); | ||||
| 
 | ||||
|     if (num_compressed == (unsigned int)m_levels.size()) | ||||
|     	// Finalize the worker thread, close it.
 | ||||
|     if (num_compressed == (int)m_levels.size()) | ||||
|         // Finalize the worker thread, close it.
 | ||||
|     	this->reset(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -345,9 +345,9 @@ bool GLToolbar::is_any_item_pressed() const | |||
|     return false; | ||||
| } | ||||
| 
 | ||||
| unsigned int GLToolbar::get_item_id(const std::string& name) const | ||||
| int GLToolbar::get_item_id(const std::string& name) const | ||||
| { | ||||
|     for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) | ||||
|     for (int i = 0; i < (int)m_items.size(); ++i) | ||||
|     { | ||||
|         if (m_items[i]->get_name() == name) | ||||
|             return i; | ||||
|  | @ -356,19 +356,9 @@ unsigned int GLToolbar::get_item_id(const std::string& name) const | |||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| void GLToolbar::force_left_action(unsigned int item_id, GLCanvas3D& parent) | ||||
| void GLToolbar::get_additional_tooltip(int item_id, std::string& text) | ||||
| { | ||||
|     do_action(GLToolbarItem::Left, item_id, parent, false); | ||||
| } | ||||
| 
 | ||||
| void GLToolbar::force_right_action(unsigned int item_id, GLCanvas3D& parent) | ||||
| { | ||||
|     do_action(GLToolbarItem::Right, item_id, parent, false); | ||||
| } | ||||
| 
 | ||||
| void GLToolbar::get_additional_tooltip(unsigned int item_id, std::string& text) | ||||
| { | ||||
|     if (item_id < (unsigned int)m_items.size()) | ||||
|     if ((0 <= item_id) && (item_id < (int)m_items.size())) | ||||
|     { | ||||
|         GLToolbarItem* item = m_items[item_id]; | ||||
|         if (item != nullptr) | ||||
|  | @ -381,9 +371,9 @@ void GLToolbar::get_additional_tooltip(unsigned int item_id, std::string& text) | |||
|     text = L(""); | ||||
| } | ||||
| 
 | ||||
| void GLToolbar::set_additional_tooltip(unsigned int item_id, const std::string& text) | ||||
| void GLToolbar::set_additional_tooltip(int item_id, const std::string& text) | ||||
| { | ||||
|     if (item_id < (unsigned int)m_items.size()) | ||||
|     if ((0 <= item_id) && (item_id < (int)m_items.size())) | ||||
|     { | ||||
|         GLToolbarItem* item = m_items[item_id]; | ||||
|         if (item != nullptr) | ||||
|  | @ -466,7 +456,7 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) | |||
|             if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Left))) | ||||
|             { | ||||
|                 // mouse is inside an icon
 | ||||
|                 do_action(GLToolbarItem::Left, (unsigned int)item_id, parent, true); | ||||
|                 do_action(GLToolbarItem::Left, item_id, parent, true); | ||||
|                 parent.set_as_dirty(); | ||||
|             } | ||||
|         } | ||||
|  | @ -483,7 +473,7 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) | |||
|             if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Right))) | ||||
|             { | ||||
|                 // mouse is inside an icon
 | ||||
|                 do_action(GLToolbarItem::Right, (unsigned int)item_id, parent, true); | ||||
|                 do_action(GLToolbarItem::Right, item_id, parent, true); | ||||
|                 parent.set_as_dirty(); | ||||
|             } | ||||
|         } | ||||
|  | @ -556,11 +546,11 @@ float GLToolbar::get_main_size() const | |||
|     return size * m_layout.scale; | ||||
| } | ||||
| 
 | ||||
| void GLToolbar::do_action(GLToolbarItem::EActionType type, unsigned int item_id, GLCanvas3D& parent, bool check_hover) | ||||
| void GLToolbar::do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas3D& parent, bool check_hover) | ||||
| { | ||||
|     if ((m_pressed_toggable_id == -1) || (m_pressed_toggable_id == item_id)) | ||||
|     { | ||||
|         if (item_id < (unsigned int)m_items.size()) | ||||
|         if ((0 <= item_id) && (item_id < (int)m_items.size())) | ||||
|         { | ||||
|             GLToolbarItem* item = m_items[item_id]; | ||||
|             if ((item != nullptr) && !item->is_separator() && (!check_hover || item->is_hovered())) | ||||
|  | @ -1246,7 +1236,7 @@ bool GLToolbar::update_items_enabled_state() | |||
| { | ||||
|     bool ret = false; | ||||
| 
 | ||||
|     for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) | ||||
|     for (int i = 0; i < (int)m_items.size(); ++i) | ||||
|     { | ||||
|         GLToolbarItem* item = m_items[i]; | ||||
|         ret |= item->update_enabled_state(); | ||||
|  |  | |||
|  | @ -254,7 +254,7 @@ private: | |||
| 
 | ||||
|     MouseCapture m_mouse_capture; | ||||
|     std::string m_tooltip; | ||||
|     unsigned int m_pressed_toggable_id; | ||||
|     int m_pressed_toggable_id; | ||||
| 
 | ||||
| public: | ||||
|     GLToolbar(EType type, const std::string& name); | ||||
|  | @ -293,15 +293,15 @@ public: | |||
| 
 | ||||
|     bool is_any_item_pressed() const; | ||||
| 
 | ||||
|     unsigned int get_item_id(const std::string& name) const; | ||||
|     int get_item_id(const std::string& name) const; | ||||
| 
 | ||||
|     void force_left_action(unsigned int item_id, GLCanvas3D& parent); | ||||
|     void force_right_action(unsigned int item_id, GLCanvas3D& parent); | ||||
|     void force_left_action(int item_id, GLCanvas3D& parent) { do_action(GLToolbarItem::Left, item_id, parent, false); } | ||||
|     void force_right_action(int item_id, GLCanvas3D& parent) { do_action(GLToolbarItem::Right, item_id, parent, false); } | ||||
| 
 | ||||
|     const std::string& get_tooltip() const { return m_tooltip; } | ||||
| 
 | ||||
|     void get_additional_tooltip(unsigned int item_id, std::string& text); | ||||
|     void set_additional_tooltip(unsigned int item_id, const std::string& text); | ||||
|     void get_additional_tooltip(int item_id, std::string& text); | ||||
|     void set_additional_tooltip(int item_id, const std::string& text); | ||||
| 
 | ||||
|     // returns true if any item changed its state
 | ||||
|     bool update_items_state(); | ||||
|  | @ -317,7 +317,7 @@ private: | |||
|     float get_height_horizontal() const; | ||||
|     float get_height_vertical() const; | ||||
|     float get_main_size() const; | ||||
|     void do_action(GLToolbarItem::EActionType type, unsigned int item_id, GLCanvas3D& parent, bool check_hover); | ||||
|     void do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas3D& parent, bool check_hover); | ||||
|     std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); | ||||
|     std::string update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent); | ||||
|     std::string update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent); | ||||
|  |  | |||
|  | @ -262,7 +262,7 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt | |||
| 	} | ||||
| 	catch (const std::exception & /* e */) | ||||
| 	{ | ||||
| 		int i = 0;//no reason, just experiment
 | ||||
| 		// int i = 0;//no reason, just experiment
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -302,8 +302,9 @@ bool GUI_App::on_init_inner() | |||
|      * change min hight of object list to the normal min value (15 * wxGetApp().em_unit())  | ||||
|      * after first whole Mainframe updating/layouting | ||||
|      */ | ||||
|     if (obj_list()->GetMinSize().GetY() > 15 * em_unit()) | ||||
|         obj_list()->SetMinSize(wxSize(-1, 15 * em_unit())); | ||||
|     const int list_min_height = 15 * em_unit(); | ||||
|     if (obj_list()->GetMinSize().GetY() > list_min_height) | ||||
|         obj_list()->SetMinSize(wxSize(-1, list_min_height)); | ||||
| 
 | ||||
|     update_mode(); // update view mode after fix of the object_list size
 | ||||
| 
 | ||||
|  | @ -474,8 +475,9 @@ void GUI_App::recreate_GUI() | |||
|     * change min hight of object list to the normal min value (15 * wxGetApp().em_unit()) | ||||
|     * after first whole Mainframe updating/layouting | ||||
|     */ | ||||
|     if (obj_list()->GetMinSize().GetY() > 15 * em_unit()) | ||||
|         obj_list()->SetMinSize(wxSize(-1, 15 * em_unit())); | ||||
|     const int list_min_height = 15 * em_unit(); | ||||
|     if (obj_list()->GetMinSize().GetY() > list_min_height) | ||||
|         obj_list()->SetMinSize(wxSize(-1, list_min_height)); | ||||
| 
 | ||||
|     update_mode(); | ||||
| 
 | ||||
|  | @ -908,11 +910,13 @@ bool GUI_App::check_unsaved_changes(const wxString &header) | |||
|     wxString dirty; | ||||
|     PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); | ||||
|     for (Tab *tab : tabs_list) | ||||
|         if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) | ||||
|         if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) { | ||||
|             if (dirty.empty()) | ||||
|                 dirty = tab->title(); | ||||
|             else | ||||
|                 dirty += wxString(", ") + tab->title(); | ||||
|         } | ||||
| 
 | ||||
|     if (dirty.empty()) | ||||
|         // No changes, the application may close or reload presets.
 | ||||
|         return true; | ||||
|  | @ -1085,7 +1089,7 @@ void GUI_App::window_pos_restore(wxTopLevelWindow* window, const std::string &na | |||
| 
 | ||||
| void GUI_App::window_pos_sanitize(wxTopLevelWindow* window) | ||||
| { | ||||
|     unsigned display_idx = wxDisplay::GetFromWindow(window); | ||||
|     /*unsigned*/int display_idx = wxDisplay::GetFromWindow(window); | ||||
|     wxRect display; | ||||
|     if (display_idx == wxNOT_FOUND) { | ||||
|         display = wxDisplay(0u).GetClientArea(); | ||||
|  |  | |||
|  | @ -29,7 +29,7 @@ namespace GUI | |||
| wxDEFINE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent); | ||||
| 
 | ||||
| // pt_FFF
 | ||||
| SettingsBundle FREQ_SETTINGS_BUNDLE_FFF = | ||||
| static SettingsBundle FREQ_SETTINGS_BUNDLE_FFF = | ||||
| { | ||||
|     { L("Layers and Perimeters"), { "layer_height" , "perimeters", "top_solid_layers", "bottom_solid_layers" } }, | ||||
|     { L("Infill")               , { "fill_density", "fill_pattern" } }, | ||||
|  | @ -40,13 +40,13 @@ SettingsBundle FREQ_SETTINGS_BUNDLE_FFF = | |||
| }; | ||||
| 
 | ||||
| // pt_SLA
 | ||||
| SettingsBundle FREQ_SETTINGS_BUNDLE_SLA = | ||||
| static SettingsBundle FREQ_SETTINGS_BUNDLE_SLA = | ||||
| { | ||||
|     { L("Pad and Support")      , { "supports_enable", "pad_enable" } } | ||||
| }; | ||||
| 
 | ||||
| // Note: id accords to type of the sub-object (adding volume), so sequence of the menu items is important
 | ||||
| std::vector<std::pair<std::string, std::string>> ADD_VOLUME_MENU_ITEMS = {  | ||||
| static std::vector<std::pair<std::string, std::string>> ADD_VOLUME_MENU_ITEMS = { | ||||
| //     menu_item Name            menu_item bitmap name
 | ||||
|     {L("Add part"),              "add_part" },           // ~ModelVolumeType::MODEL_PART
 | ||||
|     {L("Add modifier"),          "add_modifier"},        // ~ModelVolumeType::PARAMETER_MODIFIER
 | ||||
|  | @ -126,7 +126,7 @@ ObjectList::ObjectList(wxWindow* parent) : | |||
| #ifdef __WXMSW__ | ||||
| 		// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
 | ||||
| 		int new_selected_column = -1; | ||||
| #endif __WXMSW__ | ||||
| #endif //__WXMSW__
 | ||||
|         if (wxGetKeyState(WXK_SHIFT)) | ||||
|         { | ||||
|             wxDataViewItemArray sels; | ||||
|  | @ -149,12 +149,12 @@ ObjectList::ObjectList(wxWindow* parent) : | |||
| 	        	wxUIActionSimulator sim; | ||||
| 				sim.Char(WXK_RETURN); | ||||
| 	        } | ||||
| #endif __WXMSW__ | ||||
| #endif //__WXMSW__
 | ||||
| 	        m_last_selected_item = new_selected_item; | ||||
|         } | ||||
| #ifdef __WXMSW__ | ||||
|         m_last_selected_column = new_selected_column; | ||||
| #endif __WXMSW__ | ||||
| #endif //__WXMSW__
 | ||||
| 
 | ||||
|         selection_changed(); | ||||
| #ifndef __WXMSW__ | ||||
|  | @ -192,7 +192,7 @@ ObjectList::ObjectList(wxWindow* parent) : | |||
|         this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->undo();  					}, wxID_UNDO); | ||||
|         this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->redo();                    	}, wxID_REDO); | ||||
|     } | ||||
| #else __WXOSX__ | ||||
| #else //__WXOSX__
 | ||||
|     Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX
 | ||||
| #endif | ||||
| 
 | ||||
|  | @ -281,9 +281,9 @@ void ObjectList::create_popup_menus() | |||
|     create_instance_popupmenu(&m_menu_instance); | ||||
| } | ||||
| 
 | ||||
| void ObjectList::get_selected_item_indexes(int& obj_idx, int& vol_idx, const wxDataViewItem& input_item/* = wxDataViewItem(0)*/) | ||||
| void ObjectList::get_selected_item_indexes(int& obj_idx, int& vol_idx, const wxDataViewItem& input_item/* = wxDataViewItem(nullptr)*/) | ||||
| { | ||||
|     const wxDataViewItem item = input_item == wxDataViewItem(0) ? GetSelection() : input_item; | ||||
|     const wxDataViewItem item = input_item == wxDataViewItem(nullptr) ? GetSelection() : input_item; | ||||
| 
 | ||||
|     if (!item) | ||||
|     { | ||||
|  | @ -737,7 +737,7 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol | |||
|     if (items.size() > 1) | ||||
|     { | ||||
|         m_selection_mode = smVolume; | ||||
|         m_last_selected_item = wxDataViewItem(0); | ||||
|         m_last_selected_item = wxDataViewItem(nullptr); | ||||
|     } | ||||
| 
 | ||||
|     select_items(items); | ||||
|  | @ -844,7 +844,7 @@ void ObjectList::show_context_menu() | |||
| 
 | ||||
|         wxMenu* menu = type & itInstance ? &m_menu_instance : | ||||
|                        type & itLayer ? &m_menu_layer : | ||||
|                        m_objects_model->GetParent(item) != wxDataViewItem(0) ? &m_menu_part : | ||||
|                        m_objects_model->GetParent(item) != wxDataViewItem(nullptr) ? &m_menu_part : | ||||
|                        printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object; | ||||
| 
 | ||||
|         if (!(type & itInstance)) | ||||
|  | @ -915,9 +915,9 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event) | |||
| 
 | ||||
|     const bool mult_sel = multiple_selection(); | ||||
| 
 | ||||
|     if (mult_sel && !selected_instances_of_same_object() || | ||||
|         !mult_sel && (GetSelection() != item || | ||||
|         m_objects_model->GetParent(item) == wxDataViewItem(0) ) ) { | ||||
|     if ((mult_sel && !selected_instances_of_same_object()) || | ||||
|         (!mult_sel && (GetSelection() != item)) || | ||||
|         m_objects_model->GetParent(item) == wxDataViewItem(nullptr) ) { | ||||
|         event.Veto(); | ||||
|         return; | ||||
|     } | ||||
|  | @ -962,10 +962,10 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event) | |||
| 
 | ||||
| bool ObjectList::can_drop(const wxDataViewItem& item) const  | ||||
| { | ||||
|     return  m_dragged_data.type() == itInstance && !item.IsOk()     || | ||||
|             m_dragged_data.type() == itVolume && item.IsOk() && | ||||
|     return (m_dragged_data.type() == itInstance && !item.IsOk())     || | ||||
|            (m_dragged_data.type() == itVolume && item.IsOk() && | ||||
|             m_objects_model->GetItemType(item) == itVolume && | ||||
|             m_dragged_data.obj_idx() == m_objects_model->GetObjectIdByItem(item); | ||||
|             m_dragged_data.obj_idx() == m_objects_model->GetObjectIdByItem(item)); | ||||
| } | ||||
| 
 | ||||
| void ObjectList::OnDropPossible(wxDataViewEvent &event) | ||||
|  | @ -1331,7 +1331,7 @@ void ObjectList::append_menu_items_add_volume(wxMenu* menu) | |||
|         return; | ||||
|     } | ||||
|      | ||||
|     for (int type = mode == comExpert ? 0 : 1 ; type < ADD_VOLUME_MENU_ITEMS.size(); type++) | ||||
|     for (size_t type = (mode == comExpert ? 0 : 1) ; type < ADD_VOLUME_MENU_ITEMS.size(); type++) | ||||
|     { | ||||
|         auto& item = ADD_VOLUME_MENU_ITEMS[type]; | ||||
| 
 | ||||
|  | @ -1402,7 +1402,7 @@ wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_) | |||
|     // Create new items for settings popupmenu
 | ||||
| 
 | ||||
|     if (printer_technology() == ptFFF || | ||||
|         menu->GetMenuItems().size() > 0 && !menu->GetMenuItems().back()->IsSeparator()) | ||||
|        (menu->GetMenuItems().size() > 0 && !menu->GetMenuItems().back()->IsSeparator())) | ||||
|         menu->SetFirstSeparator(); | ||||
| 
 | ||||
|     // Add frequently settings
 | ||||
|  | @ -1435,9 +1435,9 @@ wxMenuItem* ObjectList::append_menu_item_instance_to_object(wxMenu* menu, wxWind | |||
|         [this](wxCommandEvent&) { split_instances(); }, "", menu, [](){return wxGetApp().plater()->can_set_instance_to_object(); }, parent); | ||||
| } | ||||
| 
 | ||||
| wxMenuItem* ObjectList::append_menu_item_printable(wxMenu* menu, wxWindow* parent) | ||||
| wxMenuItem* ObjectList::append_menu_item_printable(wxMenu* menu, wxWindow* /*parent*/) | ||||
| { | ||||
|     return append_menu_check_item(menu, wxID_ANY, _(L("Printable")), "", [this](wxCommandEvent&) { | ||||
|     return append_menu_check_item(menu, wxID_ANY, _(L("Printable")), "", [](wxCommandEvent&) { | ||||
|         wxGetApp().plater()->canvas3D()->get_selection().toggle_instance_printable_state(); | ||||
|         }, menu); | ||||
| } | ||||
|  | @ -1510,7 +1510,7 @@ void ObjectList::append_menu_item_delete(wxMenu* menu) | |||
| void ObjectList::append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu) | ||||
| { | ||||
|     append_menu_item(menu, wxID_ANY, _(L("Scale to print volume")), _(L("Scale the selected object to fit the print volume")), | ||||
|         [this](wxCommandEvent&) { wxGetApp().plater()->scale_selection_to_fit_print_volume(); }, "", menu); | ||||
|         [](wxCommandEvent&) { wxGetApp().plater()->scale_selection_to_fit_print_volume(); }, "", menu); | ||||
| } | ||||
| 
 | ||||
| void ObjectList::create_object_popupmenu(wxMenu *menu) | ||||
|  | @ -1576,7 +1576,7 @@ void ObjectList::create_instance_popupmenu(wxMenu*menu) | |||
|      * 2. Separate selected instances from the initial object to the separated object, | ||||
|      *    if some (not all) instances are selected | ||||
|      */ | ||||
|     wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { | ||||
|     wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [](wxUpdateUIEvent& evt) { | ||||
| //         evt.Enable(can_split_instances()); }, m_menu_item_split_instances->GetId());
 | ||||
|         evt.SetText(wxGetApp().plater()->canvas3D()->get_selection().is_single_full_object() ?  | ||||
|                     _(L("Set as a Separated Objects")) : _(L("Set as a Separated Object"))); | ||||
|  | @ -1588,7 +1588,7 @@ wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu) | |||
|     wxMenu *menu = new wxMenu; | ||||
| 
 | ||||
|     settings_menu_hierarchy settings_menu; | ||||
|     const bool is_part = m_objects_model->GetParent(GetSelection()) != wxDataViewItem(0); | ||||
|     const bool is_part = m_objects_model->GetParent(GetSelection()) != wxDataViewItem(nullptr); | ||||
|     get_options_menu(settings_menu, is_part); | ||||
| 
 | ||||
|     for (auto cat : settings_menu) { | ||||
|  | @ -1681,7 +1681,7 @@ void ObjectList::load_part( ModelObject* model_object, | |||
| 
 | ||||
|     wxArrayString input_files; | ||||
|     wxGetApp().import_model(parent, input_files); | ||||
|     for (int i = 0; i < input_files.size(); ++i) { | ||||
|     for (size_t i = 0; i < input_files.size(); ++i) { | ||||
|         std::string input_file = input_files.Item(i).ToUTF8().data(); | ||||
| 
 | ||||
|         Model model; | ||||
|  | @ -1719,7 +1719,7 @@ void ObjectList::load_part( ModelObject* model_object, | |||
| 
 | ||||
| void ObjectList::load_generic_subobject(const std::string& type_name, const ModelVolumeType type) | ||||
| { | ||||
|     const auto obj_idx = get_selected_obj_idx(); | ||||
|     const int obj_idx = get_selected_obj_idx(); | ||||
|     if (obj_idx < 0)  | ||||
|         return; | ||||
| 
 | ||||
|  | @ -1852,9 +1852,9 @@ void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item) | |||
| { | ||||
|     const bool is_layer_settings = m_objects_model->GetItemType(parent_item) == itLayer; | ||||
| 
 | ||||
|     const int opt_cnt = m_config->keys().size(); | ||||
|     if (opt_cnt == 1 && m_config->has("extruder") ||  | ||||
|         is_layer_settings && opt_cnt == 2 && m_config->has("extruder") && m_config->has("layer_height")) | ||||
|     const size_t opt_cnt = m_config->keys().size(); | ||||
|     if ((opt_cnt == 1 && m_config->has("extruder")) || | ||||
|         (is_layer_settings && opt_cnt == 2 && m_config->has("extruder") && m_config->has("layer_height"))) | ||||
|         return; | ||||
| 
 | ||||
|     take_snapshot(_(L("Delete Settings"))); | ||||
|  | @ -2052,13 +2052,13 @@ wxDataViewItem ObjectList::add_layer_root_item(const wxDataViewItem obj_item) | |||
|     if (obj_idx < 0 ||  | ||||
|         object(obj_idx)->layer_config_ranges.empty() || | ||||
|         printer_technology() == ptSLA) | ||||
|         return wxDataViewItem(0); | ||||
|         return wxDataViewItem(nullptr); | ||||
| 
 | ||||
|     // create LayerRoot item
 | ||||
|     wxDataViewItem layers_item = m_objects_model->AddLayersRoot(obj_item); | ||||
| 
 | ||||
|     // and create Layer item(s) according to the layer_config_ranges
 | ||||
|     for (const auto range : object(obj_idx)->layer_config_ranges) | ||||
|     for (const auto& range : object(obj_idx)->layer_config_ranges) | ||||
|         add_layer_item(range.first, layers_item); | ||||
| 
 | ||||
|     Expand(layers_item); | ||||
|  | @ -2150,7 +2150,7 @@ void ObjectList::part_selection_changed() | |||
| 
 | ||||
|     const auto item = GetSelection(); | ||||
| 
 | ||||
|     if ( multiple_selection() || item && m_objects_model->GetItemType(item) == itInstanceRoot )  | ||||
|     if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) | ||||
|     { | ||||
|         og_name = _(L("Group manipulation")); | ||||
| 
 | ||||
|  | @ -2162,7 +2162,7 @@ void ObjectList::part_selection_changed() | |||
|     { | ||||
|         if (item) | ||||
|         { | ||||
|             if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { | ||||
|             if (m_objects_model->GetParent(item) == wxDataViewItem(nullptr)) { | ||||
|                 obj_idx = m_objects_model->GetIdByItem(item); | ||||
|                 og_name = _(L("Object manipulation")); | ||||
|                 m_config = &(*m_objects)[obj_idx]->config; | ||||
|  | @ -2281,7 +2281,7 @@ SettingsBundle ObjectList::get_item_settings_bundle(const DynamicPrintConfig* co | |||
| // Add new SettingsItem for parent_item if it doesn't exist, or just update a digest according to new config
 | ||||
| wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const DynamicPrintConfig* config) | ||||
| { | ||||
|     wxDataViewItem ret = wxDataViewItem(0); | ||||
|     wxDataViewItem ret = wxDataViewItem(nullptr); | ||||
| 
 | ||||
|     if (!parent_item) | ||||
|         return ret; | ||||
|  | @ -2337,7 +2337,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) | |||
|     if (model_object->instances.size()>1) | ||||
|     { | ||||
|         std::vector<bool> print_idicator(model_object->instances.size()); | ||||
|         for (int i = 0; i < model_object->instances.size(); ++i) | ||||
|         for (size_t i = 0; i < model_object->instances.size(); ++i) | ||||
|             print_idicator[i] = model_object->instances[i]->printable; | ||||
| 
 | ||||
|         const wxDataViewItem object_item = m_objects_model->GetItemById(obj_idx); | ||||
|  | @ -2364,7 +2364,7 @@ void ObjectList::delete_object_from_list() | |||
|     auto item = GetSelection(); | ||||
|     if (!item)  | ||||
|         return; | ||||
|     if (m_objects_model->GetParent(item) == wxDataViewItem(0)) | ||||
|     if (m_objects_model->GetParent(item) == wxDataViewItem(nullptr)) | ||||
|         select_item(m_objects_model->Delete(item)); | ||||
|     else | ||||
|         select_item(m_objects_model->Delete(m_objects_model->GetParent(item))); | ||||
|  | @ -2521,7 +2521,7 @@ void ObjectList::remove() | |||
|     wxDataViewItemArray sels; | ||||
|     GetSelections(sels); | ||||
| 
 | ||||
|     wxDataViewItem parent = wxDataViewItem(0); | ||||
|     wxDataViewItem parent = wxDataViewItem(nullptr); | ||||
| 
 | ||||
|     if (sels.Count() == 1) | ||||
|         parent = delete_item(GetSelection()); | ||||
|  | @ -2588,7 +2588,7 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range& curre | |||
|     { | ||||
|         take_snapshot(_(L("Add Height Range"))); | ||||
| 
 | ||||
|         const t_layer_height_range& new_range = { last_range.second, last_range.second + 2.0f }; | ||||
|         const t_layer_height_range& new_range = { last_range.second, last_range.second + 2. }; | ||||
|         ranges[new_range] = get_default_layer_config(obj_idx); | ||||
|         add_layer_item(new_range, layers_item); | ||||
|     } | ||||
|  | @ -2611,7 +2611,7 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range& curre | |||
|             if (delta < get_min_layer_height(old_config.opt_int("extruder"))/*0.05f*/) // next range division has no sense 
 | ||||
|                 return;  | ||||
| 
 | ||||
|             const coordf_t midl_layer = next_range.first + 0.5f * delta; | ||||
|             const coordf_t midl_layer = next_range.first + 0.5 * delta; | ||||
|              | ||||
|             t_layer_height_range new_range = { midl_layer, next_range.second }; | ||||
| 
 | ||||
|  | @ -2711,7 +2711,7 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_lay | |||
| 
 | ||||
|     if (root_item.IsOk()) | ||||
|         // create Layer item(s) according to the layer_config_ranges
 | ||||
|         for (const auto r : ranges) | ||||
|         for (const auto& r : ranges) | ||||
|             add_layer_item(r.first, root_item); | ||||
| 
 | ||||
|     select_item(sel_type&itLayer ? m_objects_model->GetItemByLayerRange(obj_idx, new_range) : root_item); | ||||
|  | @ -3028,7 +3028,7 @@ void ObjectList::select_item_all_children() | |||
| 
 | ||||
|     // There is no selection before OR some object is selected   =>  select all objects
 | ||||
|     if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject) { | ||||
|         for (int i = 0; i < m_objects->size(); i++) | ||||
|         for (size_t i = 0; i < m_objects->size(); i++) | ||||
|             sels.Add(m_objects_model->GetItemById(i)); | ||||
|         m_selection_mode = smInstance; | ||||
|     } | ||||
|  | @ -3054,7 +3054,7 @@ void ObjectList::update_selection_mode() | |||
|     // All items are unselected 
 | ||||
|     if (!GetSelection()) | ||||
|     { | ||||
|         m_last_selected_item = wxDataViewItem(0); | ||||
|         m_last_selected_item = wxDataViewItem(nullptr); | ||||
|         m_selection_mode = smUndef; | ||||
|         return; | ||||
|     } | ||||
|  | @ -3096,9 +3096,9 @@ bool ObjectList::check_last_selection(wxString& msg_str) | |||
|     if (impossible_multi_selection(itVolume, smVolume) || | ||||
|         impossible_multi_selection(itLayer,  smLayer ) || | ||||
|         type & itSettings || | ||||
|         type & itVolume   && !(m_selection_mode & smVolume  ) || | ||||
|         type & itLayer    && !(m_selection_mode & smLayer   ) || | ||||
|         type & itInstance && !(m_selection_mode & smInstance) | ||||
|         (type & itVolume   && !(m_selection_mode & smVolume  )) || | ||||
|         (type & itLayer    && !(m_selection_mode & smLayer   )) || | ||||
|         (type & itInstance && !(m_selection_mode & smInstance)) | ||||
|         ) | ||||
|     { | ||||
|         // Inform user why selection isn't complited
 | ||||
|  | @ -3151,7 +3151,7 @@ void ObjectList::fix_multiselection_conflicts() | |||
| 
 | ||||
|         const ItemType item_type = m_selection_mode & smVolume ? itVolume : itLayer; | ||||
| 
 | ||||
|         for (const auto child : children) | ||||
|         for (const auto& child : children) | ||||
|             if (IsSelected(child) && m_objects_model->GetItemType(child) & item_type) | ||||
|                 sels.Add(child); | ||||
| 
 | ||||
|  | @ -3161,7 +3161,7 @@ void ObjectList::fix_multiselection_conflicts() | |||
|     } | ||||
|     else | ||||
|     { | ||||
|         for (const auto item : sels) | ||||
|         for (const auto& item : sels) | ||||
|         { | ||||
|             if (!IsSelected(item)) // if this item is unselected now (from previous actions)
 | ||||
|                 continue; | ||||
|  | @ -3172,13 +3172,13 @@ void ObjectList::fix_multiselection_conflicts() | |||
|             } | ||||
| 
 | ||||
|             const wxDataViewItem& parent = m_objects_model->GetParent(item); | ||||
|             if (parent != wxDataViewItem(0) && IsSelected(parent)) | ||||
|             if (parent != wxDataViewItem(nullptr) && IsSelected(parent)) | ||||
|                 Unselect(parent); | ||||
|             else | ||||
|             { | ||||
|                 wxDataViewItemArray unsels; | ||||
|                 m_objects_model->GetAllChildren(item, unsels); | ||||
|                 for (const auto unsel_item : unsels) | ||||
|                 for (const auto& unsel_item : unsels) | ||||
|                     Unselect(unsel_item); | ||||
|             } | ||||
| 
 | ||||
|  | @ -3193,7 +3193,7 @@ void ObjectList::fix_multiselection_conflicts() | |||
|         show_info(this, msg_string, _(L("Info"))); | ||||
| 
 | ||||
|     if (!IsSelected(m_last_selected_item)) | ||||
|         m_last_selected_item = wxDataViewItem(0); | ||||
|         m_last_selected_item = wxDataViewItem(nullptr); | ||||
| 
 | ||||
|     m_prevent_list_events = false; | ||||
| } | ||||
|  | @ -3334,7 +3334,7 @@ void ObjectList::update_object_list_by_printer_technology() | |||
|     GetSelections(sel); // stash selection
 | ||||
| 
 | ||||
|     wxDataViewItemArray object_items; | ||||
|     m_objects_model->GetChildren(wxDataViewItem(0), object_items); | ||||
|     m_objects_model->GetChildren(wxDataViewItem(nullptr), object_items); | ||||
| 
 | ||||
|     for (auto& object_item : object_items) { | ||||
|         // Update Settings Item for object
 | ||||
|  | @ -3405,7 +3405,7 @@ void ObjectList::instances_to_separated_object(const int obj_idx, const std::set | |||
| 
 | ||||
|     // create new object from selected instance  
 | ||||
|     ModelObject* model_object = (*m_objects)[obj_idx]->get_model()->add_object(*(*m_objects)[obj_idx]); | ||||
|     for (int inst_idx = model_object->instances.size() - 1; inst_idx >= 0; inst_idx--) | ||||
|     for (int inst_idx = int(model_object->instances.size()) - 1; inst_idx >= 0; inst_idx--) | ||||
|     { | ||||
|         if (find(inst_idxs.begin(), inst_idxs.end(), inst_idx) != inst_idxs.end()) | ||||
|             continue; | ||||
|  | @ -3599,7 +3599,7 @@ void ObjectList::OnEditingStarted(wxDataViewEvent &event) | |||
| { | ||||
| 	m_last_selected_column = -1; | ||||
| } | ||||
| #endif __WXMSW__ | ||||
| #endif //__WXMSW__
 | ||||
| 
 | ||||
| void ObjectList::OnEditingDone(wxDataViewEvent &event) | ||||
| { | ||||
|  | @ -3618,7 +3618,7 @@ void ObjectList::OnEditingDone(wxDataViewEvent &event) | |||
| 	// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
 | ||||
| 	// Here the last active column is forgotten, so when leaving the editing mode, the next mouse click will not enter the editing mode of the newly selected column.
 | ||||
| 	m_last_selected_column = -1; | ||||
| #endif __WXMSW__ | ||||
| #endif //__WXMSW__
 | ||||
| } | ||||
| 
 | ||||
| void ObjectList::show_multi_selection_menu() | ||||
|  |  | |||
|  | @ -116,7 +116,7 @@ bool ObjectSettings::update_settings_list() | |||
| 
 | ||||
|             auto optgroup = std::make_shared<ConfigOptionsGroup>(m_og->ctrl_parent(), _(cat.first), config, false, extra_column); | ||||
|             optgroup->label_width = 15; | ||||
|             optgroup->sidetext_width = 5.5; | ||||
|             optgroup->sidetext_width = 5; | ||||
| 
 | ||||
|             optgroup->m_on_change = [this, config](const t_config_option_key& opt_id, const boost::any& value) { | ||||
|                                     this->update_config_values(config); | ||||
|  | @ -241,4 +241,4 @@ void ObjectSettings::msw_rescale() | |||
| } | ||||
| 
 | ||||
| } //namespace GUI
 | ||||
| } //namespace Slic3r 
 | ||||
| } //namespace Slic3r 
 | ||||
|  |  | |||
|  | @ -5,11 +5,11 @@ | |||
| #include <vector> | ||||
| #include <wx/panel.h> | ||||
| #include "wxExtensions.hpp" | ||||
| #include "libslic3r/PrintConfig.hpp" | ||||
| 
 | ||||
| class wxBoxSizer; | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| class DynamicPrintConfig; | ||||
| namespace GUI { | ||||
| class ConfigOptionsGroup; | ||||
| 
 | ||||
|  |  | |||
|  | @ -640,9 +640,10 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool kee | |||
|     bool   snap_to_min = force_sliders_full_range || m_slider->is_lower_at_min(); | ||||
| 	bool   snap_to_max  = force_sliders_full_range || m_slider->is_higher_at_max(); | ||||
| 
 | ||||
|     std::vector<std::pair<int, double>> values; | ||||
|     fill_slider_values(values, layers_z); | ||||
|     m_slider->SetSliderValues(values); | ||||
|     std::vector<double> &ticks_from_config = (wxGetApp().preset_bundle->project_config.option<ConfigOptionFloats>("colorprint_heights"))->values; | ||||
|     check_slider_values(ticks_from_config, layers_z); | ||||
| 
 | ||||
|     m_slider->SetSliderValues(layers_z); | ||||
|     assert(m_slider->GetMinValue() == 0); | ||||
|     m_slider->SetMaxValue(layers_z.empty() ? 0 : layers_z.size() - 1); | ||||
| 
 | ||||
|  | @ -662,9 +663,6 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool kee | |||
|     } | ||||
|     m_slider->SetSelectionSpan(idx_low, idx_high); | ||||
| 
 | ||||
|     const auto& config = wxGetApp().preset_bundle->project_config; | ||||
|     const std::vector<double> &ticks_from_config = (config.option<ConfigOptionFloats>("colorprint_heights"))->values; | ||||
| 
 | ||||
|     m_slider->SetTicksValues(ticks_from_config); | ||||
| 
 | ||||
|     bool color_print_enable = (wxGetApp().plater()->printer_technology() == ptFFF); | ||||
|  | @ -676,26 +674,18 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool kee | |||
|     m_slider->EnableTickManipulation(color_print_enable); | ||||
| } | ||||
| 
 | ||||
| void Preview::fill_slider_values(std::vector<std::pair<int, double>> &values, | ||||
| void Preview::check_slider_values(std::vector<double>& ticks_from_config, | ||||
|                                  const std::vector<double> &layers_z) | ||||
| { | ||||
|     values.clear(); | ||||
|     for (int i = 0; i < layers_z.size(); ++i) | ||||
|     { | ||||
|         values.push_back(std::pair<int, double>(i + 1, layers_z[i])); | ||||
|     } | ||||
| 
 | ||||
|     // All ticks that would end up outside the slider range should be erased.
 | ||||
|     // TODO: this should be placed into more appropriate part of code,
 | ||||
|     // this function is e.g. not called when the last object is deleted
 | ||||
|     std::vector<double> &ticks_from_config = (wxGetApp().preset_bundle->project_config.option<ConfigOptionFloats>("colorprint_heights"))->values; | ||||
|     unsigned int old_size = ticks_from_config.size(); | ||||
|     ticks_from_config.erase(std::remove_if(ticks_from_config.begin(), ticks_from_config.end(), | ||||
|                                            [values](double val) | ||||
|                                            [layers_z](double val) | ||||
|     { | ||||
|         return (values.back().second < val && | ||||
|                 // we can't ignore tick on last layer
 | ||||
|                 fabs(values.back().second - val) > DoubleSlider::epsilon()); | ||||
|         auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val - DoubleSlider::epsilon()); | ||||
|         return it == layers_z.end(); | ||||
|     }), | ||||
|                             ticks_from_config.end()); | ||||
|     if (ticks_from_config.size() != old_size) | ||||
|  |  | |||
|  | @ -155,7 +155,7 @@ private: | |||
|     // Create/Update/Reset double slider on 3dPreview
 | ||||
|     void create_double_slider(); | ||||
|     void update_double_slider(const std::vector<double>& layers_z, bool keep_z_range = false); | ||||
|     void fill_slider_values(std::vector<std::pair<int, double>> &values, | ||||
|     void check_slider_values(std::vector<double> &ticks_from_config, | ||||
|                             const std::vector<double> &layers_z); | ||||
|     void reset_double_slider(); | ||||
|     // update DoubleSlider after keyDown in canvas
 | ||||
|  |  | |||
|  | @ -69,7 +69,6 @@ public: | |||
|     enum EState | ||||
|     { | ||||
|         Off, | ||||
|         Hover, | ||||
|         On, | ||||
|         Num_States | ||||
|     }; | ||||
|  |  | |||
|  | @ -44,12 +44,12 @@ public: | |||
|     Vec3d get_flattening_normal() const; | ||||
| 
 | ||||
| protected: | ||||
|     virtual bool on_init(); | ||||
|     virtual std::string on_get_name() const; | ||||
|     virtual bool on_is_activable() const; | ||||
|     virtual void on_start_dragging(); | ||||
|     virtual void on_render() const; | ||||
|     virtual void on_render_for_picking() const; | ||||
|     virtual bool on_init() override; | ||||
|     virtual std::string on_get_name() const override; | ||||
|     virtual bool on_is_activable() const override; | ||||
|     virtual void on_start_dragging() override; | ||||
|     virtual void on_render() const override; | ||||
|     virtual void on_render_for_picking() const override; | ||||
|     virtual void on_set_state() override; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -87,16 +87,12 @@ protected: | |||
|     virtual void on_set_state() | ||||
|     { | ||||
|         for (GLGizmoRotate& g : m_gizmos) | ||||
|         { | ||||
|             g.set_state(m_state); | ||||
|         } | ||||
|     } | ||||
|     virtual void on_set_hover_id() | ||||
|     { | ||||
|         for (int i = 0; i < 3; ++i) | ||||
|         { | ||||
|             m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1); | ||||
|         } | ||||
|     } | ||||
|     virtual void on_enable_grabber(unsigned int id) | ||||
|     { | ||||
|  |  | |||
|  | @ -329,6 +329,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) | |||
|             case X: { local_offset_vec = local_offset * Vec3d::UnitX(); break; } | ||||
|             case Y: { local_offset_vec = local_offset * Vec3d::UnitY(); break; } | ||||
|             case Z: { local_offset_vec = local_offset * Vec3d::UnitZ(); break; } | ||||
|             default: break; | ||||
|             } | ||||
| 
 | ||||
|             m_offset = m_offsets_transform * local_offset_vec; | ||||
|  |  | |||
|  | @ -12,7 +12,9 @@ | |||
| #include "slic3r/GUI/GUI.hpp" | ||||
| #include "slic3r/GUI/GUI_ObjectSettings.hpp" | ||||
| #include "slic3r/GUI/GUI_ObjectList.hpp" | ||||
| #include "slic3r/GUI/Plater.hpp" | ||||
| #include "slic3r/GUI/PresetBundle.hpp" | ||||
| #include "libslic3r/SLAPrint.hpp" | ||||
| #include "libslic3r/Tesselate.hpp" | ||||
| 
 | ||||
| 
 | ||||
|  | @ -306,7 +308,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) | |||
|         } | ||||
|         else { | ||||
|             render_color[3] = 1.f; | ||||
|             if ((m_hover_id == i && m_editing_mode)) { // ignore hover state unless editing mode is active
 | ||||
|             if ((size_t(m_hover_id) == i && m_editing_mode)) { // ignore hover state unless editing mode is active
 | ||||
|                 render_color[0] = 0.f; | ||||
|                 render_color[1] = 1.0f; | ||||
|                 render_color[2] = 1.0f; | ||||
|  | @ -328,7 +330,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) | |||
| 
 | ||||
|         // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
 | ||||
|         glsafe(::glPushMatrix()); | ||||
|         glsafe(::glTranslated(support_point.pos(0), support_point.pos(1), support_point.pos(2))); | ||||
|         glsafe(::glTranslatef(support_point.pos(0), support_point.pos(1), support_point.pos(2))); | ||||
|         glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); | ||||
| 
 | ||||
|         if (vol->is_left_handed()) | ||||
|  | @ -345,16 +347,16 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) | |||
|             Eigen::AngleAxisd aa(q); | ||||
|             glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); | ||||
| 
 | ||||
|             const float cone_radius = 0.25f; // mm
 | ||||
|             const float cone_height = 0.75f; | ||||
|             const double cone_radius = 0.25; // mm
 | ||||
|             const double cone_height = 0.75; | ||||
|             glsafe(::glPushMatrix()); | ||||
|             glsafe(::glTranslatef(0.f, 0.f, support_point.head_front_radius * RenderPointScale)); | ||||
|             ::gluCylinder(m_quadric, 0.f, cone_radius, cone_height, 24, 1); | ||||
|             ::gluCylinder(m_quadric, 0., cone_radius, cone_height, 24, 1); | ||||
|             glsafe(::glTranslatef(0.f, 0.f, cone_height)); | ||||
|             ::gluDisk(m_quadric, 0.0, cone_radius, 24, 1); | ||||
|             glsafe(::glPopMatrix()); | ||||
|         } | ||||
|         ::gluSphere(m_quadric, support_point.head_front_radius * RenderPointScale, 24, 12); | ||||
|         ::gluSphere(m_quadric, (double)support_point.head_front_radius * RenderPointScale, 24, 12); | ||||
|         if (vol->is_left_handed()) | ||||
|             glFrontFace(GL_CCW); | ||||
| 
 | ||||
|  | @ -775,7 +777,7 @@ std::vector<const ConfigOption*> GLGizmoSlaSupports::get_config_options(const st | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const | ||||
| void GLGizmoSlaSupports::update_cache_entry_normal(size_t i) const | ||||
| { | ||||
|     int idx = 0; | ||||
|     Eigen::Matrix<float, 1, 3> pp = m_editing_cache[i].support_point.pos; | ||||
|  | @ -1098,9 +1100,6 @@ std::string GLGizmoSlaSupports::on_get_name() const | |||
| 
 | ||||
| void GLGizmoSlaSupports::on_set_state() | ||||
| { | ||||
|     if (m_state == Hover) | ||||
|         return; | ||||
| 
 | ||||
|     // m_model_object pointer can be invalid (for instance because of undo/redo action),
 | ||||
|     // we should recover it from the object id
 | ||||
|     m_model_object = nullptr; | ||||
|  | @ -1111,6 +1110,9 @@ void GLGizmoSlaSupports::on_set_state() | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (m_state == m_old_state) | ||||
|         return; | ||||
| 
 | ||||
|     if (m_state == On && m_old_state != On) { // the gizmo was just turned on
 | ||||
|         Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); | ||||
|         if (is_mesh_update_necessary()) | ||||
|  |  | |||
|  | @ -11,7 +11,6 @@ | |||
| #include "slic3r/GUI/I18N.hpp"  // ...and redefine again when we are done with the igl code
 | ||||
| 
 | ||||
| #include "libslic3r/SLA/SLACommon.hpp" | ||||
| #include "libslic3r/SLAPrint.hpp" | ||||
| #include <wx/dialog.h> | ||||
| 
 | ||||
| #include <cereal/types/vector.hpp> | ||||
|  | @ -31,7 +30,7 @@ private: | |||
|     ObjectID m_model_object_id = 0; | ||||
|     int m_active_instance = -1; | ||||
|     float m_active_instance_bb_radius; // to cache the bb
 | ||||
|     mutable float m_z_shift = 0.f; | ||||
|     mutable double m_z_shift = 0.f; | ||||
|     bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal); | ||||
| 
 | ||||
|     const float RenderPointScale = 1.f; | ||||
|  | @ -78,7 +77,7 @@ private: | |||
| 
 | ||||
| public: | ||||
|     GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); | ||||
|     virtual ~GLGizmoSlaSupports(); | ||||
|     ~GLGizmoSlaSupports() override; | ||||
|     void set_sla_support_data(ModelObject* model_object, const Selection& selection); | ||||
|     bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); | ||||
|     void delete_selected_points(bool force = false); | ||||
|  | @ -90,21 +89,19 @@ public: | |||
|     void reslice_SLA_supports(bool postpone_error_messages = false) const; | ||||
| 
 | ||||
| private: | ||||
|     bool on_init(); | ||||
|     void on_update(const UpdateData& data); | ||||
|     virtual void on_render() const; | ||||
|     virtual void on_render_for_picking() const; | ||||
|     bool on_init() override; | ||||
|     void on_update(const UpdateData& data) override; | ||||
|     void on_render() const override; | ||||
|     void on_render_for_picking() const override; | ||||
| 
 | ||||
|     //void render_selection_rectangle() const;
 | ||||
|     void render_points(const Selection& selection, bool picking = false) const; | ||||
|     void render_clipping_plane(const Selection& selection) const; | ||||
|     bool is_mesh_update_necessary() const; | ||||
|     void update_mesh(); | ||||
|     void update_cache_entry_normal(unsigned int i) const; | ||||
|     void update_cache_entry_normal(size_t i) const; | ||||
|     bool unsaved_changes() const; | ||||
| 
 | ||||
|     EState m_no_hover_state = Off; | ||||
|     EState m_no_hover_old_state = Off; | ||||
|     bool m_lock_unique_islands = false; | ||||
|     bool m_editing_mode = false;            // Is editing mode active?
 | ||||
|     bool m_old_editing_state = false;       // To keep track of whether the user toggled between the modes (needed for imgui refreshes).
 | ||||
|  | @ -157,20 +154,21 @@ private: | |||
| 
 | ||||
| protected: | ||||
|     void on_set_state() override; | ||||
|     virtual void on_set_hover_id() | ||||
|     void on_set_hover_id() override | ||||
| 
 | ||||
|     { | ||||
|         if (! m_editing_mode || (int)m_editing_cache.size() <= m_hover_id) | ||||
|             m_hover_id = -1; | ||||
|     } | ||||
|     void on_start_dragging() override; | ||||
|     void on_stop_dragging() override; | ||||
|     virtual void on_render_input_window(float x, float y, float bottom_limit) override; | ||||
|     void on_render_input_window(float x, float y, float bottom_limit) override; | ||||
| 
 | ||||
|     virtual std::string on_get_name() const; | ||||
|     virtual bool on_is_activable() const; | ||||
|     virtual bool on_is_selectable() const; | ||||
|     virtual void on_load(cereal::BinaryInputArchive& ar) override; | ||||
|     virtual void on_save(cereal::BinaryOutputArchive& ar) const override; | ||||
|     std::string on_get_name() const override; | ||||
|     bool on_is_activable() const override; | ||||
|     bool on_is_selectable() const override; | ||||
|     void on_load(cereal::BinaryInputArchive& ar) override; | ||||
|     void on_save(cereal::BinaryOutputArchive& ar) const override; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -29,9 +29,49 @@ GLGizmosManager::GLGizmosManager(GLCanvas3D& parent) | |||
| { | ||||
| } | ||||
| 
 | ||||
| GLGizmosManager::~GLGizmosManager() | ||||
| std::vector<size_t> GLGizmosManager::get_selectable_idxs() const | ||||
| { | ||||
|     reset(); | ||||
|     std::vector<size_t> out; | ||||
|     for (size_t i=0; i<m_gizmos.size(); ++i) | ||||
|         if (m_gizmos[i]->is_selectable()) | ||||
|             out.push_back(i); | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| std::vector<size_t> GLGizmosManager::get_activable_idxs() const | ||||
| { | ||||
|     std::vector<size_t> out; | ||||
|     for (size_t i=0; i<m_gizmos.size(); ++i) | ||||
|         if (m_gizmos[i]->is_activable()) | ||||
|             out.push_back(i); | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| size_t GLGizmosManager::get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const | ||||
| { | ||||
|     if (! m_enabled) | ||||
|         return Undefined; | ||||
| 
 | ||||
|     float cnv_h = (float)m_parent.get_canvas_size().get_height(); | ||||
|     float height = get_total_overlay_height(); | ||||
|     float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; | ||||
|     float scaled_border = m_overlay_border * m_overlay_scale; | ||||
|     float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; | ||||
|     float scaled_stride_y = scaled_icons_size + scaled_gap_y; | ||||
|     float top_y = 0.5f * (cnv_h - height) + scaled_border; | ||||
| 
 | ||||
|     // is mouse horizontally in the area?
 | ||||
|     if ((scaled_border <= (float)mouse_pos(0) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size))) { | ||||
|         // which icon is it on?
 | ||||
|         size_t from_top = (size_t)((float)mouse_pos(1) - top_y)/scaled_stride_y; | ||||
|         // is it really on the icon or already past the border?
 | ||||
|         if ((float)mouse_pos(1) <= top_y + from_top*scaled_stride_y + scaled_icons_size) { | ||||
|             std::vector<size_t> selectable = get_selectable_idxs(); | ||||
|             if (from_top < selectable.size()) | ||||
|                 return selectable[from_top]; | ||||
|         } | ||||
|     } | ||||
|     return Undefined; | ||||
| } | ||||
| 
 | ||||
| bool GLGizmosManager::init() | ||||
|  | @ -45,77 +85,25 @@ bool GLGizmosManager::init() | |||
|     if (!m_background_texture.metadata.filename.empty()) | ||||
|     { | ||||
|         if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false, GLTexture::SingleThreaded, false)) | ||||
|         { | ||||
|             reset(); | ||||
|             return false; | ||||
|     } | ||||
| 
 | ||||
|     m_gizmos.emplace_back(new GLGizmoMove3D(m_parent, "move.svg", 0)); | ||||
|     m_gizmos.emplace_back(new GLGizmoScale3D(m_parent, "scale.svg", 1)); | ||||
|     m_gizmos.emplace_back(new GLGizmoRotate3D(m_parent, "rotate.svg", 2)); | ||||
|     m_gizmos.emplace_back(new GLGizmoFlatten(m_parent, "place.svg", 3)); | ||||
|     m_gizmos.emplace_back(new GLGizmoCut(m_parent, "cut.svg", 4)); | ||||
|     m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 5)); | ||||
| 
 | ||||
|     for (auto& gizmo : m_gizmos) { | ||||
|         if (! gizmo->init()) { | ||||
|             m_gizmos.clear(); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     GLGizmoBase* gizmo = new GLGizmoMove3D(m_parent, "move.svg", 0); | ||||
|     if (gizmo == nullptr) | ||||
|         return false; | ||||
| 
 | ||||
|     if (!gizmo->init()) | ||||
|         return false; | ||||
| 
 | ||||
|     m_gizmos.insert(GizmosMap::value_type(Move, gizmo)); | ||||
| 
 | ||||
|     gizmo = new GLGizmoScale3D(m_parent, "scale.svg", 1); | ||||
|     if (gizmo == nullptr) | ||||
|         return false; | ||||
| 
 | ||||
|     if (!gizmo->init()) | ||||
|         return false; | ||||
| 
 | ||||
|     m_gizmos.insert(GizmosMap::value_type(Scale, gizmo)); | ||||
| 
 | ||||
|     gizmo = new GLGizmoRotate3D(m_parent, "rotate.svg", 2); | ||||
|     if (gizmo == nullptr) | ||||
|     { | ||||
|         reset(); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (!gizmo->init()) | ||||
|     { | ||||
|         reset(); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo)); | ||||
| 
 | ||||
|     gizmo = new GLGizmoFlatten(m_parent, "place.svg", 3); | ||||
|     if (gizmo == nullptr) | ||||
|         return false; | ||||
| 
 | ||||
|     if (!gizmo->init()) { | ||||
|         reset(); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo)); | ||||
| 
 | ||||
|     gizmo = new GLGizmoCut(m_parent, "cut.svg", 4); | ||||
|     if (gizmo == nullptr) | ||||
|         return false; | ||||
| 
 | ||||
|     if (!gizmo->init()) { | ||||
|         reset(); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     m_gizmos.insert(GizmosMap::value_type(Cut, gizmo)); | ||||
| 
 | ||||
|     gizmo = new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 5); | ||||
|     if (gizmo == nullptr) | ||||
|         return false; | ||||
| 
 | ||||
|     if (!gizmo->init()) { | ||||
|         reset(); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo)); | ||||
|     m_current = Undefined; | ||||
|     m_hover = Undefined; | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
|  | @ -140,65 +128,39 @@ void GLGizmosManager::set_overlay_scale(float scale) | |||
| 
 | ||||
| void GLGizmosManager::refresh_on_off_state() | ||||
| { | ||||
|     if (m_serializing) | ||||
|     if (m_serializing || m_current == Undefined || m_gizmos.empty()) | ||||
|         return; | ||||
| 
 | ||||
|     GizmosMap::iterator it = m_gizmos.find(m_current); | ||||
|     if ((it != m_gizmos.end()) && (it->second != nullptr)) | ||||
|     { | ||||
|         if (!it->second->is_activable()) | ||||
|         { | ||||
|             it->second->set_state(GLGizmoBase::Off); | ||||
|             m_current = Undefined; | ||||
|         } | ||||
|     } | ||||
|     if (m_current != Undefined && ! m_gizmos[m_current]->is_activable()) | ||||
|         activate_gizmo(Undefined); | ||||
| } | ||||
| 
 | ||||
| void GLGizmosManager::reset_all_states() | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (! m_enabled || m_serializing) | ||||
|         return; | ||||
| 
 | ||||
|     if (m_serializing) | ||||
|         return; | ||||
| 
 | ||||
|     for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) | ||||
|     { | ||||
|         if (it->second != nullptr) | ||||
|         { | ||||
|             it->second->set_state(GLGizmoBase::Off); | ||||
|             it->second->set_hover_id(-1); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     m_current = Undefined; | ||||
|     activate_gizmo(Undefined); | ||||
|     m_hover = Undefined; | ||||
| } | ||||
| 
 | ||||
| void GLGizmosManager::set_hover_id(int id) | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (!m_enabled || m_current == Undefined) | ||||
|         return; | ||||
| 
 | ||||
|     for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) | ||||
|     { | ||||
|         if ((it->second != nullptr) && (it->second->get_state() == GLGizmoBase::On)) | ||||
|             it->second->set_hover_id(id); | ||||
|     } | ||||
|     m_gizmos[m_current]->set_hover_id(id); | ||||
| } | ||||
| 
 | ||||
| void GLGizmosManager::enable_grabber(EType type, unsigned int id, bool enable) | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (!m_enabled || type == Undefined || m_gizmos.empty()) | ||||
|         return; | ||||
| 
 | ||||
|     GizmosMap::const_iterator it = m_gizmos.find(type); | ||||
|     if (it != m_gizmos.end()) | ||||
|     { | ||||
|         if (enable) | ||||
|             it->second->enable_grabber(id); | ||||
|         else | ||||
|             it->second->disable_grabber(id); | ||||
|     } | ||||
|     if (enable) | ||||
|         m_gizmos[type]->enable_grabber(id); | ||||
|     else | ||||
|         m_gizmos[type]->disable_grabber(id); | ||||
| } | ||||
| 
 | ||||
| void GLGizmosManager::update(const Linef3& mouse_ray, const Point& mouse_pos) | ||||
|  | @ -269,8 +231,9 @@ bool GLGizmosManager::is_running() const | |||
|     if (!m_enabled) | ||||
|         return false; | ||||
| 
 | ||||
|     GLGizmoBase* curr = get_current(); | ||||
|     return (curr != nullptr) ? (curr->get_state() == GLGizmoBase::On) : false; | ||||
|     //GLGizmoBase* curr = get_current();
 | ||||
|     //return (curr != nullptr) ? (curr->get_state() == GLGizmoBase::On) : false;
 | ||||
|     return m_current != Undefined; | ||||
| } | ||||
| 
 | ||||
| bool GLGizmosManager::handle_shortcut(int key) | ||||
|  | @ -283,43 +246,12 @@ bool GLGizmosManager::handle_shortcut(int key) | |||
| 
 | ||||
|     bool handled = false; | ||||
| 
 | ||||
|     for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) | ||||
|     { | ||||
|         if ((it->second == nullptr) || !it->second->is_selectable()) | ||||
|             continue; | ||||
|     for (size_t idx : get_selectable_idxs()) { | ||||
|         int it_key = m_gizmos[idx]->get_shortcut_key(); | ||||
| 
 | ||||
|         int it_key = it->second->get_shortcut_key(); | ||||
| 
 | ||||
|         if (it->second->is_activable() && ((it_key == key - 64) || (it_key == key - 96))) | ||||
|         { | ||||
|             if ((it->second->get_state() == GLGizmoBase::On)) | ||||
|             { | ||||
|                 it->second->set_state(GLGizmoBase::Off); | ||||
|                 if (it->second->get_state() == GLGizmoBase::Off) { | ||||
|                     m_current = Undefined; | ||||
|                 } | ||||
|         if (m_gizmos[idx]->is_activable() && ((it_key == key - 64) || (it_key == key - 96))) { | ||||
|                 activate_gizmo(m_current == idx ? Undefined : (EType)idx); | ||||
|                 handled = true; | ||||
|             } | ||||
|             else if ((it->second->get_state() == GLGizmoBase::Off)) | ||||
|             { | ||||
|                 // Before turning anything on, turn everything else off to see if
 | ||||
|                 // nobody refuses. Only then activate the gizmo.
 | ||||
|                 bool can_open = true; | ||||
|                 for (GizmosMap::iterator i = m_gizmos.begin(); i != m_gizmos.end(); ++i) { | ||||
|                     if (i->first != it->first) { | ||||
|                         if (m_current == i->first && i->second->get_state() != GLGizmoBase::Off ) { | ||||
|                             i->second->set_state(GLGizmoBase::Off); | ||||
|                             if (i->second->get_state() != GLGizmoBase::Off) | ||||
|                                 can_open = false; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (can_open) { | ||||
|                     it->second->set_state(GLGizmoBase::On); | ||||
|                     m_current = it->first; | ||||
|                 } | ||||
|                 handled = true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -328,31 +260,25 @@ bool GLGizmosManager::handle_shortcut(int key) | |||
| 
 | ||||
| bool GLGizmosManager::is_dragging() const | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (! m_enabled || m_current == Undefined) | ||||
|         return false; | ||||
| 
 | ||||
|     GLGizmoBase* curr = get_current(); | ||||
|     return (curr != nullptr) ? curr->is_dragging() : false; | ||||
|     return m_gizmos[m_current]->is_dragging(); | ||||
| } | ||||
| 
 | ||||
| void GLGizmosManager::start_dragging() | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (! m_enabled || m_current == Undefined) | ||||
|         return; | ||||
| 
 | ||||
|     GLGizmoBase* curr = get_current(); | ||||
|     if (curr != nullptr) | ||||
|         curr->start_dragging(); | ||||
|     m_gizmos[m_current]->start_dragging(); | ||||
| } | ||||
| 
 | ||||
| void GLGizmosManager::stop_dragging() | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (! m_enabled || m_current == Undefined) | ||||
|         return; | ||||
| 
 | ||||
|     GLGizmoBase* curr = get_current(); | ||||
|     if (curr != nullptr) | ||||
|         curr->stop_dragging(); | ||||
|     m_gizmos[m_current]->stop_dragging(); | ||||
| } | ||||
| 
 | ||||
| Vec3d GLGizmosManager::get_displacement() const | ||||
|  | @ -360,8 +286,7 @@ Vec3d GLGizmosManager::get_displacement() const | |||
|     if (!m_enabled) | ||||
|         return Vec3d::Zero(); | ||||
| 
 | ||||
|     GizmosMap::const_iterator it = m_gizmos.find(Move); | ||||
|     return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoMove3D*>(it->second)->get_displacement() : Vec3d::Zero(); | ||||
|     return dynamic_cast<GLGizmoMove3D*>(m_gizmos[Move].get())->get_displacement(); | ||||
| } | ||||
| 
 | ||||
| Vec3d GLGizmosManager::get_scale() const | ||||
|  | @ -369,126 +294,101 @@ Vec3d GLGizmosManager::get_scale() const | |||
|     if (!m_enabled) | ||||
|         return Vec3d::Ones(); | ||||
| 
 | ||||
|     GizmosMap::const_iterator it = m_gizmos.find(Scale); | ||||
|     return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoScale3D*>(it->second)->get_scale() : Vec3d::Ones(); | ||||
|     return dynamic_cast<GLGizmoScale3D*>(m_gizmos[Scale].get())->get_scale(); | ||||
| } | ||||
| 
 | ||||
| void GLGizmosManager::set_scale(const Vec3d& scale) | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (!m_enabled || m_gizmos.empty()) | ||||
|         return; | ||||
| 
 | ||||
|     GizmosMap::const_iterator it = m_gizmos.find(Scale); | ||||
|     if (it != m_gizmos.end()) | ||||
|         reinterpret_cast<GLGizmoScale3D*>(it->second)->set_scale(scale); | ||||
|     dynamic_cast<GLGizmoScale3D*>(m_gizmos[Scale].get())->set_scale(scale); | ||||
| } | ||||
| 
 | ||||
| Vec3d GLGizmosManager::get_scale_offset() const | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (!m_enabled || m_gizmos.empty()) | ||||
|         return Vec3d::Zero(); | ||||
| 
 | ||||
|     GizmosMap::const_iterator it = m_gizmos.find(Scale); | ||||
|     return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoScale3D*>(it->second)->get_offset() : Vec3d::Zero(); | ||||
|     return dynamic_cast<GLGizmoScale3D*>(m_gizmos[Scale].get())->get_offset(); | ||||
| } | ||||
| 
 | ||||
| Vec3d GLGizmosManager::get_rotation() const | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (!m_enabled || m_gizmos.empty()) | ||||
|         return Vec3d::Zero(); | ||||
| 
 | ||||
|     GizmosMap::const_iterator it = m_gizmos.find(Rotate); | ||||
|     return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoRotate3D*>(it->second)->get_rotation() : Vec3d::Zero(); | ||||
|     return dynamic_cast<GLGizmoRotate3D*>(m_gizmos[Rotate].get())->get_rotation(); | ||||
| } | ||||
| 
 | ||||
| void GLGizmosManager::set_rotation(const Vec3d& rotation) | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (!m_enabled || m_gizmos.empty()) | ||||
|         return; | ||||
| 
 | ||||
|     GizmosMap::const_iterator it = m_gizmos.find(Rotate); | ||||
|     if (it != m_gizmos.end()) | ||||
|         reinterpret_cast<GLGizmoRotate3D*>(it->second)->set_rotation(rotation); | ||||
|     dynamic_cast<GLGizmoRotate3D*>(m_gizmos[Rotate].get())->set_rotation(rotation); | ||||
| } | ||||
| 
 | ||||
| Vec3d GLGizmosManager::get_flattening_normal() const | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (!m_enabled || m_gizmos.empty()) | ||||
|         return Vec3d::Zero(); | ||||
| 
 | ||||
|     GizmosMap::const_iterator it = m_gizmos.find(Flatten); | ||||
|     return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoFlatten*>(it->second)->get_flattening_normal() : Vec3d::Zero(); | ||||
|     return dynamic_cast<GLGizmoFlatten*>(m_gizmos[Flatten].get())->get_flattening_normal(); | ||||
| } | ||||
| 
 | ||||
| void GLGizmosManager::set_flattening_data(const ModelObject* model_object) | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (!m_enabled || m_gizmos.empty()) | ||||
|         return; | ||||
| 
 | ||||
|     GizmosMap::const_iterator it = m_gizmos.find(Flatten); | ||||
|     if (it != m_gizmos.end()) | ||||
|         reinterpret_cast<GLGizmoFlatten*>(it->second)->set_flattening_data(model_object); | ||||
|     dynamic_cast<GLGizmoFlatten*>(m_gizmos[Flatten].get())->set_flattening_data(model_object); | ||||
| } | ||||
| 
 | ||||
| void GLGizmosManager::set_sla_support_data(ModelObject* model_object) | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (!m_enabled || m_gizmos.empty()) | ||||
|         return; | ||||
| 
 | ||||
|     GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); | ||||
|     if (it != m_gizmos.end()) | ||||
|         reinterpret_cast<GLGizmoSlaSupports*>(it->second)->set_sla_support_data(model_object, m_parent.get_selection()); | ||||
|     dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->set_sla_support_data(model_object, m_parent.get_selection()); | ||||
| } | ||||
| 
 | ||||
| // Returns true if the gizmo used the event to do something, false otherwise.
 | ||||
| bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (!m_enabled || m_gizmos.empty()) | ||||
|         return false; | ||||
| 
 | ||||
|     GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); | ||||
|     if (it != m_gizmos.end()) | ||||
|         return reinterpret_cast<GLGizmoSlaSupports*>(it->second)->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); | ||||
| 
 | ||||
|     return false; | ||||
|     return dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); | ||||
| } | ||||
| 
 | ||||
| ClippingPlane GLGizmosManager::get_sla_clipping_plane() const | ||||
| { | ||||
|     if (!m_enabled || m_current != SlaSupports) | ||||
|     if (!m_enabled || m_current != SlaSupports || m_gizmos.empty()) | ||||
|         return ClippingPlane::ClipsNothing(); | ||||
| 
 | ||||
|     GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); | ||||
|     if (it != m_gizmos.end()) | ||||
|         return reinterpret_cast<GLGizmoSlaSupports*>(it->second)->get_sla_clipping_plane(); | ||||
| 
 | ||||
|     return ClippingPlane::ClipsNothing(); | ||||
|     return dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->get_sla_clipping_plane(); | ||||
| } | ||||
| 
 | ||||
| bool GLGizmosManager::wants_reslice_supports_on_undo() const | ||||
| { | ||||
|     return (m_current == SlaSupports | ||||
|         && dynamic_cast<const GLGizmoSlaSupports*>(m_gizmos.at(SlaSupports))->has_backend_supports()); | ||||
|         && dynamic_cast<const GLGizmoSlaSupports*>(m_gizmos.at(SlaSupports).get())->has_backend_supports()); | ||||
| } | ||||
| 
 | ||||
| void GLGizmosManager::render_current_gizmo() const | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (!m_enabled || m_current == Undefined) | ||||
|         return; | ||||
| 
 | ||||
|     GLGizmoBase* curr = get_current(); | ||||
|     if (curr != nullptr) | ||||
|         curr->render(); | ||||
|     m_gizmos[m_current]->render(); | ||||
| } | ||||
| 
 | ||||
| void GLGizmosManager::render_current_gizmo_for_picking_pass() const | ||||
| { | ||||
|     if (!m_enabled) | ||||
|     if (! m_enabled || m_current == Undefined) | ||||
|         return; | ||||
| 
 | ||||
|     GLGizmoBase* curr = get_current(); | ||||
|     if (curr != nullptr) | ||||
|         curr->render_for_picking(); | ||||
|     m_gizmos[m_current]->render_for_picking(); | ||||
| } | ||||
| 
 | ||||
| void GLGizmosManager::render_overlay() const | ||||
|  | @ -547,7 +447,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) | |||
|         // if the button down was done on this toolbar, prevent from dragging into the scene
 | ||||
|         processed = true; | ||||
| 
 | ||||
|     if (!overlay_contains_mouse(mouse_pos)) | ||||
|     if (get_gizmo_idx_from_mouse(mouse_pos) == Undefined) | ||||
|     { | ||||
|         // mouse is outside the toolbar
 | ||||
|         m_tooltip = ""; | ||||
|  | @ -557,14 +457,12 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) | |||
|             if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) | ||||
|                 // the gizmo got the event and took some action, there is no need to do anything more
 | ||||
|                 processed = true; | ||||
|             else if (!selection.is_empty() && grabber_contains_mouse()) | ||||
|             { | ||||
|             else if (!selection.is_empty() && grabber_contains_mouse()) { | ||||
|                 update_data(); | ||||
|                 selection.start_dragging(); | ||||
|                 start_dragging(); | ||||
| 
 | ||||
|                 if (m_current == Flatten) | ||||
|                 { | ||||
|                 if (m_current == Flatten) { | ||||
|                     // Rotate the object so the normal points downward:
 | ||||
|                     m_parent.do_flatten(get_flattening_normal(), L("Gizmo-Place on Face")); | ||||
|                     wxGetApp().obj_manipul()->set_dirty(); | ||||
|  | @ -634,25 +532,14 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) | |||
|         } | ||||
|         else if (evt.LeftUp() && is_dragging()) | ||||
|         { | ||||
|             switch (m_current) | ||||
|             { | ||||
|             case Move: | ||||
|             { | ||||
|                 m_parent.do_move(L("Gizmo-Move")); | ||||
|                 break; | ||||
|             } | ||||
|             case Scale: | ||||
|             { | ||||
|                 m_parent.do_scale(L("Gizmo-Scale")); | ||||
|                 break; | ||||
|             } | ||||
|             case Rotate: | ||||
|             { | ||||
|                 m_parent.do_rotate(L("Gizmo-Rotate")); | ||||
|                 break; | ||||
|             } | ||||
|             default: | ||||
|                 break; | ||||
|             switch (m_current) { | ||||
|             case Move : m_parent.do_move(L("Gizmo-Move")); | ||||
|                         break; | ||||
|             case Scale : m_parent.do_scale(L("Gizmo-Scale")); | ||||
|                          break; | ||||
|             case Rotate : m_parent.do_rotate(L("Gizmo-Rotate")); | ||||
|                           break; | ||||
|             default : break; | ||||
|             } | ||||
| 
 | ||||
|             stop_dragging(); | ||||
|  | @ -842,7 +729,7 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) | |||
|     { | ||||
|         if (m_current == SlaSupports) | ||||
|         { | ||||
|             GLGizmoSlaSupports* gizmo = reinterpret_cast<GLGizmoSlaSupports*>(get_current()); | ||||
|             GLGizmoSlaSupports* gizmo = dynamic_cast<GLGizmoSlaSupports*>(get_current()); | ||||
| 
 | ||||
|             if (keyCode == WXK_SHIFT) | ||||
|             { | ||||
|  | @ -863,7 +750,8 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) | |||
|     } | ||||
|     else if (evt.GetEventType() == wxEVT_KEY_DOWN) | ||||
|     { | ||||
|         if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT)) && reinterpret_cast<GLGizmoSlaSupports*>(get_current())->is_in_editing_mode()) | ||||
|         if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT)) | ||||
|           && dynamic_cast<GLGizmoSlaSupports*>(get_current())->is_in_editing_mode()) | ||||
|         { | ||||
| //            m_parent.set_cursor(GLCanvas3D::Cross);
 | ||||
|             processed = true; | ||||
|  | @ -882,18 +770,7 @@ void GLGizmosManager::update_after_undo_redo(const UndoRedo::Snapshot& snapshot) | |||
|     m_serializing = false; | ||||
|     if (m_current == SlaSupports | ||||
|      && snapshot.snapshot_data.flags & UndoRedo::SnapshotData::RECALCULATE_SLA_SUPPORTS) | ||||
|         dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports])->reslice_SLA_supports(true); | ||||
| } | ||||
| 
 | ||||
| void GLGizmosManager::reset() | ||||
| { | ||||
|     for (GizmosMap::value_type& gizmo : m_gizmos) | ||||
|     { | ||||
|         delete gizmo.second; | ||||
|         gizmo.second = nullptr; | ||||
|     } | ||||
| 
 | ||||
|     m_gizmos.clear(); | ||||
|         dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->reslice_SLA_supports(true); | ||||
| } | ||||
| 
 | ||||
| void GLGizmosManager::render_background(float left, float top, float right, float bottom, float border) const | ||||
|  | @ -911,7 +788,7 @@ void GLGizmosManager::render_background(float left, float top, float right, floa | |||
|         float internal_top = top - border; | ||||
|         float internal_bottom = bottom + border; | ||||
| 
 | ||||
|         float left_uv = 0.0f; | ||||
|         // float left_uv = 0.0f;
 | ||||
|         float right_uv = 1.0f; | ||||
|         float top_uv = 1.0f; | ||||
|         float bottom_uv = 0.0f; | ||||
|  | @ -981,33 +858,32 @@ void GLGizmosManager::do_render_overlay() const | |||
|     float scaled_icons_size = m_overlay_icons_size * m_overlay_scale * inv_zoom; | ||||
|     float scaled_stride_y = scaled_icons_size + scaled_gap_y; | ||||
|     unsigned int icons_texture_id = m_icons_texture.get_id(); | ||||
|     unsigned int tex_width = m_icons_texture.get_width(); | ||||
|     unsigned int tex_height = m_icons_texture.get_height(); | ||||
|     float inv_tex_width = (tex_width != 0) ? 1.0f / (float)tex_width : 0.0f; | ||||
|     float inv_tex_height = (tex_height != 0) ? 1.0f / (float)tex_height : 0.0f; | ||||
|     int tex_width = m_icons_texture.get_width(); | ||||
|     int tex_height = m_icons_texture.get_height(); | ||||
|     float inv_tex_width = (tex_width != 0) ? 1.0f / tex_width : 0.0f; | ||||
|     float inv_tex_height = (tex_height != 0) ? 1.0f / tex_height : 0.0f; | ||||
| 
 | ||||
|     if ((icons_texture_id == 0) || (tex_width <= 0) || (tex_height <= 0)) | ||||
|         return; | ||||
| 
 | ||||
|     for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) | ||||
|     for (size_t idx : get_selectable_idxs()) | ||||
|     { | ||||
|         if ((it->second == nullptr) || !it->second->is_selectable()) | ||||
|             continue; | ||||
|         GLGizmoBase* gizmo = m_gizmos[idx].get(); | ||||
| 
 | ||||
|         unsigned int sprite_id = it->second->get_sprite_id(); | ||||
|         GLGizmoBase::EState state = it->second->get_state(); | ||||
|         unsigned int sprite_id = gizmo->get_sprite_id(); | ||||
|         int icon_idx = m_current == idx ? 2 : (m_hover == idx ? 1 : 0); | ||||
| 
 | ||||
|         float u_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_width; | ||||
|         float v_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_height; | ||||
|         float v_top = sprite_id * v_icon_size; | ||||
|         float u_left = state * u_icon_size; | ||||
|         float u_left = icon_idx * u_icon_size; | ||||
|         float v_bottom = v_top + v_icon_size; | ||||
|         float u_right = u_left + u_icon_size; | ||||
| 
 | ||||
|         GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); | ||||
|         if (it->second->get_state() == GLGizmoBase::On) { | ||||
|             float toolbar_top = (float)cnv_h - m_parent.get_view_toolbar_height(); | ||||
|             it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top); | ||||
|         if (idx == m_current) { | ||||
|             float toolbar_top = cnv_h - m_parent.get_view_toolbar_height(); | ||||
|             gizmo->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top); | ||||
|         } | ||||
|         top_y -= scaled_stride_y; | ||||
|     } | ||||
|  | @ -1021,13 +897,14 @@ float GLGizmosManager::get_total_overlay_height() const | |||
|     float scaled_stride_y = scaled_icons_size + scaled_gap_y; | ||||
|     float height = 2.0f * scaled_border; | ||||
| 
 | ||||
|     for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) | ||||
|     /*for (size_t idx=0; idx<m_gizmos.size(); ++idx)
 | ||||
|     { | ||||
|         if ((it->second == nullptr) || !it->second->is_selectable()) | ||||
|         if ((m_gizmos[idx] == nullptr) || !m_gizmos[idx]->is_selectable()) | ||||
|             continue; | ||||
| 
 | ||||
|         height += scaled_stride_y; | ||||
|     } | ||||
|     }*/ | ||||
|     height += get_selectable_idxs().size() * scaled_stride_y; | ||||
| 
 | ||||
|     return height - scaled_gap_y; | ||||
| } | ||||
|  | @ -1039,19 +916,21 @@ float GLGizmosManager::get_total_overlay_width() const | |||
| 
 | ||||
| GLGizmoBase* GLGizmosManager::get_current() const | ||||
| { | ||||
|     GizmosMap::const_iterator it = m_gizmos.find(m_current); | ||||
|     return (it != m_gizmos.end()) ? it->second : nullptr; | ||||
|     if (m_current==Undefined || m_gizmos.empty()) | ||||
|         return nullptr; | ||||
|     else | ||||
|         return m_gizmos[m_current].get(); | ||||
| } | ||||
| 
 | ||||
| bool GLGizmosManager::generate_icons_texture() const | ||||
| { | ||||
|     std::string path = resources_dir() + "/icons/"; | ||||
|     std::vector<std::string> filenames; | ||||
|     for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) | ||||
|     for (size_t idx=0; idx<m_gizmos.size(); ++idx) | ||||
|     { | ||||
|         if (it->second != nullptr) | ||||
|         if (m_gizmos[idx] != nullptr) | ||||
|         { | ||||
|             const std::string& icon_filename = it->second->get_icon_filename(); | ||||
|             const std::string& icon_filename = m_gizmos[idx]->get_icon_filename(); | ||||
|             if (!icon_filename.empty()) | ||||
|                 filenames.push_back(path + icon_filename); | ||||
|         } | ||||
|  | @ -1074,74 +953,9 @@ void GLGizmosManager::update_on_off_state(const Vec2d& mouse_pos) | |||
|     if (!m_enabled) | ||||
|         return; | ||||
| 
 | ||||
|     float cnv_h = (float)m_parent.get_canvas_size().get_height(); | ||||
|     float height = get_total_overlay_height(); | ||||
| 
 | ||||
|     float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; | ||||
|     float scaled_border = m_overlay_border * m_overlay_scale; | ||||
|     float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; | ||||
|     float scaled_stride_y = scaled_icons_size + scaled_gap_y; | ||||
|     float top_y = 0.5f * (cnv_h - height) + scaled_border; | ||||
| 
 | ||||
|     auto inside = [scaled_border, scaled_icons_size, &mouse_pos](float top_y) { | ||||
|         return (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); | ||||
|     }; | ||||
| 
 | ||||
|     bool could_activate = true; | ||||
|     for (std::pair<const GLGizmosManager::EType, GLGizmoBase*> &type_and_gizmo : m_gizmos) | ||||
|     { | ||||
|         GLGizmoBase *gizmo = type_and_gizmo.second; | ||||
|         if ((gizmo == nullptr) || !gizmo->is_selectable()) | ||||
|             continue; | ||||
| 
 | ||||
|         if (! (gizmo->is_activable() && inside(top_y))) { | ||||
|             gizmo->set_state(GLGizmoBase::Off); | ||||
|             if (gizmo->get_state() != GLGizmoBase::Off) { | ||||
|                 // Gizmo refused to leave it's active state. Don't try to select
 | ||||
|                 could_activate = false; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         top_y += scaled_stride_y; | ||||
|     } | ||||
| 
 | ||||
|     // We may change m_current soon. If we did it during following loop, gizmos that take undo/redo snapshots
 | ||||
|     // in their on_set_state function could snapshot a state with the new gizmo already active.
 | ||||
|     // Therefore, just remember what needs to be done and actually change m_current afterwards.
 | ||||
|     EType new_current = m_current; | ||||
|     top_y = 0.5f * (cnv_h - height) + scaled_border; | ||||
|     for (std::pair<const GLGizmosManager::EType, GLGizmoBase*> &type_and_gizmo : m_gizmos) | ||||
|     { | ||||
|         GLGizmoBase *gizmo = type_and_gizmo.second; | ||||
|         if ((gizmo == nullptr) || !gizmo->is_selectable()) | ||||
|             continue; | ||||
| 
 | ||||
|         if (gizmo->is_activable() && inside(top_y)) | ||||
|         { | ||||
|             if ((gizmo->get_state() == GLGizmoBase::On)) | ||||
|             { | ||||
|                 gizmo->set_state(GLGizmoBase::Off); | ||||
|                 if (gizmo->get_state() == GLGizmoBase::Off) { | ||||
|                     gizmo->set_state(GLGizmoBase::Hover); | ||||
|                     new_current = Undefined; | ||||
|                 } | ||||
|             } | ||||
|             else if ((gizmo->get_state() == GLGizmoBase::Hover) && could_activate) | ||||
|             { | ||||
|                 gizmo->set_state(GLGizmoBase::On); | ||||
|                 new_current = type_and_gizmo.first; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         top_y += scaled_stride_y; | ||||
|     } | ||||
|     m_current = new_current; | ||||
| 
 | ||||
|     if (could_activate) { | ||||
|         GizmosMap::iterator it = m_gizmos.find(m_current); | ||||
|         if ((it != m_gizmos.end()) && (it->second != nullptr) && (it->second->get_state() != GLGizmoBase::On)) | ||||
|             it->second->set_state(GLGizmoBase::On); | ||||
|     } | ||||
|     size_t idx = get_gizmo_idx_from_mouse(mouse_pos); | ||||
|     if (idx != Undefined && m_gizmos[idx]->is_activable() && m_hover == idx) | ||||
|         activate_gizmo(m_current == idx ? Undefined : (EType)idx); | ||||
| } | ||||
| 
 | ||||
| std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos) | ||||
|  | @ -1151,60 +965,37 @@ std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos) | |||
|     if (!m_enabled) | ||||
|         return name; | ||||
| 
 | ||||
|     float cnv_h = (float)m_parent.get_canvas_size().get_height(); | ||||
|     float height = get_total_overlay_height(); | ||||
|     float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; | ||||
|     float scaled_border = m_overlay_border * m_overlay_scale; | ||||
|     float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; | ||||
|     float scaled_stride_y = scaled_icons_size + scaled_gap_y; | ||||
|     float top_y = 0.5f * (cnv_h - height) + scaled_border; | ||||
|     m_hover = Undefined; | ||||
| 
 | ||||
|     for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) | ||||
|     { | ||||
|         if ((it->second == nullptr) || !it->second->is_selectable()) | ||||
|             continue; | ||||
|     size_t idx = get_gizmo_idx_from_mouse(mouse_pos); | ||||
|     if (idx != Undefined) { | ||||
|         name = m_gizmos[idx]->get_name(); | ||||
| 
 | ||||
|         bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); | ||||
|         if (inside) | ||||
|             name = it->second->get_name(); | ||||
| 
 | ||||
|         if (it->second->is_activable() && (it->second->get_state() != GLGizmoBase::On)) | ||||
|             it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); | ||||
| 
 | ||||
|         top_y += scaled_stride_y; | ||||
|         if (m_gizmos[idx]->is_activable()) | ||||
|             m_hover = (EType)idx; | ||||
|     } | ||||
| 
 | ||||
|     return name; | ||||
| } | ||||
| 
 | ||||
| bool GLGizmosManager::overlay_contains_mouse(const Vec2d& mouse_pos) const | ||||
| void GLGizmosManager::activate_gizmo(EType type) | ||||
| { | ||||
|     if (!m_enabled) | ||||
|         return false; | ||||
|     if (m_gizmos.empty() || m_current == type) | ||||
|         return; | ||||
| 
 | ||||
|     float cnv_h = (float)m_parent.get_canvas_size().get_height(); | ||||
|     float height = get_total_overlay_height(); | ||||
| 
 | ||||
|     float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; | ||||
|     float scaled_border = m_overlay_border * m_overlay_scale; | ||||
|     float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; | ||||
|     float scaled_stride_y = scaled_icons_size + scaled_gap_y; | ||||
|     float top_y = 0.5f * (cnv_h - height) + scaled_border; | ||||
| 
 | ||||
|     for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) | ||||
|     { | ||||
|         if ((it->second == nullptr) || !it->second->is_selectable()) | ||||
|             continue; | ||||
| 
 | ||||
|         if ((scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size)) | ||||
|             return true; | ||||
| 
 | ||||
|         top_y += scaled_stride_y; | ||||
|     if (m_current != Undefined) { | ||||
|         m_gizmos[m_current]->set_state(GLGizmoBase::Off); | ||||
|         if (m_gizmos[m_current]->get_state() != GLGizmoBase::Off) | ||||
|             return; // gizmo refused to be turned off, do nothing.
 | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
|     if (type != Undefined) | ||||
|         m_gizmos[type]->set_state(GLGizmoBase::On); | ||||
| 
 | ||||
|     m_current = type; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| bool GLGizmosManager::grabber_contains_mouse() const | ||||
| { | ||||
|     if (!m_enabled) | ||||
|  |  | |||
|  | @ -54,25 +54,30 @@ public: | |||
| 
 | ||||
|     enum EType : unsigned char | ||||
|     { | ||||
|         Undefined, | ||||
|         Move, | ||||
|         Scale, | ||||
|         Rotate, | ||||
|         Flatten, | ||||
|         Cut, | ||||
|         SlaSupports, | ||||
|         Num_Types | ||||
|         Undefined | ||||
|     }; | ||||
| 
 | ||||
| private: | ||||
|     GLCanvas3D& m_parent; | ||||
|     bool m_enabled; | ||||
|     typedef std::map<EType, GLGizmoBase*> GizmosMap; | ||||
|     GizmosMap m_gizmos; | ||||
|     std::vector<std::unique_ptr<GLGizmoBase>> m_gizmos; | ||||
|     mutable GLTexture m_icons_texture; | ||||
|     mutable bool m_icons_texture_dirty; | ||||
|     BackgroundTexture m_background_texture; | ||||
|     EType m_current; | ||||
|     EType m_hover; | ||||
| 
 | ||||
|     std::vector<size_t> get_selectable_idxs() const; | ||||
|     std::vector<size_t> get_activable_idxs() const; | ||||
|     size_t get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const; | ||||
| 
 | ||||
|     void activate_gizmo(EType type); | ||||
| 
 | ||||
|     float m_overlay_icons_size; | ||||
|     float m_overlay_scale; | ||||
|  | @ -98,7 +103,6 @@ private: | |||
| 
 | ||||
| public: | ||||
|     explicit GLGizmosManager(GLCanvas3D& parent); | ||||
|     ~GLGizmosManager(); | ||||
| 
 | ||||
|     bool init(); | ||||
| 
 | ||||
|  | @ -112,16 +116,8 @@ public: | |||
| 
 | ||||
|         ar(m_current); | ||||
| 
 | ||||
|         GLGizmoBase* curr = get_current(); | ||||
| 		for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { | ||||
| 			GLGizmoBase* gizmo = it->second; | ||||
| 			if (gizmo != nullptr) { | ||||
| 				gizmo->set_hover_id(-1); | ||||
| 				gizmo->set_state((it->second == curr) ? GLGizmoBase::On : GLGizmoBase::Off); | ||||
| 				if (gizmo == curr) | ||||
| 					gizmo->load(ar); | ||||
| 			} | ||||
| 		} | ||||
|         if (m_current != Undefined) | ||||
|             m_gizmos[m_current]->load(ar); | ||||
|     } | ||||
| 
 | ||||
|     template<class Archive> | ||||
|  | @ -132,9 +128,8 @@ public: | |||
| 
 | ||||
|         ar(m_current); | ||||
| 
 | ||||
|         GLGizmoBase* curr = get_current(); | ||||
|         if (curr != nullptr) | ||||
|             curr->save(ar); | ||||
|         if (m_current != Undefined && !m_gizmos.empty()) | ||||
|             m_gizmos[m_current]->save(ar); | ||||
|     } | ||||
| 
 | ||||
|     bool is_enabled() const { return m_enabled; } | ||||
|  | @ -195,8 +190,6 @@ public: | |||
|     void update_after_undo_redo(const UndoRedo::Snapshot& snapshot); | ||||
| 
 | ||||
| private: | ||||
|     void reset(); | ||||
| 
 | ||||
|     void render_background(float left, float top, float right, float bottom, float border) const; | ||||
|     void do_render_overlay() const; | ||||
| 
 | ||||
|  | @ -209,7 +202,6 @@ private: | |||
| 
 | ||||
|     void update_on_off_state(const Vec2d& mouse_pos); | ||||
|     std::string update_hover_state(const Vec2d& mouse_pos); | ||||
|     bool overlay_contains_mouse(const Vec2d& mouse_pos) const; | ||||
|     bool grabber_contains_mouse() const; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -50,8 +50,9 @@ class MainFrame : public DPIFrame | |||
|     wxString    m_qs_last_input_file = wxEmptyString; | ||||
|     wxString    m_qs_last_output_file = wxEmptyString; | ||||
|     wxString    m_last_config = wxEmptyString; | ||||
| 
 | ||||
|     wxMenuItem* m_menu_item_repeat { nullptr }; | ||||
| #if 0 | ||||
|     wxMenuItem* m_menu_item_repeat { nullptr }; // doesn't used now
 | ||||
| #endif | ||||
|     wxMenuItem* m_menu_item_reslice_now { nullptr }; | ||||
| 
 | ||||
|     PrintHostQueueDialog *m_printhost_queue_dlg; | ||||
|  |  | |||
|  | @ -457,8 +457,9 @@ void ConfigOptionsGroup::Show(const bool show) | |||
| bool ConfigOptionsGroup::update_visibility(ConfigOptionMode mode) { | ||||
|     if (m_options_mode.empty()) | ||||
|         return true; | ||||
|     if (m_grid_sizer->GetEffectiveRowsCount() != m_options_mode.size() && | ||||
|         m_options_mode.size() == 1) | ||||
|     int opt_mode_size = m_options_mode.size(); | ||||
|     if (m_grid_sizer->GetEffectiveRowsCount() != opt_mode_size && | ||||
|         opt_mode_size == 1) | ||||
|         return m_options_mode[0] <= mode; | ||||
| 
 | ||||
|     Show(true); | ||||
|  | @ -476,7 +477,7 @@ bool ConfigOptionsGroup::update_visibility(ConfigOptionMode mode) { | |||
|         coef+= cols; | ||||
| 	} | ||||
| 
 | ||||
|     if (hidden_row_cnt == m_options_mode.size()) { | ||||
|     if (hidden_row_cnt == opt_mode_size) { | ||||
|         sizer->ShowItems(false); | ||||
|         return false; | ||||
|     } | ||||
|  |  | |||
|  | @ -510,24 +510,27 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) : | |||
|     option.opt.sidetext = ""; | ||||
|     line.append_option(option); | ||||
| 
 | ||||
|     auto wiping_dialog_btn = [config, this](wxWindow* parent) { | ||||
|     auto wiping_dialog_btn = [this](wxWindow* parent) { | ||||
|         m_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); | ||||
|         m_wiping_dialog_button->SetFont(wxGetApp().normal_font()); | ||||
|         auto sizer = new wxBoxSizer(wxHORIZONTAL); | ||||
|         sizer->Add(m_wiping_dialog_button, 0, wxALIGN_CENTER_VERTICAL); | ||||
|         m_wiping_dialog_button->Bind(wxEVT_BUTTON, ([parent](wxCommandEvent& e) | ||||
|         { | ||||
|             auto &config = wxGetApp().preset_bundle->project_config; | ||||
|             const std::vector<double> &init_matrix = (config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values; | ||||
|             const std::vector<double> &init_extruders = (config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values; | ||||
|             auto &project_config = wxGetApp().preset_bundle->project_config; | ||||
|             const std::vector<double> &init_matrix = (project_config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values; | ||||
|             const std::vector<double> &init_extruders = (project_config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values; | ||||
| 
 | ||||
|             WipingDialog dlg(parent, cast<float>(init_matrix), cast<float>(init_extruders)); | ||||
|             const DynamicPrintConfig* config = &wxGetApp().preset_bundle->printers.get_edited_preset().config; | ||||
|             const std::vector<std::string> &extruder_colours = (config->option<ConfigOptionStrings>("extruder_colour"))->values; | ||||
| 
 | ||||
|             WipingDialog dlg(parent, cast<float>(init_matrix), cast<float>(init_extruders), extruder_colours); | ||||
| 
 | ||||
|             if (dlg.ShowModal() == wxID_OK) { | ||||
|                 std::vector<float> matrix = dlg.get_matrix(); | ||||
|                 std::vector<float> extruders = dlg.get_extruders(); | ||||
|                 (config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = std::vector<double>(matrix.begin(), matrix.end()); | ||||
|                 (config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values = std::vector<double>(extruders.begin(), extruders.end()); | ||||
|                 (project_config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = std::vector<double>(matrix.begin(), matrix.end()); | ||||
|                 (project_config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values = std::vector<double>(extruders.begin(), extruders.end()); | ||||
|                 wxPostEvent(parent, SimpleEvent(EVT_SCHEDULE_BACKGROUND_PROCESS, parent)); | ||||
|             } | ||||
|         })); | ||||
|  | @ -1072,8 +1075,6 @@ void Sidebar::show_info_sizer() | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     const ModelInstance* model_instance = !model_object->instances.empty() ? model_object->instances.front() : nullptr; | ||||
| 
 | ||||
|     auto size = model_object->bounding_box().size(); | ||||
|     p->object_info->info_size->SetLabel(wxString::Format("%.2f x %.2f x %.2f",size(0), size(1), size(2))); | ||||
|     p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast<int>(model_object->materials_count()))); | ||||
|  | @ -2284,20 +2285,20 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_ | |||
| 
 | ||||
|             if (! is_project_file) { | ||||
|                 if (model.looks_like_multipart_object()) { | ||||
|                     wxMessageDialog dlg(q, _(L( | ||||
|                     wxMessageDialog msg_dlg(q, _(L( | ||||
|                         "This file contains several objects positioned at multiple heights. " | ||||
|                         "Instead of considering them as multiple objects, should I consider\n" | ||||
|                         "this file as a single object having multiple parts?\n" | ||||
|                         )), _(L("Multi-part object detected")), wxICON_WARNING | wxYES | wxNO); | ||||
|                     if (dlg.ShowModal() == wxID_YES) { | ||||
|                     if (msg_dlg.ShowModal() == wxID_YES) { | ||||
|                         model.convert_multipart_object(nozzle_dmrs->values.size()); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             else if ((wxGetApp().get_mode() == comSimple) && (type_3mf || type_any_amf) && model_has_advanced_features(model)) { | ||||
|                 wxMessageDialog dlg(q, _(L("This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode?\n")), | ||||
|                 wxMessageDialog msg_dlg(q, _(L("This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode?\n")), | ||||
|                     _(L("Detected advanced data")), wxICON_WARNING | wxYES | wxNO); | ||||
|                 if (dlg.ShowModal() == wxID_YES) | ||||
|                 if (msg_dlg.ShowModal() == wxID_YES) | ||||
|                 { | ||||
|                     Slic3r::GUI::wxGetApp().save_mode(comAdvanced); | ||||
|                     view3D->set_as_dirty(); | ||||
|  | @ -2336,12 +2337,12 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_ | |||
|     } | ||||
| 
 | ||||
|     if (new_model != nullptr && new_model->objects.size() > 1) { | ||||
|         wxMessageDialog dlg(q, _(L( | ||||
|         wxMessageDialog msg_dlg(q, _(L( | ||||
|                 "Multiple objects were loaded for a multi-material printer.\n" | ||||
|                 "Instead of considering them as multiple objects, should I consider\n" | ||||
|                 "these files to represent a single object having multiple parts?\n" | ||||
|             )), _(L("Multi-part object detected")), wxICON_WARNING | wxYES | wxNO); | ||||
|         if (dlg.ShowModal() == wxID_YES) { | ||||
|         if (msg_dlg.ShowModal() == wxID_YES) { | ||||
|             new_model->convert_multipart_object(nozzle_dmrs->values.size()); | ||||
|         } | ||||
| 
 | ||||
|  | @ -3260,6 +3261,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) | |||
|             else | ||||
|                 this->update_sla_scene(); | ||||
|             break; | ||||
|         default: break; | ||||
|         } | ||||
|     } else if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_PREVIEW) { | ||||
|         // Update the SLA preview. Only called if not RELOAD_SLA_SUPPORT_POINTS, as the block above will refresh the preview anyways.
 | ||||
|  | @ -3279,6 +3281,7 @@ void Plater::priv::on_slicing_completed(wxCommandEvent &) | |||
|         else | ||||
|             this->update_sla_scene(); | ||||
|         break; | ||||
|     default: break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -3325,6 +3328,7 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) | |||
|         else | ||||
|             this->update_sla_scene(); | ||||
|         break; | ||||
|     default: break; | ||||
|     } | ||||
| 
 | ||||
|     if (canceled) { | ||||
|  |  | |||
|  | @ -697,6 +697,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool | |||
| 		config.option<ConfigOptionString>("default_sla_print_profile", true); | ||||
| 		config.option<ConfigOptionString>("default_sla_material_profile", true); | ||||
| 		break; | ||||
|     default: break; | ||||
| 	} | ||||
| 
 | ||||
|     // 1) Create a name from the file name.
 | ||||
|  | @ -805,6 +806,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool | |||
|         load_preset(this->sla_materials, 1, "sla_material_settings_id"); | ||||
|         load_preset(this->printers,      2, "printer_settings_id"); | ||||
|         break; | ||||
|     default: break; | ||||
|     } | ||||
| 
 | ||||
| 	this->update_compatible(false); | ||||
|  | @ -1349,6 +1351,7 @@ void PresetBundle::update_compatible(bool select_other_if_incompatible) | |||
|                 [&prefered_sla_material_profile](const std::string& profile_name){ return profile_name == prefered_sla_material_profile; }); | ||||
| 		break; | ||||
| 	} | ||||
|     default: break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -89,7 +89,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle | |||
|     double bridge_flow_ratio                = print_config.opt_float("bridge_flow_ratio"); | ||||
|     double perimeter_speed                  = print_config.opt_float("perimeter_speed"); | ||||
|     double external_perimeter_speed         = print_config.get_abs_value("external_perimeter_speed", perimeter_speed); | ||||
|     double gap_fill_speed                   = print_config.opt_float("gap_fill_speed"); | ||||
|     // double gap_fill_speed                   = print_config.opt_float("gap_fill_speed");
 | ||||
|     double infill_speed                     = print_config.opt_float("infill_speed"); | ||||
|     double small_perimeter_speed            = print_config.get_abs_value("small_perimeter_speed", perimeter_speed); | ||||
|     double solid_infill_speed               = print_config.get_abs_value("solid_infill_speed", infill_speed); | ||||
|  | @ -123,7 +123,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle | |||
|     // Current filament values
 | ||||
|     double filament_diameter                = filament_config.opt_float("filament_diameter", 0); | ||||
|     double filament_crossection             = M_PI * 0.25 * filament_diameter * filament_diameter; | ||||
|     double extrusion_multiplier             = filament_config.opt_float("extrusion_multiplier", 0); | ||||
|     // double extrusion_multiplier             = filament_config.opt_float("extrusion_multiplier", 0);
 | ||||
|     // The following value will be annotated by this hint, so it does not take part in the calculation.
 | ||||
| //    double filament_max_volumetric_speed    = filament_config.opt_float("filament_max_volumetric_speed", 0);
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -310,7 +310,7 @@ void Selection::remove_volume(unsigned int object_idx, unsigned int volume_idx) | |||
|     for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) | ||||
|     { | ||||
|         GLVolume* v = (*m_volumes)[i]; | ||||
|         if ((v->object_idx() == object_idx) && (v->volume_idx() == volume_idx)) | ||||
|         if ((v->object_idx() == (int)object_idx) && (v->volume_idx() == (int)volume_idx)) | ||||
|             do_remove_volume(i); | ||||
|     } | ||||
| 
 | ||||
|  | @ -992,7 +992,7 @@ void Selection::translate(unsigned int object_idx, const Vec3d& displacement) | |||
|     for (unsigned int i : m_list) | ||||
|     { | ||||
|         GLVolume* v = (*m_volumes)[i]; | ||||
|         if (v->object_idx() == object_idx) | ||||
|         if (v->object_idx() == (int)object_idx) | ||||
|             v->set_instance_offset(v->get_instance_offset() + displacement); | ||||
|     } | ||||
| 
 | ||||
|  | @ -1037,7 +1037,7 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co | |||
|     for (unsigned int i : m_list) | ||||
|     { | ||||
|         GLVolume* v = (*m_volumes)[i]; | ||||
|         if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) | ||||
|         if ((v->object_idx() == (int)object_idx) && (v->instance_idx() == (int)instance_idx)) | ||||
|             v->set_instance_offset(v->get_instance_offset() + displacement); | ||||
|     } | ||||
| 
 | ||||
|  | @ -1063,7 +1063,7 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co | |||
|                 continue; | ||||
| 
 | ||||
|             GLVolume* v = (*m_volumes)[j]; | ||||
|             if ((v->object_idx() != object_idx) || (v->instance_idx() != instance_idx)) | ||||
|             if ((v->object_idx() != object_idx) || (v->instance_idx() != (int)instance_idx)) | ||||
|                 continue; | ||||
| 
 | ||||
|             v->set_instance_offset(v->get_instance_offset() + displacement); | ||||
|  | @ -1154,7 +1154,7 @@ void Selection::erase() | |||
|         for (const ItemForDelete& i : items_set) { | ||||
|             if (i.type == ItemType::itVolume) { | ||||
|                 const int vol_in_obj_cnt = volumes_in_obj.find(i.obj_idx) == volumes_in_obj.end() ? 0 : volumes_in_obj.at(i.obj_idx); | ||||
|                 if (vol_in_obj_cnt == m_model->objects[i.obj_idx]->volumes.size()) { | ||||
|                 if (vol_in_obj_cnt == (int)m_model->objects[i.obj_idx]->volumes.size()) { | ||||
|                     if (i.sub_obj_idx == vol_in_obj_cnt - 1) | ||||
|                         items.emplace_back(ItemType::itObject, i.obj_idx, 0); | ||||
|                     continue; | ||||
|  | @ -1389,7 +1389,7 @@ std::vector<unsigned int> Selection::get_volume_idxs_from_object(unsigned int ob | |||
| 
 | ||||
|     for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) | ||||
|     { | ||||
|         if ((*m_volumes)[i]->object_idx() == object_idx) | ||||
|         if ((*m_volumes)[i]->object_idx() == (int)object_idx) | ||||
|             idxs.push_back(i); | ||||
|     } | ||||
| 
 | ||||
|  | @ -1403,7 +1403,7 @@ std::vector<unsigned int> Selection::get_volume_idxs_from_instance(unsigned int | |||
|     for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) | ||||
|     { | ||||
|         const GLVolume* v = (*m_volumes)[i]; | ||||
|         if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) | ||||
|         if ((v->object_idx() == (int)object_idx) && (v->instance_idx() == (int)instance_idx)) | ||||
|             idxs.push_back(i); | ||||
|     } | ||||
| 
 | ||||
|  | @ -1417,9 +1417,9 @@ std::vector<unsigned int> Selection::get_volume_idxs_from_volume(unsigned int ob | |||
|     for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) | ||||
|     { | ||||
|         const GLVolume* v = (*m_volumes)[i]; | ||||
|         if ((v->object_idx() == object_idx) && (v->volume_idx() == volume_idx)) | ||||
|         if ((v->object_idx() == (int)object_idx) && (v->volume_idx() == (int)volume_idx)) | ||||
|         { | ||||
|             if ((instance_idx != -1) && (v->instance_idx() == instance_idx)) | ||||
|             if (((int)instance_idx != -1) && (v->instance_idx() == (int)instance_idx)) | ||||
|                 idxs.push_back(i); | ||||
|         } | ||||
|     } | ||||
|  | @ -1607,7 +1607,7 @@ void Selection::update_type() | |||
|             } | ||||
|             else | ||||
|             { | ||||
|                 int sels_cntr = 0; | ||||
|                 unsigned int sels_cntr = 0; | ||||
|                 for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it) | ||||
|                 { | ||||
|                     const ModelObject* model_object = m_model->objects[it->first]; | ||||
|  | @ -1759,7 +1759,7 @@ void Selection::do_remove_instance(unsigned int object_idx, unsigned int instanc | |||
|     for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) | ||||
|     { | ||||
|         GLVolume* v = (*m_volumes)[i]; | ||||
|         if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) | ||||
|         if ((v->object_idx() == (int)object_idx) && (v->instance_idx() == (int)instance_idx)) | ||||
|             do_remove_volume(i); | ||||
|     } | ||||
| } | ||||
|  | @ -1769,7 +1769,7 @@ void Selection::do_remove_object(unsigned int object_idx) | |||
|     for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) | ||||
|     { | ||||
|         GLVolume* v = (*m_volumes)[i]; | ||||
|         if (v->object_idx() == object_idx) | ||||
|         if (v->object_idx() == (int)object_idx) | ||||
|             do_remove_volume(i); | ||||
|     } | ||||
| } | ||||
|  | @ -1836,7 +1836,6 @@ void Selection::render_synchronized_volumes() const | |||
|     { | ||||
|         const GLVolume* volume = (*m_volumes)[i]; | ||||
|         int object_idx = volume->object_idx(); | ||||
|         int instance_idx = volume->instance_idx(); | ||||
|         int volume_idx = volume->volume_idx(); | ||||
|         for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) | ||||
|         { | ||||
|  |  | |||
|  | @ -995,6 +995,7 @@ void Tab::update_preset_description_line() | |||
|                     description_line += "\n\n\t" + _(L("default SLA print profile")) + ": \n\t\t" + default_sla_print_profile; | ||||
|                 break; | ||||
|             } | ||||
|             default: break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -1263,251 +1264,9 @@ void TabPrint::update() | |||
|     if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) | ||||
|         return; // ys_FIXME
 | ||||
| 
 | ||||
|     /*
 | ||||
|     // #ys_FIXME_to_delete
 | ||||
|     //! Temporary workaround for the correct updates of the TextCtrl (like "layer_height"):
 | ||||
|     // KillFocus() for the wxSpinCtrl use CallAfter function. So,
 | ||||
|     // to except the duplicate call of the update() after dialog->ShowModal(),
 | ||||
|     // let check if this process is already started.
 | ||||
|     if (is_msg_dlg_already_exist) | ||||
|         return; | ||||
|         */ | ||||
| 
 | ||||
|     m_update_cnt++; | ||||
| //	Freeze();
 | ||||
| 
 | ||||
|     /* #ys_FIXME_delete_after_testing (refactoring)
 | ||||
|      * | ||||
|     // layer_height shouldn't be equal to zero
 | ||||
|     if (m_config->opt_float("layer_height") < EPSILON) | ||||
|     { | ||||
|         const wxString msg_text = _(L("Zero layer height is not valid.\n\nThe layer height will be reset to 0.01.")); | ||||
|         wxMessageDialog dialog(parent(), msg_text, _(L("Layer height")), wxICON_WARNING | wxOK); | ||||
|         DynamicPrintConfig new_conf = *m_config; | ||||
|         is_msg_dlg_already_exist = true; | ||||
|         dialog.ShowModal(); | ||||
|         new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.01)); | ||||
|         load_config(new_conf); | ||||
|         is_msg_dlg_already_exist = false; | ||||
|     } | ||||
| 
 | ||||
|     if (fabs(m_config->option<ConfigOptionFloatOrPercent>("first_layer_height")->value - 0) < EPSILON) | ||||
|     { | ||||
|         const wxString msg_text = _(L("Zero first layer height is not valid.\n\nThe first layer height will be reset to 0.01.")); | ||||
|         wxMessageDialog dialog(parent(), msg_text, _(L("First layer height")), wxICON_WARNING | wxOK); | ||||
|         DynamicPrintConfig new_conf = *m_config; | ||||
|         is_msg_dlg_already_exist = true; | ||||
|         dialog.ShowModal(); | ||||
|         new_conf.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.01, false)); | ||||
|         load_config(new_conf); | ||||
|         is_msg_dlg_already_exist = false; | ||||
|     } | ||||
| 
 | ||||
|     double fill_density = m_config->option<ConfigOptionPercent>("fill_density")->value; | ||||
| 
 | ||||
|     if (m_config->opt_bool("spiral_vase") && | ||||
|         !(m_config->opt_int("perimeters") == 1 && m_config->opt_int("top_solid_layers") == 0 && | ||||
|         fill_density == 0)) { | ||||
|         wxString msg_text = _(L("The Spiral Vase mode requires:\n" | ||||
|             "- one perimeter\n" | ||||
|             "- no top solid layers\n" | ||||
|             "- 0% fill density\n" | ||||
|             "- no support material\n" | ||||
|             "- no ensure_vertical_shell_thickness\n" | ||||
|             "\nShall I adjust those settings in order to enable Spiral Vase?")); | ||||
|         wxMessageDialog dialog(parent(), msg_text, _(L("Spiral Vase")), wxICON_WARNING | wxYES | wxNO); | ||||
|         DynamicPrintConfig new_conf = *m_config; | ||||
|         if (dialog.ShowModal() == wxID_YES) { | ||||
|             new_conf.set_key_value("perimeters", new ConfigOptionInt(1)); | ||||
|             new_conf.set_key_value("top_solid_layers", new ConfigOptionInt(0)); | ||||
|             new_conf.set_key_value("fill_density", new ConfigOptionPercent(0)); | ||||
|             new_conf.set_key_value("support_material", new ConfigOptionBool(false)); | ||||
|             new_conf.set_key_value("support_material_enforce_layers", new ConfigOptionInt(0)); | ||||
|             new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(false)); | ||||
|             fill_density = 0; | ||||
|         } | ||||
|         else { | ||||
|             new_conf.set_key_value("spiral_vase", new ConfigOptionBool(false)); | ||||
|         } | ||||
|         load_config(new_conf); | ||||
|         on_value_change("fill_density", fill_density); | ||||
|     } | ||||
| 
 | ||||
|     if (m_config->opt_bool("wipe_tower") && m_config->opt_bool("support_material") && | ||||
|         m_config->opt_float("support_material_contact_distance") > 0. && | ||||
|         (m_config->opt_int("support_material_extruder") != 0 || m_config->opt_int("support_material_interface_extruder") != 0)) { | ||||
|         wxString msg_text = _(L("The Wipe Tower currently supports the non-soluble supports only\n" | ||||
|             "if they are printed with the current extruder without triggering a tool change.\n" | ||||
|             "(both support_material_extruder and support_material_interface_extruder need to be set to 0).\n" | ||||
|             "\nShall I adjust those settings in order to enable the Wipe Tower?")); | ||||
|         wxMessageDialog dialog(parent(), msg_text, _(L("Wipe Tower")), wxICON_WARNING | wxYES | wxNO); | ||||
|         DynamicPrintConfig new_conf = *m_config; | ||||
|         if (dialog.ShowModal() == wxID_YES) { | ||||
|             new_conf.set_key_value("support_material_extruder", new ConfigOptionInt(0)); | ||||
|             new_conf.set_key_value("support_material_interface_extruder", new ConfigOptionInt(0)); | ||||
|         } | ||||
|         else | ||||
|             new_conf.set_key_value("wipe_tower", new ConfigOptionBool(false)); | ||||
|         load_config(new_conf); | ||||
|     } | ||||
| 
 | ||||
|     if (m_config->opt_bool("wipe_tower") && m_config->opt_bool("support_material") && | ||||
|         m_config->opt_float("support_material_contact_distance") == 0 && | ||||
|         !m_config->opt_bool("support_material_synchronize_layers")) { | ||||
|         wxString msg_text = _(L("For the Wipe Tower to work with the soluble supports, the support layers\n" | ||||
|             "need to be synchronized with the object layers.\n" | ||||
|             "\nShall I synchronize support layers in order to enable the Wipe Tower?")); | ||||
|         wxMessageDialog dialog(parent(), msg_text, _(L("Wipe Tower")), wxICON_WARNING | wxYES | wxNO); | ||||
|         DynamicPrintConfig new_conf = *m_config; | ||||
|         if (dialog.ShowModal() == wxID_YES) { | ||||
|             new_conf.set_key_value("support_material_synchronize_layers", new ConfigOptionBool(true)); | ||||
|         } | ||||
|         else | ||||
|             new_conf.set_key_value("wipe_tower", new ConfigOptionBool(false)); | ||||
|         load_config(new_conf); | ||||
|     } | ||||
| 
 | ||||
|     if (m_config->opt_bool("support_material")) { | ||||
|         // Ask only once.
 | ||||
|         if (!m_support_material_overhangs_queried) { | ||||
|             m_support_material_overhangs_queried = true; | ||||
|             if (!m_config->opt_bool("overhangs")/* != 1* /) {
 | ||||
|                 wxString msg_text = _(L("Supports work better, if the following feature is enabled:\n" | ||||
|                     "- Detect bridging perimeters\n" | ||||
|                     "\nShall I adjust those settings for supports?")); | ||||
|                 wxMessageDialog dialog(parent(), msg_text, _(L("Support Generator")), wxICON_WARNING | wxYES | wxNO | wxCANCEL); | ||||
|                 DynamicPrintConfig new_conf = *m_config; | ||||
|                 auto answer = dialog.ShowModal(); | ||||
|                 if (answer == wxID_YES) { | ||||
|                     // Enable "detect bridging perimeters".
 | ||||
|                     new_conf.set_key_value("overhangs", new ConfigOptionBool(true)); | ||||
|                 } else if (answer == wxID_NO) { | ||||
|                     // Do nothing, leave supports on and "detect bridging perimeters" off.
 | ||||
|                 } else if (answer == wxID_CANCEL) { | ||||
|                     // Disable supports.
 | ||||
|                     new_conf.set_key_value("support_material", new ConfigOptionBool(false)); | ||||
|                     m_support_material_overhangs_queried = false; | ||||
|                 } | ||||
|                 load_config(new_conf); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else { | ||||
|         m_support_material_overhangs_queried = false; | ||||
|     } | ||||
| 
 | ||||
|     if (m_config->option<ConfigOptionPercent>("fill_density")->value == 100) { | ||||
|         auto fill_pattern = m_config->option<ConfigOptionEnum<InfillPattern>>("fill_pattern")->value; | ||||
|         std::string str_fill_pattern = ""; | ||||
|         t_config_enum_values map_names = m_config->option<ConfigOptionEnum<InfillPattern>>("fill_pattern")->get_enum_values(); | ||||
|         for (auto it : map_names) { | ||||
|             if (fill_pattern == it.second) { | ||||
|                 str_fill_pattern = it.first; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         if (!str_fill_pattern.empty()) { | ||||
|             const std::vector<std::string> &external_fill_pattern = m_config->def()->get("top_fill_pattern")->enum_values; | ||||
|             bool correct_100p_fill = false; | ||||
|             for (const std::string &fill : external_fill_pattern) | ||||
|             { | ||||
|                 if (str_fill_pattern == fill) | ||||
|                     correct_100p_fill = true; | ||||
|             } | ||||
|             // get fill_pattern name from enum_labels for using this one at dialog_msg
 | ||||
|             str_fill_pattern = _utf8(m_config->def()->get("fill_pattern")->enum_labels[fill_pattern]); | ||||
|             if (!correct_100p_fill) { | ||||
|                 wxString msg_text = GUI::from_u8((boost::format(_utf8(L("The %1% infill pattern is not supposed to work at 100%% density.\n\n" | ||||
|                                                                "Shall I switch to rectilinear fill pattern?"))) % str_fill_pattern).str()); | ||||
|                 wxMessageDialog dialog(parent(), msg_text, _(L("Infill")), wxICON_WARNING | wxYES | wxNO); | ||||
|                 DynamicPrintConfig new_conf = *m_config; | ||||
|                 if (dialog.ShowModal() == wxID_YES) { | ||||
|                     new_conf.set_key_value("fill_pattern", new ConfigOptionEnum<InfillPattern>(ipRectilinear)); | ||||
|                     fill_density = 100; | ||||
|                 } | ||||
|                 else | ||||
|                     fill_density = m_presets->get_selected_preset().config.option<ConfigOptionPercent>("fill_density")->value; | ||||
|                 new_conf.set_key_value("fill_density", new ConfigOptionPercent(fill_density)); | ||||
|                 load_config(new_conf); | ||||
|                 on_value_change("fill_density", fill_density); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     bool have_perimeters = m_config->opt_int("perimeters") > 0; | ||||
|     for (auto el : {"extra_perimeters", "ensure_vertical_shell_thickness", "thin_walls", "overhangs", | ||||
|                     "seam_position", "external_perimeters_first", "external_perimeter_extrusion_width", | ||||
|                     "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed" }) | ||||
|         get_field(el)->toggle(have_perimeters); | ||||
| 
 | ||||
|     bool have_infill = m_config->option<ConfigOptionPercent>("fill_density")->value > 0; | ||||
|     // infill_extruder uses the same logic as in Print::extruders()
 | ||||
|     for (auto el : {"fill_pattern", "infill_every_layers", "infill_only_where_needed", | ||||
|                     "solid_infill_every_layers", "solid_infill_below_area", "infill_extruder" }) | ||||
|         get_field(el)->toggle(have_infill); | ||||
| 
 | ||||
|     bool have_solid_infill = m_config->opt_int("top_solid_layers") > 0 || m_config->opt_int("bottom_solid_layers") > 0; | ||||
|     // solid_infill_extruder uses the same logic as in Print::extruders()
 | ||||
|     for (auto el : {"top_fill_pattern", "bottom_fill_pattern", "infill_first", "solid_infill_extruder", | ||||
|                     "solid_infill_extrusion_width", "solid_infill_speed" }) | ||||
|         get_field(el)->toggle(have_solid_infill); | ||||
| 
 | ||||
|     for (auto el : {"fill_angle", "bridge_angle", "infill_extrusion_width", | ||||
|                     "infill_speed", "bridge_speed" }) | ||||
|         get_field(el)->toggle(have_infill || have_solid_infill); | ||||
| 
 | ||||
|     get_field("gap_fill_speed")->toggle(have_perimeters && have_infill); | ||||
| 
 | ||||
|     bool have_top_solid_infill = m_config->opt_int("top_solid_layers") > 0; | ||||
|     for (auto el : { "top_infill_extrusion_width", "top_solid_infill_speed" }) | ||||
|         get_field(el)->toggle(have_top_solid_infill); | ||||
| 
 | ||||
|     bool have_default_acceleration = m_config->opt_float("default_acceleration") > 0; | ||||
|     for (auto el : {"perimeter_acceleration", "infill_acceleration", | ||||
|                     "bridge_acceleration", "first_layer_acceleration" }) | ||||
|         get_field(el)->toggle(have_default_acceleration); | ||||
| 
 | ||||
|     bool have_skirt = m_config->opt_int("skirts") > 0 || m_config->opt_float("min_skirt_length") > 0; | ||||
|     for (auto el : { "skirt_distance", "skirt_height" }) | ||||
|         get_field(el)->toggle(have_skirt); | ||||
| 
 | ||||
|     bool have_brim = m_config->opt_float("brim_width") > 0; | ||||
|     // perimeter_extruder uses the same logic as in Print::extruders()
 | ||||
|     get_field("perimeter_extruder")->toggle(have_perimeters || have_brim); | ||||
| 
 | ||||
|     bool have_raft = m_config->opt_int("raft_layers") > 0; | ||||
|     bool have_support_material = m_config->opt_bool("support_material") || have_raft; | ||||
|     bool have_support_material_auto = have_support_material && m_config->opt_bool("support_material_auto"); | ||||
|     bool have_support_interface = m_config->opt_int("support_material_interface_layers") > 0; | ||||
|     bool have_support_soluble = have_support_material && m_config->opt_float("support_material_contact_distance") == 0; | ||||
|     for (auto el : {"support_material_pattern", "support_material_with_sheath", | ||||
|                     "support_material_spacing", "support_material_angle", "support_material_interface_layers", | ||||
|                     "dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance", | ||||
|                     "support_material_xy_spacing" }) | ||||
|         get_field(el)->toggle(have_support_material); | ||||
|     get_field("support_material_threshold")->toggle(have_support_material_auto); | ||||
| 
 | ||||
|     for (auto el : {"support_material_interface_spacing", "support_material_interface_extruder", | ||||
|                     "support_material_interface_speed", "support_material_interface_contact_loops" }) | ||||
|         get_field(el)->toggle(have_support_material && have_support_interface); | ||||
|     get_field("support_material_synchronize_layers")->toggle(have_support_soluble); | ||||
| 
 | ||||
|     get_field("perimeter_extrusion_width")->toggle(have_perimeters || have_skirt || have_brim); | ||||
|     get_field("support_material_extruder")->toggle(have_support_material || have_skirt); | ||||
|     get_field("support_material_speed")->toggle(have_support_material || have_brim || have_skirt); | ||||
| 
 | ||||
|     bool have_sequential_printing = m_config->opt_bool("complete_objects"); | ||||
|     for (auto el : { "extruder_clearance_radius", "extruder_clearance_height" }) | ||||
|         get_field(el)->toggle(have_sequential_printing); | ||||
| 
 | ||||
|     bool have_ooze_prevention = m_config->opt_bool("ooze_prevention"); | ||||
|     get_field("standby_temperature_delta")->toggle(have_ooze_prevention); | ||||
| 
 | ||||
|     bool have_wipe_tower = m_config->opt_bool("wipe_tower"); | ||||
|     for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging"}) | ||||
|         get_field(el)->toggle(have_wipe_tower); | ||||
|         */ | ||||
| 
 | ||||
|     m_config_manipulation.update_print_fff_config(m_config, true); | ||||
| 
 | ||||
|     m_recommended_thin_wall_thickness_description_line->SetText( | ||||
|  | @ -3826,84 +3585,6 @@ void TabSLAPrint::update() | |||
| 
 | ||||
|     m_update_cnt++; | ||||
| 
 | ||||
|     /* #ys_FIXME_delete_after_testing (refactoring)
 | ||||
|      * | ||||
|     bool supports_en = m_config->opt_bool("supports_enable"); | ||||
| 
 | ||||
|     get_field("support_head_front_diameter")->toggle(supports_en); | ||||
|     get_field("support_head_penetration")->toggle(supports_en); | ||||
|     get_field("support_head_width")->toggle(supports_en); | ||||
|     get_field("support_pillar_diameter")->toggle(supports_en); | ||||
|     get_field("support_pillar_connection_mode")->toggle(supports_en); | ||||
|     get_field("support_buildplate_only")->toggle(supports_en); | ||||
|     get_field("support_base_diameter")->toggle(supports_en); | ||||
|     get_field("support_base_height")->toggle(supports_en); | ||||
|     get_field("support_base_safety_distance")->toggle(supports_en); | ||||
|     get_field("support_critical_angle")->toggle(supports_en); | ||||
|     get_field("support_max_bridge_length")->toggle(supports_en); | ||||
|     get_field("support_max_pillar_link_distance")->toggle(supports_en); | ||||
|     get_field("support_points_density_relative")->toggle(supports_en); | ||||
|     get_field("support_points_minimal_distance")->toggle(supports_en); | ||||
| 
 | ||||
|     bool pad_en = m_config->opt_bool("pad_enable"); | ||||
| 
 | ||||
|     get_field("pad_wall_thickness")->toggle(pad_en); | ||||
|     get_field("pad_wall_height")->toggle(pad_en); | ||||
|     get_field("pad_max_merge_distance")->toggle(pad_en); | ||||
|     // get_field("pad_edge_radius")->toggle(supports_en);
 | ||||
|     get_field("pad_wall_slope")->toggle(pad_en); | ||||
|     get_field("pad_around_object")->toggle(pad_en); | ||||
| 
 | ||||
|     double head_penetration = m_config->opt_float("support_head_penetration"); | ||||
|     double head_width       = m_config->opt_float("support_head_width"); | ||||
|     if (head_penetration > head_width) { | ||||
|         wxString msg_text = _( | ||||
|             L("Head penetration should not be greater than the head width.")); | ||||
| 
 | ||||
|         wxMessageDialog dialog(parent(), | ||||
|                                msg_text, | ||||
|                                _(L("Invalid Head penetration")), | ||||
|                                wxICON_WARNING | wxOK); | ||||
| 
 | ||||
|         DynamicPrintConfig new_conf = *m_config; | ||||
|         if (dialog.ShowModal() == wxID_OK) { | ||||
|             new_conf.set_key_value("support_head_penetration", | ||||
|                                    new ConfigOptionFloat(head_width)); | ||||
|         } | ||||
| 
 | ||||
|         load_config(new_conf); | ||||
|     } | ||||
| 
 | ||||
|     double pinhead_d = m_config->opt_float("support_head_front_diameter"); | ||||
|     double pillar_d  = m_config->opt_float("support_pillar_diameter"); | ||||
|     if (pinhead_d > pillar_d) { | ||||
|         wxString msg_text = _(L( | ||||
|             "Pinhead diameter should be smaller than the pillar diameter.")); | ||||
| 
 | ||||
|         wxMessageDialog dialog (parent(), | ||||
|                                 msg_text, | ||||
|                                 _(L("Invalid pinhead diameter")), | ||||
|                                 wxICON_WARNING | wxOK); | ||||
| 
 | ||||
|         DynamicPrintConfig new_conf = *m_config; | ||||
|         if (dialog.ShowModal() == wxID_OK) { | ||||
|             new_conf.set_key_value("support_head_front_diameter", | ||||
|                                    new ConfigOptionFloat(pillar_d / 2.0)); | ||||
|         } | ||||
| 
 | ||||
|         load_config(new_conf); | ||||
|     } | ||||
| 
 | ||||
|     bool   has_suppad = pad_en && supports_en; | ||||
|     bool zero_elev    = m_config->opt_bool("pad_around_object") && has_suppad; | ||||
| 
 | ||||
|     get_field("support_object_elevation")->toggle(supports_en && !zero_elev); | ||||
|     get_field("pad_object_gap")->toggle(zero_elev); | ||||
|     get_field("pad_object_connector_stride")->toggle(zero_elev); | ||||
|     get_field("pad_object_connector_width")->toggle(zero_elev); | ||||
|     get_field("pad_object_connector_penetration")->toggle(zero_elev); | ||||
| */ | ||||
| 
 | ||||
|     m_config_manipulation.update_print_sla_config(m_config, true); | ||||
|     m_update_cnt--; | ||||
| 
 | ||||
|  |  | |||
|  | @ -321,7 +321,6 @@ protected: | |||
| 
 | ||||
| class TabPrint : public Tab | ||||
| { | ||||
|     bool is_msg_dlg_already_exist {false}; | ||||
| public: | ||||
| 	TabPrint(wxNotebook* parent) :  | ||||
| // 		Tab(parent, _(L("Print Settings")), L("print")) {}
 | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| #include <algorithm> | ||||
| #include <sstream> | ||||
| #include "WipeTowerDialog.hpp" | ||||
| #include "PresetBundle.hpp" | ||||
| #include "GUI.hpp" | ||||
| #include "I18N.hpp" | ||||
| #include "GUI_App.hpp" | ||||
|  | @ -137,11 +138,11 @@ std::string RammingPanel::get_parameters() | |||
| 
 | ||||
| 
 | ||||
| // Parent dialog for purging volume adjustments - it fathers WipingPanel widget (that contains all controls) and a button to toggle simple/advanced mode:
 | ||||
| WipingDialog::WipingDialog(wxWindow* parent,const std::vector<float>& matrix, const std::vector<float>& extruders) | ||||
| WipingDialog::WipingDialog(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, const std::vector<std::string>& extruder_colours) | ||||
| : wxDialog(parent, wxID_ANY, _(L("Wipe tower - Purging volume adjustment")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/) | ||||
| { | ||||
|     auto widget_button = new wxButton(this,wxID_ANY,"-",wxPoint(0,0),wxDefaultSize); | ||||
|     m_panel_wiping  = new WipingPanel(this,matrix,extruders, widget_button); | ||||
|     m_panel_wiping  = new WipingPanel(this,matrix,extruders, extruder_colours, widget_button); | ||||
| 
 | ||||
|     auto main_sizer = new wxBoxSizer(wxVERTICAL); | ||||
| 
 | ||||
|  | @ -180,7 +181,7 @@ void WipingPanel::format_sizer(wxSizer* sizer, wxPanel* page, wxGridSizer* grid_ | |||
| } | ||||
| 
 | ||||
| // This panel contains all control widgets for both simple and advanced mode (these reside in separate sizers)
 | ||||
| WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, wxButton* widget_button) | ||||
| WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, const std::vector<std::string>& extruder_colours, wxButton* widget_button) | ||||
| : wxPanel(parent,wxID_ANY, wxDefaultPosition, wxDefaultSize/*,wxBORDER_RAISED*/) | ||||
| { | ||||
|     m_widget_button = widget_button;    // pointer to the button in parent dialog
 | ||||
|  | @ -188,6 +189,12 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, con | |||
| 
 | ||||
|     m_number_of_extruders = (int)(sqrt(matrix.size())+0.001); | ||||
| 
 | ||||
|     for (const std::string& color : extruder_colours) { | ||||
|         unsigned char rgb[3]; | ||||
|         Slic3r::PresetBundle::parse_color(color, rgb); | ||||
|         m_colours.push_back(wxColor(rgb[0], rgb[1], rgb[2])); | ||||
|     } | ||||
| 
 | ||||
| 	// Create two switched panels with their own sizers
 | ||||
|     m_sizer_simple          = new wxBoxSizer(wxVERTICAL); | ||||
|     m_sizer_advanced        = new wxBoxSizer(wxVERTICAL); | ||||
|  | @ -211,14 +218,36 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, con | |||
| 				edit_boxes[i][j]->SetValue(wxString("") << int(matrix[m_number_of_extruders*j + i])); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|     const int clr_icon_side = edit_boxes.front().front()->GetSize().y; | ||||
|     const auto icon_size = wxSize(clr_icon_side, clr_icon_side); | ||||
| 
 | ||||
| 	m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString(""))); | ||||
| 	for (unsigned int i = 0; i < m_number_of_extruders; ++i) | ||||
| 		m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); | ||||
| 	for (unsigned int i = 0; i < m_number_of_extruders; ++i) { | ||||
| 		m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); | ||||
| 		for (unsigned int j = 0; j < m_number_of_extruders; ++j) | ||||
| 			m_gridsizer_advanced->Add(edit_boxes[j][i], 0); | ||||
| 	} | ||||
|         auto hsizer = new wxBoxSizer(wxHORIZONTAL); | ||||
|         hsizer->AddSpacer(20); | ||||
|         hsizer->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER); | ||||
|         wxWindow* w = new wxWindow(m_page_advanced, wxID_ANY, wxDefaultPosition, icon_size, wxBORDER_SIMPLE); | ||||
|         w->SetCanFocus(false); | ||||
|         w->SetBackgroundColour(m_colours[i]); | ||||
|         hsizer->AddStretchSpacer(); | ||||
|         hsizer->Add(w); | ||||
| 		m_gridsizer_advanced->Add(hsizer, 1, wxEXPAND); | ||||
|     } | ||||
| 	for (unsigned int i = 0; i < m_number_of_extruders; ++i) { | ||||
|         auto hsizer = new wxBoxSizer(wxHORIZONTAL); | ||||
|         wxWindow* w = new wxWindow(m_page_advanced, wxID_ANY, wxDefaultPosition, icon_size, wxBORDER_SIMPLE); | ||||
|         w->SetCanFocus(false); | ||||
|         w->SetBackgroundColour(m_colours[i]); | ||||
|         hsizer->AddSpacer(20); | ||||
|         hsizer->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); | ||||
|         hsizer->AddStretchSpacer(); | ||||
|         hsizer->Add(w); | ||||
|         m_gridsizer_advanced->Add(hsizer, 1, wxEXPAND); | ||||
| 
 | ||||
|     for (unsigned int j = 0; j < m_number_of_extruders; ++j) | ||||
|         m_gridsizer_advanced->Add(edit_boxes[j][i], 0); | ||||
|     } | ||||
| 
 | ||||
| 	// collect and format sizer
 | ||||
| 	format_sizer(m_sizer_advanced, m_page_advanced, m_gridsizer_advanced, | ||||
|  | @ -237,7 +266,16 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, con | |||
| 	for (unsigned int i=0;i<m_number_of_extruders;++i) { | ||||
|         m_old.push_back(new wxSpinCtrl(m_page_simple,wxID_ANY,wxEmptyString,wxDefaultPosition, wxSize(ITEM_WIDTH(), -1),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i])); | ||||
|         m_new.push_back(new wxSpinCtrl(m_page_simple,wxID_ANY,wxEmptyString,wxDefaultPosition, wxSize(ITEM_WIDTH(), -1),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i+1])); | ||||
| 		gridsizer_simple->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString(_(L("Tool #"))) << i + 1 << ": "), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); | ||||
| 
 | ||||
|         auto hsizer = new wxBoxSizer(wxHORIZONTAL); | ||||
|         wxWindow* w = new wxWindow(m_page_simple, wxID_ANY, wxDefaultPosition, icon_size, wxBORDER_SIMPLE); | ||||
|         w->SetCanFocus(false); | ||||
|         w->SetBackgroundColour(m_colours[i]); | ||||
|         hsizer->Add(w, wxALIGN_CENTER_VERTICAL); | ||||
|         hsizer->AddSpacer(10); | ||||
|         hsizer->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString(_(L("Tool #"))) << i + 1 << ": "), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); | ||||
| 
 | ||||
|         gridsizer_simple->Add(hsizer, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL); | ||||
|         gridsizer_simple->Add(m_old.back(),0); | ||||
|         gridsizer_simple->Add(m_new.back(),0); | ||||
| 	} | ||||
|  |  | |||
|  | @ -46,7 +46,7 @@ private: | |||
| 
 | ||||
| class WipingPanel : public wxPanel { | ||||
| public: | ||||
|     WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, wxButton* widget_button); | ||||
|     WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, const std::vector<std::string>& extruder_colours, wxButton* widget_button); | ||||
|     std::vector<float> read_matrix_values(); | ||||
|     std::vector<float> read_extruders_values(); | ||||
|     void toggle_advanced(bool user_action = false); | ||||
|  | @ -59,6 +59,7 @@ private: | |||
|     std::vector<wxSpinCtrl*> m_old; | ||||
|     std::vector<wxSpinCtrl*> m_new; | ||||
|     std::vector<std::vector<wxTextCtrl*>> edit_boxes; | ||||
|     std::vector<wxColour> m_colours; | ||||
|     unsigned int m_number_of_extruders  = 0; | ||||
|     bool m_advanced                     = false; | ||||
| 	wxPanel*	m_page_simple = nullptr; | ||||
|  | @ -76,7 +77,7 @@ private: | |||
| 
 | ||||
| class WipingDialog : public wxDialog { | ||||
| public: | ||||
|     WipingDialog(wxWindow* parent,const std::vector<float>& matrix, const std::vector<float>& extruders); | ||||
|     WipingDialog(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, const std::vector<std::string>& extruder_colours); | ||||
|     std::vector<float> get_matrix() const    { return m_output_matrix; } | ||||
|     std::vector<float> get_extruders() const { return m_output_extruders; } | ||||
| 
 | ||||
|  |  | |||
|  | @ -752,7 +752,7 @@ static bool append_root_node(ObjectDataViewModelNode *parent_node, | |||
|      | ||||
|     if (inst_root_id < 0) { | ||||
|         if ((root_type&itInstanceRoot) || | ||||
|             (root_type&itLayerRoot) && get_root_idx(parent_node, itInstanceRoot)<0) | ||||
|             ( (root_type&itLayerRoot) && get_root_idx(parent_node, itInstanceRoot)<0) ) | ||||
|             parent_node->Append(*root_node); | ||||
|         else if (root_type&itLayerRoot) | ||||
|             parent_node->Insert(*root_node, static_cast<unsigned int>(get_root_idx(parent_node, itInstanceRoot))); | ||||
|  | @ -1379,7 +1379,12 @@ void ObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type | |||
|     type = itUndef; | ||||
| 
 | ||||
|     ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); | ||||
|     if (!node || node->GetIdx() <-1 || node->GetIdx() == -1 && !(node->GetType() & (itObject | itSettings | itInstanceRoot | itLayerRoot/* | itLayer*/))) | ||||
|     if (!node ||  | ||||
|         node->GetIdx() <-1 ||  | ||||
|         ( node->GetIdx() == -1 &&  | ||||
|          !(node->GetType() & (itObject | itSettings | itInstanceRoot | itLayerRoot/* | itLayer*/)) | ||||
|         ) | ||||
|        ) | ||||
|         return; | ||||
| 
 | ||||
|     idx = node->GetIdx(); | ||||
|  | @ -2187,9 +2192,9 @@ double DoubleSlider::get_double_value(const SelectedSlider& selection) | |||
|         return 0.0; | ||||
|     if (m_values.size() <= m_higher_value) { | ||||
|         correct_higher_value(); | ||||
|         return m_values.back().second; | ||||
|         return m_values.back(); | ||||
|     } | ||||
|     return m_values[selection == ssLower ? m_lower_value : m_higher_value].second; | ||||
|     return m_values[selection == ssLower ? m_lower_value : m_higher_value]; | ||||
| } | ||||
| 
 | ||||
| std::vector<double> DoubleSlider::GetTicksValues() const | ||||
|  | @ -2201,7 +2206,7 @@ std::vector<double> DoubleSlider::GetTicksValues() const | |||
|         for (int tick : m_ticks) { | ||||
|             if (tick > val_size) | ||||
|                 break; | ||||
|             values.push_back(m_values[tick].second); | ||||
|             values.push_back(m_values[tick]); | ||||
|         } | ||||
| 
 | ||||
|     return values; | ||||
|  | @ -2215,14 +2220,13 @@ void DoubleSlider::SetTicksValues(const std::vector<double>& heights) | |||
|     const bool was_empty = m_ticks.empty(); | ||||
| 
 | ||||
|     m_ticks.clear(); | ||||
|     unsigned int i = 0; | ||||
|     for (auto h : heights) { | ||||
|         while (i < m_values.size() && m_values[i].second - epsilon()/*1e-6*/ < h) | ||||
|             ++i; | ||||
|         // don't miss last layer if it is
 | ||||
|         if (i == m_values.size() && fabs(m_values[i-1].second - h) > epsilon()) | ||||
|             return; | ||||
|         m_ticks.insert(i-1); | ||||
|         auto it = std::lower_bound(m_values.begin(), m_values.end(), h - epsilon()); | ||||
| 
 | ||||
|         if (it == m_values.end()) | ||||
|             continue; | ||||
| 
 | ||||
|         m_ticks.insert(it-m_values.begin()); | ||||
|     } | ||||
|      | ||||
|     if (!was_empty && m_ticks.empty()) | ||||
|  | @ -2342,13 +2346,14 @@ wxString DoubleSlider::get_label(const SelectedSlider& selection) const | |||
| 
 | ||||
|     const wxString str = m_values.empty() ?  | ||||
|                          wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) : | ||||
|                          wxNumberFormatter::ToString(m_values[value].second, 2, wxNumberFormatter::Style_None); | ||||
|     return wxString::Format("%s\n(%d)", str, m_values.empty() ? value : m_values[value].first); | ||||
|                          wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None); | ||||
|     return wxString::Format("%s\n(%d)", str, m_values.empty() ? value : value+1); | ||||
| } | ||||
| 
 | ||||
| void DoubleSlider::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const | ||||
| { | ||||
|     if ((m_is_one_layer || m_higher_value==m_lower_value) && selection != m_selection || !selection)  | ||||
|     if ( selection == ssUndef ||  | ||||
|         ((m_is_one_layer || m_higher_value==m_lower_value) && selection != m_selection) ) | ||||
|         return; | ||||
|     wxCoord text_width, text_height; | ||||
|     const wxString label = get_label(selection); | ||||
|  | @ -2680,7 +2685,7 @@ void DoubleSlider::correct_lower_value() | |||
|     else if (m_lower_value > m_max_value) | ||||
|         m_lower_value = m_max_value; | ||||
|      | ||||
|     if (m_lower_value >= m_higher_value && m_lower_value <= m_max_value || m_is_one_layer) | ||||
|     if ((m_lower_value >= m_higher_value && m_lower_value <= m_max_value) || m_is_one_layer) | ||||
|         m_higher_value = m_lower_value; | ||||
| } | ||||
| 
 | ||||
|  | @ -2691,7 +2696,7 @@ void DoubleSlider::correct_higher_value() | |||
|     else if (m_higher_value < m_min_value) | ||||
|         m_higher_value = m_min_value; | ||||
|      | ||||
|     if (m_higher_value <= m_lower_value && m_higher_value >= m_min_value || m_is_one_layer) | ||||
|     if ((m_higher_value <= m_lower_value && m_higher_value >= m_min_value) || m_is_one_layer) | ||||
|         m_lower_value = m_higher_value; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -385,9 +385,9 @@ class ObjectDataViewModel :public wxDataViewModel | |||
| { | ||||
|     std::vector<ObjectDataViewModelNode*>       m_objects; | ||||
|     std::vector<wxBitmap*>                      m_volume_bmps; | ||||
|     wxBitmap*                                   m_warning_bmp; | ||||
|     wxBitmap*                                   m_warning_bmp { nullptr }; | ||||
| 
 | ||||
|     wxDataViewCtrl*                             m_ctrl{ nullptr }; | ||||
|     wxDataViewCtrl*                             m_ctrl { nullptr }; | ||||
| 
 | ||||
| public: | ||||
|     ObjectDataViewModel(); | ||||
|  | @ -720,15 +720,17 @@ public: | |||
|         const wxString& name = wxEmptyString); | ||||
|     ~DoubleSlider() {} | ||||
| 
 | ||||
|     // permissible error for layer height
 | ||||
|     /* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values. 
 | ||||
|      * So, let use same value as a permissible error for layer height. | ||||
|      */ | ||||
|     static double epsilon() { return 0.0011;} | ||||
| 
 | ||||
|     void    msw_rescale(); | ||||
| 
 | ||||
|     int GetMinValue() const { return m_min_value; } | ||||
|     int GetMaxValue() const { return m_max_value; } | ||||
|     double GetMinValueD()  { return m_values.empty() ? 0. : m_values[m_min_value].second; } | ||||
|     double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value].second; } | ||||
|     double GetMinValueD()  { return m_values.empty() ? 0. : m_values[m_min_value]; } | ||||
|     double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; } | ||||
|     int GetLowerValue() const { return m_lower_value; } | ||||
|     int GetHigherValue() const { return m_higher_value; } | ||||
|     int GetActiveValue() const; | ||||
|  | @ -744,7 +746,7 @@ public: | |||
|     void SetKoefForLabels(const double koef) { | ||||
|         m_label_koef = koef; | ||||
|     } | ||||
|     void SetSliderValues(const std::vector<std::pair<int, double>>& values) { | ||||
|     void SetSliderValues(const std::vector<double>& values) { | ||||
|         m_values = values; | ||||
|     } | ||||
|     void ChangeOneLayerLock(); | ||||
|  | @ -865,7 +867,7 @@ private: | |||
|     std::vector<wxPen*> m_line_pens; | ||||
|     std::vector<wxPen*> m_segm_pens; | ||||
|     std::set<int>       m_ticks; | ||||
|     std::vector<std::pair<int,double>> m_values; | ||||
|     std::vector<double> m_values; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -65,13 +65,6 @@ new_from_type(CLASS, type) | |||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
| void | ||||
| make_fill(CLASS, layer_region, out_append) | ||||
|     char*                       CLASS; | ||||
|     LayerRegion*                layer_region; | ||||
|     ExtrusionEntityCollection*  out_append; | ||||
|     CODE: | ||||
|         make_fill(*layer_region, *out_append); | ||||
| %} | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Enrico Turri
						Enrico Turri