mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-21 06:21:16 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fe75c776d | ||
|
|
a06ed2d182 |
No files matched your search
@@ -9214,10 +9214,9 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string &path_d
|
||||
effective_height = static_cast<float>(m_sub_layer_height);
|
||||
}
|
||||
|
||||
// Effective extrusion length per distance unit = (filament_flow_ratio/cross_section) * mm3_per_mm / print flow ratio
|
||||
// m_writer.extruder()->e_per_mm3() below is (filament flow ratio / cross-sectional area)
|
||||
double e_per_mm = m_writer.filament()->e_per_mm3() * _mm3_per_mm;
|
||||
e_per_mm /= filament_flow_ratio;
|
||||
// _mm3_per_mm already includes the filament flow ratio, so convert it with the bare cross-section
|
||||
// rather than e_per_mm3(), which would apply the ratio a second time.
|
||||
double e_per_mm = _mm3_per_mm / m_writer.filament()->filament_crossection();
|
||||
|
||||
// set speed
|
||||
if (speed == -1) {
|
||||
|
||||
@@ -1882,6 +1882,23 @@ StringObjectException Print::validate(std::vector<StringObjectException> *warnin
|
||||
if (extruders.empty())
|
||||
return { L("No extrusions under current settings.") };
|
||||
|
||||
// A flow ratio of 0 would extrude nothing, and a nil one would write NaN extrusion lengths.
|
||||
{
|
||||
const std::vector<double> &flow_ratios = m_config.filament_flow_ratio.values;
|
||||
const std::vector<int> &self_index = m_config.filament_self_index.values;
|
||||
for (size_t column = 0; column < flow_ratios.size(); ++column) {
|
||||
// filament_self_index maps a column to its filament once the filament arrays hold one column per variant
|
||||
const unsigned int filament_id = self_index.size() == flow_ratios.size() ? self_index[column] - 1 : column;
|
||||
if (flow_ratios[column] > 0. || !std::binary_search(extruders.begin(), extruders.end(), filament_id))
|
||||
continue;
|
||||
StringObjectException err;
|
||||
// TRN %1% is the filament number as shown in the filament list, starting from 1.
|
||||
err.string = Slic3r::format(L("The flow ratio of filament %1% must be greater than 0."), filament_id + 1);
|
||||
err.opt_key = "filament_flow_ratio";
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// IDEX/IQEX: block multi-color in non-primary parallel modes when the active
|
||||
// configuration can't physically support it (no within-gantry toolchange path
|
||||
// or MMU lane sharing). See imex_multicolor_block_reason() for the full rule.
|
||||
|
||||
@@ -700,7 +700,7 @@ void GLVolume::render_with_outline(const GUI::Size& cnv_size)
|
||||
}
|
||||
|
||||
//BBS add render for simple case
|
||||
void GLVolume::simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector<ColorRGBA>& extruder_colors, bool ban_light)
|
||||
void GLVolume::simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector<ColorRGBA>& extruder_colors, bool ban_light, bool ban_color_volume)
|
||||
{
|
||||
if (this->is_left_handed())
|
||||
glFrontFace(GL_CW);
|
||||
@@ -734,7 +734,7 @@ void GLVolume::simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_obj
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (color_volume && !picking) {
|
||||
if (color_volume && !picking && !ban_color_volume) {
|
||||
const bool brighten_selected = selected && !disabled && !force_native_color && !force_neutral_color;
|
||||
|
||||
// when force_transparent, we need to keep the alpha
|
||||
|
||||
@@ -370,7 +370,7 @@ public:
|
||||
virtual void render_with_outline(const GUI::Size& cnv_size);
|
||||
|
||||
//BBS: add simple render function for thumbnail
|
||||
void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector<ColorRGBA>& extruder_colors, bool ban_light =false);
|
||||
void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector<ColorRGBA>& extruder_colors, bool ban_light =false, bool ban_color_volume = false);
|
||||
|
||||
void set_bounding_boxes_as_dirty() {
|
||||
m_transformed_bounding_box.reset();
|
||||
|
||||
@@ -2804,7 +2804,9 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size)
|
||||
_render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), show_axes);
|
||||
}
|
||||
m_frame_profiler.mark("bed");
|
||||
if (show_bed) //BBS: add outline logic
|
||||
// Exception: the painting gizmos hide the bed mesh on purpose and draw the plate instead,
|
||||
// so there the plate list is the only bed reference in the scene.
|
||||
if (show_bed || _is_below_plate_view_active()) //BBS: add outline logic
|
||||
_render_platelist(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward(), only_current, only_body, hover_id, true, show_grid);
|
||||
if (m_design_canvas && show_bed)
|
||||
// Design tab: replace the plate's corner-origin grid with the origin-centred CAD grid.
|
||||
@@ -2816,6 +2818,7 @@ void GLCanvas3D::_render_scene(const Camera& camera, const Size& cnv_size)
|
||||
_render_shadows(camera.get_view_matrix(), camera.get_projection_matrix());
|
||||
m_frame_profiler.mark("shadows");
|
||||
_render_objects(GLVolumeCollection::ERenderType::Opaque, !m_gizmos.is_running());
|
||||
_render_below_plate_volumes();
|
||||
m_frame_profiler.mark("objects");
|
||||
_render_sla_slices();
|
||||
_render_selection();
|
||||
@@ -8824,6 +8827,84 @@ void GLCanvas3D::_render_bed(const Transform3d& view_matrix, const Transform3d&
|
||||
m_bed.render(*this, view_matrix, projection_matrix, bottom, scale_factor, show_axes);
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_is_below_plate_view_active() const
|
||||
{
|
||||
return dynamic_cast<const GLGizmoPainterBase*>(m_gizmos.get_current()) != nullptr && m_gizmos.is_hiding_instances();
|
||||
}
|
||||
|
||||
// The painting gizmo clips the selected object at the plate, so the part under the plate is not drawn.
|
||||
// This pass draws that part of the selected instance again at 50% opacity, depth writes off.
|
||||
void GLCanvas3D::_render_below_plate_volumes()
|
||||
{
|
||||
if (!_is_below_plate_view_active() || m_volumes.empty())
|
||||
return;
|
||||
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
|
||||
if (shader == nullptr)
|
||||
return;
|
||||
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
const Transform3d& view_matrix = camera.get_view_matrix();
|
||||
const Transform3d& proj_matrix = camera.get_projection_matrix();
|
||||
const int object_idx = m_selection.get_object_idx();
|
||||
const int instance_idx = m_selection.get_instance_idx();
|
||||
|
||||
// Depth test stays on so whatever is already in the depth buffer hides the ghost; depth writes
|
||||
// are off and back faces are culled, so each pixel blends only the near surface once.
|
||||
const GLboolean cull_face = ::glIsEnabled(GL_CULL_FACE);
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glEnable(GL_CULL_FACE));
|
||||
glsafe(::glCullFace(GL_BACK));
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
shader->start_using();
|
||||
// Keep z <= SINKING_Z_THRESHOLD, the complement of the clip the painting gizmo applies.
|
||||
shader->set_uniform("z_range", std::array<float, 2>{ -FLT_MAX, float(SINKING_Z_THRESHOLD) });
|
||||
shader->set_uniform("clipping_plane", m_volumes.get_clipping_plane());
|
||||
shader->set_uniform("use_color_clip_plane", false);
|
||||
shader->set_uniform("is_outline", false);
|
||||
shader->set_uniform("slope.actived", false);
|
||||
shader->set_uniform("print_volume.type", -1);
|
||||
shader->set_uniform("extruder_printable_heights", std::array<float, 3>{ 0.0f, 0.0f, 0.0f });
|
||||
shader->set_uniform("shadow_intensity", 0.0f);
|
||||
|
||||
// simple_render() needs these to reach the plain surface; with ban_color_volume it ignores both.
|
||||
ModelObjectPtrs& model_objects = wxGetApp().model().objects;
|
||||
std::vector<ColorRGBA> extruder_colors = wxGetApp().plater()->get_extruders_colors();
|
||||
|
||||
for (GLVolume* volume : m_volumes.volumes) {
|
||||
// is_active cannot gate this pass: the color painter marks the whole instance inactive and
|
||||
// draws it in its own gizmo pass. The instance and print-part checks below are what pick
|
||||
// this ghost out of the scene.
|
||||
if (volume->object_idx() != object_idx || volume->instance_idx() != instance_idx ||
|
||||
volume->is_modifier || volume->is_wipe_tower || volume->volume_idx() < 0)
|
||||
continue;
|
||||
|
||||
volume->set_render_color();
|
||||
ColorRGBA color = volume->render_color;
|
||||
color.a(0.5f);
|
||||
volume->model.set_color(color);
|
||||
|
||||
const Transform3d model_matrix = volume->world_matrix();
|
||||
shader->set_uniform("view_model_matrix", view_matrix * model_matrix);
|
||||
shader->set_uniform("projection_matrix", proj_matrix);
|
||||
shader->set_uniform("volume_world_matrix", model_matrix);
|
||||
const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
|
||||
shader->set_uniform("view_normal_matrix", view_normal_matrix);
|
||||
// Painted facets would send render() through the MMU segmentation sub-models, which carry
|
||||
// their own opaque filament colours; the ghost draws the plain surface so the alpha holds.
|
||||
volume->simple_render(shader, model_objects, extruder_colors, false, true);
|
||||
}
|
||||
shader->stop_using();
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
if (cull_face == GL_FALSE)
|
||||
glsafe(::glDisable(GL_CULL_FACE));
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_hides_inactive_plates() const
|
||||
{
|
||||
return m_canvas_type == ECanvasType::CanvasView3D && !m_design_canvas && wxGetApp().focus_mode();
|
||||
@@ -11952,9 +12033,10 @@ bool GLCanvas3D::is_bed_visible() const
|
||||
if (!m_main_toolbar.is_enabled())
|
||||
return true;
|
||||
|
||||
const auto type = m_gizmos.get_current_type();
|
||||
return type != GLGizmosManager::FdmSupports && type != GLGizmosManager::Seam &&
|
||||
type != GLGizmosManager::MmSegmentation && type != GLGizmosManager::FuzzySkin;
|
||||
// The painting gizmos clip the selected instance at the plate (see InstancesHider), so the bed
|
||||
// mesh would sit in front of the part below it. They draw the plate and its grid instead (see
|
||||
// _render_scene) and the below-plate part as a translucent ghost.
|
||||
return !_is_below_plate_view_active();
|
||||
}
|
||||
|
||||
Vec3d GLCanvas3D::get_camera_pan_anchor(Camera& camera, ECameraNavigationType navigation_type,
|
||||
|
||||
@@ -1552,6 +1552,8 @@ private:
|
||||
void _render_imex_ghost_tooltip();
|
||||
void _render_section_view_caps();
|
||||
void _render_wireframe_overlay();
|
||||
bool _is_below_plate_view_active() const;
|
||||
void _render_below_plate_volumes();
|
||||
bool _is_xray_view_active() const;
|
||||
void _render_xray_volumes();
|
||||
//BBS: GUI refactor: add canvas size as parameters
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <glad/gl.h>
|
||||
#include "slic3r/GUI/Selection.hpp"
|
||||
#include <limits>
|
||||
#include "libslic3r/Model.hpp"
|
||||
@@ -174,6 +173,9 @@ void InstancesHider::on_update()
|
||||
m_clippers.emplace_back(new MeshClipper);
|
||||
m_clippers.back()->set_plane(ClippingPlane(-Vec3d::UnitZ(), z_min));
|
||||
m_clippers.back()->set_mesh(mesh->its);
|
||||
// Outline only: a filled cap would read as a surface the object does not have, and it
|
||||
// covers the model below it. 0.4 mm matches the cut gizmo's contour width.
|
||||
m_clippers.back()->set_behaviour(false, 0.4);
|
||||
}
|
||||
m_old_meshes = meshes;
|
||||
}
|
||||
@@ -214,10 +216,7 @@ void InstancesHider::render_cut() const
|
||||
else
|
||||
clipper->set_limiting_plane(ClippingPlane::ClipsNothing());
|
||||
|
||||
glsafe(::glPushAttrib(GL_DEPTH_TEST));
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
clipper->render_cut(mv->is_model_part() ? ColorRGBA(0.8f, 0.3f, 0.0f, 1.0f) : color_from_model_volume(*mv));
|
||||
glsafe(::glPopAttrib());
|
||||
clipper->render_contour(mv->is_model_part() ? ColorRGBA(0.8f, 0.3f, 0.0f, 1.0f) : color_from_model_volume(*mv));
|
||||
|
||||
++clipper_id;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <catch2/catch_message.hpp>
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/ModelArrange.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
@@ -89,6 +90,28 @@ TEST_CASE("The exclude object tolerance defaults to the former fixed 0.1 mm", "[
|
||||
Catch::Matchers::WithinAbs(0.1, 1e-9));
|
||||
}
|
||||
|
||||
TEST_CASE("The filament flow ratio scales the extruded length once", "[GCode]")
|
||||
{
|
||||
// Retraction off, so every positive relative E move is extrusion and scales with the ratio.
|
||||
auto extruded_length = [](double flow_ratio) {
|
||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({ { "use_relative_e_distances", "1" }, { "retraction_length", "0" }, { "wipe", "0" } });
|
||||
config.set_key_value("filament_flow_ratio", new ConfigOptionFloatsNullable{ flow_ratio });
|
||||
double total = 0.;
|
||||
GCodeReader reader;
|
||||
reader.parse_buffer(Test::slice({ Test::cube(20) }, config),
|
||||
[&](GCodeReader &, const GCodeReader::GCodeLine &line) {
|
||||
if ((line.cmd_is("G1") || line.cmd_is("G2") || line.cmd_is("G3")) && line.has_e() && line.e() > 0.f)
|
||||
total += line.e();
|
||||
});
|
||||
return total;
|
||||
};
|
||||
|
||||
const double full = extruded_length(1.);
|
||||
REQUIRE(full > 0.);
|
||||
CHECK_THAT(extruded_length(0.9) / full, Catch::Matchers::WithinRel(0.9, 1e-3));
|
||||
}
|
||||
|
||||
// Firmwares read the compatibility header lines (Anycubic's GoKlipper heats the bed with
|
||||
// first_layer_bed_temperature), so they must describe the filament the plate prints, not filament 1.
|
||||
TEST_CASE("The G-code header names the first layer temperatures of the filament the plate prints", "[GCode]")
|
||||
|
||||
@@ -547,6 +547,31 @@ TEST_CASE("Print::validate tolerates a null warnings pointer", "[Print][validate
|
||||
CHECK(err.string.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("Print::validate refuses a used filament with a flow ratio of 0", "[Print][validate][Regression]")
|
||||
{
|
||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||
config.set_key_value("filament_flow_ratio", new ConfigOptionFloatsNullable{ 0. });
|
||||
|
||||
std::vector<StringObjectException> warnings;
|
||||
StringObjectException err = validate_cubes(config, warnings);
|
||||
|
||||
CHECK_FALSE(err.string.empty());
|
||||
CHECK_FALSE(err.is_warning);
|
||||
CHECK(err.opt_key == "filament_flow_ratio");
|
||||
}
|
||||
|
||||
TEST_CASE("Print::validate ignores a flow ratio of 0 on an unused filament", "[Print][validate]")
|
||||
{
|
||||
// The cube prints with filament 1, so filament 2's ratio is never used.
|
||||
DynamicPrintConfig config = multifilament_config(2, { { "filament_self_index", "1,2" } });
|
||||
config.set_key_value("filament_flow_ratio", new ConfigOptionFloatsNullable{ 1., 0. });
|
||||
|
||||
std::vector<StringObjectException> warnings;
|
||||
StringObjectException err = validate_cubes(config, warnings);
|
||||
|
||||
CHECK(err.string.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("Purge tower selection keeps ordinary printers on the classic path", "[Print][PurgeTower][Regression]")
|
||||
{
|
||||
DynamicPrintConfig config = multifilament_config(2, {
|
||||
|
||||
Reference in new issue
Block a user