Merge branch 'master' into fs_undoredo
This commit is contained in:
commit
369a881872
@ -4,12 +4,17 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "GCodeProcessor.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "LocalesUtils.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "Surface.hpp"
|
||||
#include "Fill/FillRectilinear.hpp"
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
@ -512,6 +517,8 @@ WipeTower::WipeTower(const PrintConfig& config, const std::vector<std::vector<fl
|
||||
m_wipe_tower_width(float(config.wipe_tower_width)),
|
||||
m_wipe_tower_rotation_angle(float(config.wipe_tower_rotation_angle)),
|
||||
m_wipe_tower_brim_width(float(config.wipe_tower_brim_width)),
|
||||
m_wipe_tower_cone_angle(float(config.wipe_tower_cone_angle)),
|
||||
m_extra_spacing(float(config.wipe_tower_extra_spacing/100.)),
|
||||
m_y_shift(0.f),
|
||||
m_z_pos(0.f),
|
||||
m_bridging(float(config.wipe_tower_bridging)),
|
||||
@ -1169,45 +1176,171 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
|
||||
";------------------\n\n\n\n\n\n\n");
|
||||
}
|
||||
|
||||
// outer perimeter (always):
|
||||
writer.rectangle(wt_box, feedrate);
|
||||
const float spacing = m_perimeter_width - m_layer_height*float(1.-M_PI_4);
|
||||
|
||||
// This block creates the stabilization cone.
|
||||
// First define a lambda to draw the rectangle with stabilization.
|
||||
auto supported_rectangle = [this, &writer, spacing](const box_coordinates& wt_box, double feedrate, bool infill_cone) -> Polygon {
|
||||
const auto [R, support_scale] = get_wipe_tower_cone_base(m_wipe_tower_width, m_wipe_tower_height, m_wipe_tower_depth, m_wipe_tower_cone_angle);
|
||||
|
||||
double z = m_no_sparse_layers ? (m_current_height + m_layer_info->height) : m_layer_info->z; // the former should actually work in both cases, but let's stay on the safe side (the 2.6.0 is close)
|
||||
|
||||
double r = std::tan(Geometry::deg2rad(m_wipe_tower_cone_angle/2.f)) * (m_wipe_tower_height - z);
|
||||
Vec2f center = (wt_box.lu + wt_box.rd) / 2.;
|
||||
double w = wt_box.lu.y() - wt_box.ld.y();
|
||||
enum Type {
|
||||
Arc,
|
||||
Corner,
|
||||
ArcStart,
|
||||
ArcEnd
|
||||
};
|
||||
|
||||
// First generate vector of annotated point which form the boundary.
|
||||
std::vector<std::pair<Vec2f, Type>> pts = {{wt_box.ru, Corner}};
|
||||
if (double alpha_start = std::asin((0.5*w)/r); ! std::isnan(alpha_start) && r > 0.5*w+0.01) {
|
||||
for (double alpha = alpha_start; alpha < M_PI-alpha_start+0.001; alpha+=(M_PI-2*alpha_start) / 20.)
|
||||
pts.emplace_back(Vec2f(center.x() + r*std::cos(alpha)/support_scale, center.y() + r*std::sin(alpha)), alpha == alpha_start ? ArcStart : Arc);
|
||||
pts.back().second = ArcEnd;
|
||||
}
|
||||
pts.emplace_back(wt_box.lu, Corner);
|
||||
pts.emplace_back(wt_box.ld, Corner);
|
||||
for (int i=int(pts.size())-3; i>0; --i)
|
||||
pts.emplace_back(Vec2f(pts[i].first.x(), 2*center.y()-pts[i].first.y()), i == int(pts.size())-3 ? ArcStart : i == 1 ? ArcEnd : Arc);
|
||||
pts.emplace_back(wt_box.rd, Corner);
|
||||
|
||||
// Create a Polygon from the points.
|
||||
Polygon poly;
|
||||
for (const auto& [pt, tag] : pts)
|
||||
poly.points.push_back(Point::new_scale(pt));
|
||||
|
||||
// Prepare polygons to be filled by infill.
|
||||
Polylines polylines;
|
||||
if (infill_cone && m_wipe_tower_width > 2*spacing && m_wipe_tower_depth > 2*spacing) {
|
||||
ExPolygons infill_areas;
|
||||
ExPolygon wt_contour(poly);
|
||||
Polygon wt_rectangle(Points{Point::new_scale(wt_box.ld), Point::new_scale(wt_box.rd), Point::new_scale(wt_box.ru), Point::new_scale(wt_box.lu)});
|
||||
wt_rectangle = offset(wt_rectangle, scale_(-spacing/2.)).front();
|
||||
wt_contour = offset_ex(wt_contour, scale_(-spacing/2.)).front();
|
||||
infill_areas = diff_ex(wt_contour, wt_rectangle);
|
||||
if (infill_areas.size() == 2) {
|
||||
ExPolygon& bottom_expoly = infill_areas.front().contour.points.front().y() < infill_areas.back().contour.points.front().y() ? infill_areas[0] : infill_areas[1];
|
||||
std::unique_ptr<Fill> filler(Fill::new_from_type(ipMonotonicLines));
|
||||
filler->angle = Geometry::deg2rad(45.f);
|
||||
filler->spacing = spacing;
|
||||
FillParams params;
|
||||
params.density = 1.f;
|
||||
Surface surface(stBottom, bottom_expoly);
|
||||
filler->bounding_box = get_extents(bottom_expoly);
|
||||
polylines = filler->fill_surface(&surface, params);
|
||||
if (! polylines.empty()) {
|
||||
if (polylines.front().points.front().x() > polylines.back().points.back().x()) {
|
||||
std::reverse(polylines.begin(), polylines.end());
|
||||
for (Polyline& p : polylines)
|
||||
p.reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the closest corner and travel to it.
|
||||
int start_i = 0;
|
||||
double min_dist = std::numeric_limits<double>::max();
|
||||
for (int i=0; i<int(pts.size()); ++i) {
|
||||
if (pts[i].second == Corner) {
|
||||
double dist = (pts[i].first - Vec2f(writer.x(), writer.y())).squaredNorm();
|
||||
if (dist < min_dist) {
|
||||
min_dist = dist;
|
||||
start_i = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
writer.travel(pts[start_i].first);
|
||||
|
||||
// Now actually extrude the boundary (and possibly infill):
|
||||
int i = start_i+1 == int(pts.size()) ? 0 : start_i + 1;
|
||||
while (i != start_i) {
|
||||
writer.extrude(pts[i].first, feedrate);
|
||||
if (pts[i].second == ArcEnd) {
|
||||
// Extrude the infill.
|
||||
if (! polylines.empty()) {
|
||||
// Extrude the infill and travel back to where we were.
|
||||
bool mirror = ((pts[i].first.y() - center.y()) * (unscale(polylines.front().points.front()).y() - center.y())) < 0.;
|
||||
for (const Polyline& line : polylines) {
|
||||
writer.travel(center - (mirror ? 1.f : -1.f) * (unscale(line.points.front()).cast<float>() - center));
|
||||
for (size_t i=0; i<line.points.size(); ++i)
|
||||
writer.extrude(center - (mirror ? 1.f : -1.f) * (unscale(line.points[i]).cast<float>() - center));
|
||||
}
|
||||
writer.travel(pts[i].first);
|
||||
}
|
||||
}
|
||||
if (++i == int(pts.size()))
|
||||
i = 0;
|
||||
}
|
||||
writer.extrude(pts[start_i].first, feedrate);
|
||||
return poly;
|
||||
};
|
||||
|
||||
// outer contour (always)
|
||||
bool infill_cone = first_layer && m_wipe_tower_width > 2*spacing && m_wipe_tower_depth > 2*spacing;
|
||||
Polygon poly = supported_rectangle(wt_box, feedrate, infill_cone);
|
||||
|
||||
|
||||
// brim (first layer only)
|
||||
if (first_layer) {
|
||||
box_coordinates box = wt_box;
|
||||
float spacing = m_perimeter_width - m_layer_height*float(1.-M_PI_4);
|
||||
// How many perimeters shall the brim have?
|
||||
size_t loops_num = (m_wipe_tower_brim_width + spacing/2.f) / spacing;
|
||||
|
||||
|
||||
for (size_t i = 0; i < loops_num; ++ i) {
|
||||
box.expand(spacing);
|
||||
writer.rectangle(box);
|
||||
poly = offset(poly, scale_(spacing)).front();
|
||||
int cp = poly.closest_point_index(Point::new_scale(writer.x(), writer.y()));
|
||||
writer.travel(unscale(poly.points[cp]).cast<float>());
|
||||
for (int i=cp+1; true; ++i ) {
|
||||
if (i==int(poly.points.size()))
|
||||
i = 0;
|
||||
writer.extrude(unscale(poly.points[i]).cast<float>());
|
||||
if (i == cp)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Save actual brim width to be later passed to the Print object, which will use it
|
||||
// for skirt calculation and pass it to GLCanvas for precise preview box
|
||||
m_wipe_tower_brim_width_real = wt_box.ld.x() - box.ld.x() + spacing/2.f;
|
||||
wt_box = box;
|
||||
m_wipe_tower_brim_width_real = loops_num * spacing;
|
||||
}
|
||||
|
||||
// Now prepare future wipe. box contains rectangle that was extruded last (ccw).
|
||||
Vec2f target = (writer.pos() == wt_box.ld ? wt_box.rd :
|
||||
(writer.pos() == wt_box.rd ? wt_box.ru :
|
||||
(writer.pos() == wt_box.ru ? wt_box.lu :
|
||||
wt_box.ld)));
|
||||
writer.add_wipe_point(writer.pos())
|
||||
.add_wipe_point(target);
|
||||
|
||||
// Now prepare future wipe.
|
||||
int i = poly.closest_point_index(Point::new_scale(writer.x(), writer.y()));
|
||||
writer.add_wipe_point(writer.pos());
|
||||
writer.add_wipe_point(unscale(poly.points[i==0 ? int(poly.points.size())-1 : i-1]).cast<float>());
|
||||
|
||||
// Ask our writer about how much material was consumed.
|
||||
// Skip this in case the layer is sparse and config option to not print sparse layers is enabled.
|
||||
if (! m_no_sparse_layers || toolchanges_on_layer || first_layer)
|
||||
if (! m_no_sparse_layers || toolchanges_on_layer || first_layer) {
|
||||
if (m_current_tool < m_used_filament_length.size())
|
||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||
m_current_height += m_layer_info->height;
|
||||
}
|
||||
|
||||
return construct_tcr(writer, false, old_tool);
|
||||
}
|
||||
|
||||
// Static method to get the radius and x-scaling of the stabilizing cone base.
|
||||
std::pair<double, double> WipeTower::get_wipe_tower_cone_base(double width, double height, double depth, double angle_deg)
|
||||
{
|
||||
double R = std::tan(Geometry::deg2rad(angle_deg/2.)) * height;
|
||||
double fake_width = 0.66 * width;
|
||||
double diag = std::hypot(fake_width / 2., depth / 2.);
|
||||
double support_scale = 1.;
|
||||
if (R > diag) {
|
||||
double w = fake_width;
|
||||
double sin = 0.5 * depth / diag;
|
||||
double tan = depth / w;
|
||||
double t = (R - diag) * sin;
|
||||
support_scale = (w / 2. + t / tan + t * tan) / (w / 2.);
|
||||
}
|
||||
return std::make_pair(R, support_scale);
|
||||
}
|
||||
|
||||
// Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box
|
||||
void WipeTower::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool,
|
||||
unsigned int new_tool, float wipe_volume)
|
||||
@ -1250,6 +1383,8 @@ void WipeTower::plan_tower()
|
||||
m_wipe_tower_depth = 0.f;
|
||||
for (auto& layer : m_plan)
|
||||
layer.depth = 0.f;
|
||||
m_wipe_tower_height = m_plan.empty() ? 0.f : m_plan.back().z;
|
||||
m_current_height = 0.f;
|
||||
|
||||
for (int layer_index = int(m_plan.size()) - 1; layer_index >= 0; --layer_index)
|
||||
{
|
||||
@ -1334,8 +1469,6 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
|
||||
if (m_plan.empty())
|
||||
return;
|
||||
|
||||
m_extra_spacing = 1.f;
|
||||
|
||||
plan_tower();
|
||||
for (int i=0;i<5;++i) {
|
||||
save_on_last_wipe();
|
||||
@ -1343,6 +1476,7 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
|
||||
}
|
||||
|
||||
m_layer_info = m_plan.begin();
|
||||
m_current_height = 0.f;
|
||||
|
||||
// we don't know which extruder to start with - we'll set it according to the first toolchange
|
||||
for (const auto& layer : m_plan) {
|
||||
@ -1358,7 +1492,7 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
|
||||
m_old_temperature = -1; // reset last temperature written in the gcode
|
||||
|
||||
std::vector<WipeTower::ToolChangeResult> layer_result;
|
||||
for (auto layer : m_plan)
|
||||
for (const WipeTower::WipeTowerInfo& layer : m_plan)
|
||||
{
|
||||
set_layer(layer.z, layer.height, 0, false/*layer.z == m_plan.front().z*/, layer.z == m_plan.back().z);
|
||||
m_internal_rotation += 180.f;
|
||||
|
@ -23,6 +23,8 @@ class WipeTower
|
||||
public:
|
||||
static const std::string never_skip_tag() { return "_GCODE_WIPE_TOWER_NEVER_SKIP_TAG"; }
|
||||
|
||||
static std::pair<double, double> get_wipe_tower_cone_base(double width, double height, double depth, double angle_deg);
|
||||
|
||||
struct Extrusion
|
||||
{
|
||||
Extrusion(const Vec2f &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {}
|
||||
@ -142,6 +144,7 @@ public:
|
||||
|
||||
float get_depth() const { return m_wipe_tower_depth; }
|
||||
float get_brim_width() const { return m_wipe_tower_brim_width_real; }
|
||||
float get_wipe_tower_height() const { return m_wipe_tower_height; }
|
||||
|
||||
|
||||
|
||||
@ -253,6 +256,8 @@ private:
|
||||
Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
|
||||
float m_wipe_tower_width; // Width of the wipe tower.
|
||||
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
|
||||
float m_wipe_tower_height = 0.f;
|
||||
float m_wipe_tower_cone_angle = 0.f;
|
||||
float m_wipe_tower_brim_width = 0.f; // Width of brim (mm) from config
|
||||
float m_wipe_tower_brim_width_real = 0.f; // Width of brim (mm) after generation
|
||||
float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
|
||||
@ -359,6 +364,10 @@ private:
|
||||
std::vector<WipeTowerInfo> m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
|
||||
std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
|
||||
|
||||
// This sums height of all extruded layers, not counting the layers which
|
||||
// will be later removed when the "no_sparse_layers" is used.
|
||||
float m_current_height = 0.f;
|
||||
|
||||
// Stores information about used filament length per extruder:
|
||||
std::vector<float> m_used_filament_length;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -460,8 +460,8 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
|
||||
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio",
|
||||
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "gcode_resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
|
||||
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
|
||||
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||
"wipe_tower_width", "wipe_tower_cone_angle", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
|
||||
"wipe_tower_no_sparse_layers", "wipe_tower_extra_spacing", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||
"perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
|
||||
"wall_distribution_count", "min_feature_size", "min_bead_width"
|
||||
};
|
||||
|
@ -205,7 +205,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
|| opt_key == "wipe_tower"
|
||||
|| opt_key == "wipe_tower_width"
|
||||
|| opt_key == "wipe_tower_brim_width"
|
||||
|| opt_key == "wipe_tower_cone_angle"
|
||||
|| opt_key == "wipe_tower_bridging"
|
||||
|| opt_key == "wipe_tower_extra_spacing"
|
||||
|| opt_key == "wipe_tower_no_sparse_layers"
|
||||
|| opt_key == "wiping_volumes_matrix"
|
||||
|| opt_key == "parking_pos_retraction"
|
||||
@ -590,7 +592,7 @@ std::string Print::validate(std::string* warning) const
|
||||
if (*(lh.end()-2) > *(lh_tallest.end()-2))
|
||||
tallest_object_idx = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (has_custom_layering) {
|
||||
for (size_t idx_object = 0; idx_object < m_objects.size(); ++ idx_object) {
|
||||
@ -1133,23 +1135,34 @@ Polygons Print::first_layer_islands() const
|
||||
|
||||
std::vector<Point> Print::first_layer_wipe_tower_corners() const
|
||||
{
|
||||
std::vector<Point> corners;
|
||||
std::vector<Point> pts_scaled;
|
||||
|
||||
if (has_wipe_tower() && ! m_wipe_tower_data.tool_changes.empty()) {
|
||||
double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width;
|
||||
double depth = m_wipe_tower_data.depth + 2*m_wipe_tower_data.brim_width;
|
||||
Vec2d pt0(-m_wipe_tower_data.brim_width, -m_wipe_tower_data.brim_width);
|
||||
for (Vec2d pt : {
|
||||
pt0,
|
||||
Vec2d(pt0.x()+width, pt0.y() ),
|
||||
Vec2d(pt0.x()+width, pt0.y()+depth),
|
||||
Vec2d(pt0.x(), pt0.y()+depth)
|
||||
}) {
|
||||
|
||||
// First the corners.
|
||||
std::vector<Vec2d> pts = { pt0,
|
||||
Vec2d(pt0.x()+width, pt0.y()),
|
||||
Vec2d(pt0.x()+width, pt0.y()+depth),
|
||||
Vec2d(pt0.x(),pt0.y()+depth)
|
||||
};
|
||||
|
||||
// Now the stabilization cone.
|
||||
Vec2d center = (pts[0] + pts[2])/2.;
|
||||
const auto [cone_R, cone_x_scale] = WipeTower::get_wipe_tower_cone_base(m_config.wipe_tower_width, m_wipe_tower_data.height, m_wipe_tower_data.depth, m_config.wipe_tower_cone_angle);
|
||||
double r = cone_R + m_wipe_tower_data.brim_width;
|
||||
for (double alpha = 0.; alpha<2*M_PI; alpha += M_PI/20.)
|
||||
pts.emplace_back(center + r*Vec2d(std::cos(alpha)/cone_x_scale, std::sin(alpha)));
|
||||
|
||||
for (Vec2d& pt : pts) {
|
||||
pt = Eigen::Rotation2Dd(Geometry::deg2rad(m_config.wipe_tower_rotation_angle.value)) * pt;
|
||||
pt += Vec2d(m_config.wipe_tower_x.value, m_config.wipe_tower_y.value);
|
||||
corners.emplace_back(Point(scale_(pt.x()), scale_(pt.y())));
|
||||
pts_scaled.emplace_back(Point(scale_(pt.x()), scale_(pt.y())));
|
||||
}
|
||||
}
|
||||
return corners;
|
||||
return pts_scaled;
|
||||
}
|
||||
|
||||
void Print::finalize_first_layer_convex_hull()
|
||||
@ -1447,6 +1460,7 @@ void Print::_make_wipe_tower()
|
||||
wipe_tower.generate(m_wipe_tower_data.tool_changes);
|
||||
m_wipe_tower_data.depth = wipe_tower.get_depth();
|
||||
m_wipe_tower_data.brim_width = wipe_tower.get_brim_width();
|
||||
m_wipe_tower_data.height = wipe_tower.get_wipe_tower_height();
|
||||
|
||||
// Unload the current filament over the purge tower.
|
||||
coordf_t layer_height = m_objects.front()->config().layer_height.value;
|
||||
|
@ -434,6 +434,7 @@ struct WipeTowerData
|
||||
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
|
||||
float depth;
|
||||
float brim_width;
|
||||
float height;
|
||||
|
||||
void clear() {
|
||||
priming.reset(nullptr);
|
||||
|
@ -3150,6 +3150,25 @@ void PrintConfigDef::init_fff_params()
|
||||
def->min = 0.;
|
||||
def->set_default_value(new ConfigOptionFloat(2.));
|
||||
|
||||
def = this->add("wipe_tower_cone_angle", coFloat);
|
||||
def->label = L("Stabilization cone apex angle");
|
||||
def->tooltip = L("Angle at the apex of the cone that is used to stabilize the wipe tower. "
|
||||
"Larger angle means wider base.");
|
||||
def->sidetext = L("°");
|
||||
def->mode = comAdvanced;
|
||||
def->min = 0.;
|
||||
def->max = 90.;
|
||||
def->set_default_value(new ConfigOptionFloat(0.));
|
||||
|
||||
def = this->add("wipe_tower_extra_spacing", coPercent);
|
||||
def->label = L("Wipe tower purge lines spacing");
|
||||
def->tooltip = L("Spacing of purge lines on the wipe tower.");
|
||||
def->sidetext = L("%");
|
||||
def->mode = comExpert;
|
||||
def->min = 100.;
|
||||
def->max = 300.;
|
||||
def->set_default_value(new ConfigOptionPercent(100.));
|
||||
|
||||
def = this->add("wipe_into_infill", coBool);
|
||||
def->category = L("Wipe options");
|
||||
def->label = L("Wipe into this object's infill");
|
||||
|
@ -823,6 +823,8 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
((ConfigOptionFloat, wipe_tower_per_color_wipe))
|
||||
((ConfigOptionFloat, wipe_tower_rotation_angle))
|
||||
((ConfigOptionFloat, wipe_tower_brim_width))
|
||||
((ConfigOptionFloat, wipe_tower_cone_angle))
|
||||
((ConfigOptionPercent, wipe_tower_extra_spacing))
|
||||
((ConfigOptionFloat, wipe_tower_bridging))
|
||||
((ConfigOptionFloats, wiping_volumes_matrix))
|
||||
((ConfigOptionFloats, wiping_volumes_extruders))
|
||||
|
@ -480,11 +480,11 @@ int GLVolumeCollection::load_object_volume(
|
||||
|
||||
#if ENABLE_OPENGL_ES
|
||||
int GLVolumeCollection::load_wipe_tower_preview(
|
||||
float pos_x, float pos_y, float width, float depth, float height,
|
||||
float pos_x, float pos_y, float width, float depth, float height, float cone_angle,
|
||||
float rotation_angle, bool size_unknown, float brim_width, TriangleMesh* out_mesh)
|
||||
#else
|
||||
int GLVolumeCollection::load_wipe_tower_preview(
|
||||
float pos_x, float pos_y, float width, float depth, float height,
|
||||
float pos_x, float pos_y, float width, float depth, float height, float cone_angle,
|
||||
float rotation_angle, bool size_unknown, float brim_width)
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
{
|
||||
@ -544,6 +544,21 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
||||
brim_mesh.translate(-brim_width, -brim_width, 0.f);
|
||||
mesh.merge(brim_mesh);
|
||||
|
||||
// Now the stabilization cone and its base.
|
||||
const auto [R, scale_x] = WipeTower::get_wipe_tower_cone_base(width, height, depth, cone_angle);
|
||||
if (R > 0.) {
|
||||
TriangleMesh cone_mesh(its_make_cone(R, height));
|
||||
cone_mesh.scale(Vec3f(1.f/scale_x, 1.f, 1.f));
|
||||
|
||||
TriangleMesh disk_mesh(its_make_cylinder(R, brim_height));
|
||||
disk_mesh.scale(Vec3f(1. / scale_x, 1., 1.)); // Now it matches the base, which may be elliptic.
|
||||
disk_mesh.scale(Vec3f(1.f + scale_x*brim_width/R, 1.f + brim_width/R, 1.f)); // Scale so the brim is not deformed.
|
||||
cone_mesh.merge(disk_mesh);
|
||||
cone_mesh.translate(width / 2., depth / 2., 0.);
|
||||
mesh.merge(cone_mesh);
|
||||
}
|
||||
|
||||
|
||||
volumes.emplace_back(new GLVolume(color));
|
||||
GLVolume& v = *volumes.back();
|
||||
#if ENABLE_OPENGL_ES
|
||||
|
@ -424,10 +424,10 @@ public:
|
||||
|
||||
#if ENABLE_OPENGL_ES
|
||||
int load_wipe_tower_preview(
|
||||
float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, TriangleMesh* out_mesh = nullptr);
|
||||
float pos_x, float pos_y, float width, float depth, float height, float cone_angle, float rotation_angle, bool size_unknown, float brim_width, TriangleMesh* out_mesh = nullptr);
|
||||
#else
|
||||
int load_wipe_tower_preview(
|
||||
float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width);
|
||||
float pos_x, float pos_y, float width, float depth, float height, float cone_angle, float rotation_angle, bool size_unknown, float brim_width);
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
|
||||
GLVolume* new_toolpath_volume(const ColorRGBA& rgba);
|
||||
|
@ -319,8 +319,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
||||
toggle_field("standby_temperature_delta", have_ooze_prevention);
|
||||
|
||||
bool have_wipe_tower = config->opt_bool("wipe_tower");
|
||||
for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width",
|
||||
"wipe_tower_bridging", "wipe_tower_no_sparse_layers", "single_extruder_multi_material_priming" })
|
||||
for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_cone_angle",
|
||||
"wipe_tower_extra_spacing", "wipe_tower_bridging", "wipe_tower_no_sparse_layers", "single_extruder_multi_material_priming" })
|
||||
toggle_field(el, have_wipe_tower);
|
||||
|
||||
toggle_field("avoid_crossing_curled_overhangs", !config->opt_bool("avoid_crossing_perimeters"));
|
||||
|
@ -2253,7 +2253,7 @@ void GCodeViewer::load_shells(const Print& print)
|
||||
const WipeTowerData& wipe_tower_data = print.wipe_tower_data(extruders_count);
|
||||
const float depth = wipe_tower_data.depth;
|
||||
const float brim_width = wipe_tower_data.brim_width;
|
||||
m_shells.volumes.load_wipe_tower_preview(config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
|
||||
m_shells.volumes.load_wipe_tower_preview(config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_cone_angle, config.wipe_tower_rotation_angle,
|
||||
!print.is_step_done(psWipeTower), brim_width);
|
||||
}
|
||||
}
|
||||
|
@ -2035,17 +2035,18 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
const float w = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_width"))->value;
|
||||
const float a = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_rotation_angle"))->value;
|
||||
const float bw = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_brim_width"))->value;
|
||||
const float ca = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_cone_angle"))->value;
|
||||
|
||||
const Print *print = m_process->fff_print();
|
||||
const float depth = print->wipe_tower_data(extruders_count).depth;
|
||||
|
||||
#if ENABLE_OPENGL_ES
|
||||
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(
|
||||
x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower),
|
||||
x, y, w, depth, (float)height, ca, a, !print->is_step_done(psWipeTower),
|
||||
bw, &m_wipe_tower_mesh);
|
||||
#else
|
||||
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(
|
||||
x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower),
|
||||
x, y, w, depth, (float)height, ca, a, !print->is_step_done(psWipeTower),
|
||||
bw);
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
if (volume_idx_wipe_tower_old != -1)
|
||||
|
@ -2016,7 +2016,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
, config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({
|
||||
"bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance",
|
||||
"brim_width", "brim_separation", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material",
|
||||
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width",
|
||||
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_cone_angle", "wipe_tower_extra_spacing",
|
||||
"extruder_colour", "filament_colour", "material_colour", "max_print_height", "printer_model", "printer_technology",
|
||||
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
|
||||
"layer_height", "first_layer_height", "min_layer_height", "max_layer_height",
|
||||
@ -4779,7 +4779,7 @@ bool Plater::priv::can_increase_instances() const
|
||||
if (q->canvas3D()->get_gizmos_manager().get_current_type() == GLGizmosManager::Emboss) return false;
|
||||
|
||||
const auto obj_idxs = get_selection().get_object_idxs();
|
||||
return !obj_idxs.empty() && !sidebar->obj_list()->has_selected_cut_object();
|
||||
return !obj_idxs.empty() && !get_selection().is_wipe_tower() && !sidebar->obj_list()->has_selected_cut_object();
|
||||
}
|
||||
|
||||
bool Plater::priv::can_decrease_instances(int obj_idx /*= -1*/) const
|
||||
|
@ -1602,6 +1602,8 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("wipe_tower_rotation_angle");
|
||||
optgroup->append_single_option_line("wipe_tower_brim_width");
|
||||
optgroup->append_single_option_line("wipe_tower_bridging");
|
||||
optgroup->append_single_option_line("wipe_tower_cone_angle");
|
||||
optgroup->append_single_option_line("wipe_tower_extra_spacing");
|
||||
optgroup->append_single_option_line("wipe_tower_no_sparse_layers");
|
||||
optgroup->append_single_option_line("single_extruder_multi_material_priming");
|
||||
|
||||
|
@ -258,4 +258,16 @@ SCENARIO("Custom G-code", "[CustomGCode]")
|
||||
REQUIRE(match_count == 2);
|
||||
}
|
||||
}
|
||||
GIVEN("before_layer_gcode increments global variable") {
|
||||
auto config = Slic3r::DynamicPrintConfig::new_with({
|
||||
{ "start_gcode", "{global counter=0}" },
|
||||
{ "before_layer_gcode", ";Counter{counter=counter+1;counter}\n" }
|
||||
});
|
||||
std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config);
|
||||
THEN("The counter is emitted multiple times before layer change.") {
|
||||
REQUIRE(Slic3r::Test::contains(gcode, ";Counter1\n"));
|
||||
REQUIRE(Slic3r::Test::contains(gcode, ";Counter2\n"));
|
||||
REQUIRE(Slic3r::Test::contains(gcode, ";Counter3\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user