mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-01 12:51:12 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1d14329d9 | ||
|
|
236a8786ef | ||
|
|
92d30fbc55 | ||
|
|
3384daa6bc | ||
|
|
2a9cb32c1f |
Vendored
+11
-1
@@ -151,6 +151,12 @@ elseif(APPLE)
|
|||||||
# the post-install -add_rpath below.
|
# the post-install -add_rpath below.
|
||||||
set(_python_ldflags "${_python_arch_flags} -Wl,-headerpad_max_install_names")
|
set(_python_ldflags "${_python_arch_flags} -Wl,-headerpad_max_install_names")
|
||||||
|
|
||||||
|
# The macOS 27 SDK declares pipe2() and dup3() as available from macOS 27, so
|
||||||
|
# configure finds them and CPython 3.12 calls them without a runtime check.
|
||||||
|
# Below a macOS 27 deployment target they are weak-linked and resolve to NULL
|
||||||
|
# on older systems, where os.pipe() then segfaults -- in `make install`
|
||||||
|
# (compileall, ensurepip) and in the shipped app alike. Every configure below
|
||||||
|
# keeps the pipe()/dup2() fallbacks (python/cpython#153711).
|
||||||
if(IS_CROSS_COMPILE)
|
if(IS_CROSS_COMPILE)
|
||||||
set(_python_build_tgt --build=${_python_build_arch}-apple-darwin --host=${_python_host_arch}-apple-darwin)
|
set(_python_build_tgt --build=${_python_build_arch}-apple-darwin --host=${_python_host_arch}-apple-darwin)
|
||||||
set(_python_build_arch_flags "-arch ${_python_build_arch_flag} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
set(_python_build_arch_flags "-arch ${_python_build_arch_flag} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
||||||
@@ -174,7 +180,8 @@ elseif(APPLE)
|
|||||||
--enable-shared \
|
--enable-shared \
|
||||||
--without-static-libpython \
|
--without-static-libpython \
|
||||||
--disable-test-modules \
|
--disable-test-modules \
|
||||||
--build=${_python_build_arch}-apple-darwin && \
|
--build=${_python_build_arch}-apple-darwin \
|
||||||
|
ac_cv_func_pipe2=no ac_cv_func_dup3=no && \
|
||||||
make -j${NPROC} python && \
|
make -j${NPROC} python && \
|
||||||
cd '<SOURCE_DIR>' && \
|
cd '<SOURCE_DIR>' && \
|
||||||
env \
|
env \
|
||||||
@@ -191,6 +198,7 @@ elseif(APPLE)
|
|||||||
--without-static-libpython \
|
--without-static-libpython \
|
||||||
--with-openssl='${DESTDIR}' \
|
--with-openssl='${DESTDIR}' \
|
||||||
--disable-test-modules \
|
--disable-test-modules \
|
||||||
|
ac_cv_func_pipe2=no ac_cv_func_dup3=no \
|
||||||
${_python_build_tgt} \
|
${_python_build_tgt} \
|
||||||
--with-build-python='${_python_build_python}' \
|
--with-build-python='${_python_build_python}' \
|
||||||
py_cv_module__tkinter=n/a"
|
py_cv_module__tkinter=n/a"
|
||||||
@@ -213,6 +221,8 @@ elseif(APPLE)
|
|||||||
--with-openssl=${DESTDIR}
|
--with-openssl=${DESTDIR}
|
||||||
--disable-test-modules
|
--disable-test-modules
|
||||||
${_python_build_tgt}
|
${_python_build_tgt}
|
||||||
|
ac_cv_func_pipe2=no
|
||||||
|
ac_cv_func_dup3=no
|
||||||
# Tcl/Tk 9.0 (e.g. from Homebrew) is incompatible with CPython 3.12's
|
# Tcl/Tk 9.0 (e.g. from Homebrew) is incompatible with CPython 3.12's
|
||||||
# _tkinter; OrcaSlicer's embedded Python does not need tkinter anyway.
|
# _tkinter; OrcaSlicer's embedded Python does not need tkinter anyway.
|
||||||
py_cv_module__tkinter=n/a
|
py_cv_module__tkinter=n/a
|
||||||
|
|||||||
@@ -1692,9 +1692,9 @@ void Layer::make_ironing()
|
|||||||
ironing_params.just_infill = false;
|
ironing_params.just_infill = false;
|
||||||
// ORCA: Get filament-specific overrides if configured, otherwise use process values
|
// ORCA: Get filament-specific overrides if configured, otherwise use process values
|
||||||
size_t extruder_idx = ironing_params.extruder - 1;
|
size_t extruder_idx = ironing_params.extruder - 1;
|
||||||
ironing_params.line_spacing = (!config.filament_ironing_spacing.is_nil(extruder_idx)
|
ironing_params.line_spacing = std::max(IRONING_SPACING_MIN, !config.filament_ironing_spacing.is_nil(extruder_idx)
|
||||||
? config.filament_ironing_spacing.get_at(extruder_idx)
|
? config.filament_ironing_spacing.get_at(extruder_idx)
|
||||||
: config.ironing_spacing);
|
: config.ironing_spacing.value);
|
||||||
ironing_params.inset = (!config.filament_ironing_inset.is_nil(extruder_idx)
|
ironing_params.inset = (!config.filament_ironing_inset.is_nil(extruder_idx)
|
||||||
? config.filament_ironing_inset.get_at(extruder_idx)
|
? config.filament_ironing_inset.get_at(extruder_idx)
|
||||||
: config.ironing_inset);
|
: config.ironing_inset);
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ void FillConcentric::_fill_surface_single(
|
|||||||
|
|
||||||
coord_t min_spacing = scale_(this->spacing) * params.multiline;
|
coord_t min_spacing = scale_(this->spacing) * params.multiline;
|
||||||
coord_t distance = coord_t(min_spacing / params.density);
|
coord_t distance = coord_t(min_spacing / params.density);
|
||||||
|
// A non-positive step never shrinks the region, so the inset loop below would not end.
|
||||||
|
if (min_spacing <= 0 || distance <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (params.density > 0.9999f && !params.dont_adjust) {
|
if (params.density > 0.9999f && !params.dont_adjust) {
|
||||||
distance = this->_adjust_solid_spacing(bounding_box.size()(0), distance);
|
distance = this->_adjust_solid_spacing(bounding_box.size()(0), distance);
|
||||||
@@ -108,6 +111,8 @@ void FillConcentric::_fill_surface_single(const FillParams& params,
|
|||||||
// no rotation is supported for this infill pattern
|
// no rotation is supported for this infill pattern
|
||||||
Point bbox_size = expolygon.contour.bounding_box().size();
|
Point bbox_size = expolygon.contour.bounding_box().size();
|
||||||
coord_t min_spacing = scaled<coord_t>(this->spacing);
|
coord_t min_spacing = scaled<coord_t>(this->spacing);
|
||||||
|
if (min_spacing <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (params.density > 0.9999f && !params.dont_adjust) {
|
if (params.density > 0.9999f && !params.dont_adjust) {
|
||||||
coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1;
|
coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1;
|
||||||
|
|||||||
+23
-13
@@ -7313,7 +7313,7 @@ static std::unique_ptr<EdgeGrid::Grid> calculate_layer_edge_grid(const Layer& la
|
|||||||
std::string GCode::extrude_loop(const ExtrusionLoop& loop_ref,
|
std::string GCode::extrude_loop(const ExtrusionLoop& loop_ref,
|
||||||
const std::string& description,
|
const std::string& description,
|
||||||
double speed,
|
double speed,
|
||||||
const ExtrusionEntitiesPtr& region_perimeters,
|
const std::vector<const ExtrusionEntity*>& region_perimeters,
|
||||||
const Point* start_point,
|
const Point* start_point,
|
||||||
const WipeInwardSupport* wipe_support)
|
const WipeInwardSupport* wipe_support)
|
||||||
{
|
{
|
||||||
@@ -7662,7 +7662,7 @@ std::string GCode::extrude_multi_path(const ExtrusionMultiPath& multipath, const
|
|||||||
std::string GCode::extrude_entity(const ExtrusionEntity& entity,
|
std::string GCode::extrude_entity(const ExtrusionEntity& entity,
|
||||||
const std::string& description,
|
const std::string& description,
|
||||||
double speed,
|
double speed,
|
||||||
const ExtrusionEntitiesPtr& region_perimeters,
|
const std::vector<const ExtrusionEntity*>& region_perimeters,
|
||||||
const WipeInwardSupport* wipe_support)
|
const WipeInwardSupport* wipe_support)
|
||||||
{
|
{
|
||||||
if (const ExtrusionPath* path = dynamic_cast<const ExtrusionPath*>(&entity))
|
if (const ExtrusionPath* path = dynamic_cast<const ExtrusionPath*>(&entity))
|
||||||
@@ -7755,22 +7755,31 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector<Obje
|
|||||||
std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, bool ironing)
|
std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, bool ironing)
|
||||||
{
|
{
|
||||||
std::string gcode;
|
std::string gcode;
|
||||||
ExtrusionEntitiesPtr extrusions;
|
std::vector<const ExtrusionEntity*> extrusions;
|
||||||
|
std::vector<std::unique_ptr<ExtrusionEntity>> reversed;
|
||||||
const char* extrusion_name = ironing ? "ironing" : "infill";
|
const char* extrusion_name = ironing ? "ironing" : "infill";
|
||||||
for (const ObjectByExtruder::Island::Region ®ion : by_region)
|
for (const ObjectByExtruder::Island::Region ®ion : by_region)
|
||||||
if (! region.infills.empty()) {
|
if (! region.infills.empty()) {
|
||||||
extrusions.clear();
|
extrusions.clear();
|
||||||
extrusions.reserve(region.infills.size());
|
extrusions.reserve(region.infills.size());
|
||||||
for (ExtrusionEntity *ee : region.infills)
|
for (const ExtrusionEntity *ee : region.infills)
|
||||||
if ((ee->role() == erIroning) == ironing)
|
if ((ee->role() == erIroning) == ironing)
|
||||||
extrusions.emplace_back(ee);
|
extrusions.emplace_back(ee);
|
||||||
if (! extrusions.empty()) {
|
if (! extrusions.empty()) {
|
||||||
m_config.apply(print.get_print_region(®ion - &by_region.front()).config());
|
m_config.apply(print.get_print_region(®ion - &by_region.front()).config());
|
||||||
chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point());
|
reversed.clear();
|
||||||
|
chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point(), reversed);
|
||||||
|
// The reversed copies are in chain order.
|
||||||
|
auto next_reversed = reversed.begin();
|
||||||
for (const ExtrusionEntity *fill : extrusions) {
|
for (const ExtrusionEntity *fill : extrusions) {
|
||||||
|
ExtrusionEntity *own_copy = next_reversed != reversed.end() && next_reversed->get() == fill ? (next_reversed++)->get() : nullptr;
|
||||||
auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(fill);
|
auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(fill);
|
||||||
if (eec) {
|
if (eec) {
|
||||||
for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos.to_point()).entities)
|
// A reversed copy is owned here and can be moved from.
|
||||||
|
ExtrusionEntityCollection chained = own_copy ? std::move(static_cast<ExtrusionEntityCollection&>(*own_copy)) : ExtrusionEntityCollection(*eec);
|
||||||
|
if (!chained.no_sort)
|
||||||
|
chain_and_reorder_extrusion_entities(chained.entities, m_last_pos.to_point());
|
||||||
|
for (ExtrusionEntity *ee : chained.entities)
|
||||||
gcode += this->extrude_entity(*ee, extrusion_name);
|
gcode += this->extrude_entity(*ee, extrusion_name);
|
||||||
} else
|
} else
|
||||||
gcode += this->extrude_entity(*fill, extrusion_name);
|
gcode += this->extrude_entity(*fill, extrusion_name);
|
||||||
@@ -7808,9 +7817,9 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
|
|||||||
std::string gcode;
|
std::string gcode;
|
||||||
if (!support_fills.entities.empty()) {
|
if (!support_fills.entities.empty()) {
|
||||||
|
|
||||||
ExtrusionEntitiesPtr extrusions;
|
std::vector<const ExtrusionEntity*> extrusions;
|
||||||
extrusions.reserve(support_fills.entities.size());
|
extrusions.reserve(support_fills.entities.size());
|
||||||
for (ExtrusionEntity* ee : support_fills.entities) {
|
for (const ExtrusionEntity* ee : support_fills.entities) {
|
||||||
const auto role = ee->role();
|
const auto role = ee->role();
|
||||||
if ((role == support_extrusion_role) || (support_extrusion_role == erMixed && role != erIroning)) {
|
if ((role == support_extrusion_role) || (support_extrusion_role == erMixed && role != erIroning)) {
|
||||||
extrusions.emplace_back(ee);
|
extrusions.emplace_back(ee);
|
||||||
@@ -7819,9 +7828,10 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
|
|||||||
if (extrusions.empty())
|
if (extrusions.empty())
|
||||||
return gcode;
|
return gcode;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<ExtrusionEntity>> reversed;
|
||||||
//ORCA: Respect no_sort to preserve support base outline->fill order.
|
//ORCA: Respect no_sort to preserve support base outline->fill order.
|
||||||
if (!support_fills.no_sort)
|
if (!support_fills.no_sort)
|
||||||
chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point());
|
chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point(), reversed);
|
||||||
|
|
||||||
for (const ExtrusionEntity *ee : extrusions) {
|
for (const ExtrusionEntity *ee : extrusions) {
|
||||||
ExtrusionRole role = ee->role();
|
ExtrusionRole role = ee->role();
|
||||||
@@ -10054,8 +10064,8 @@ const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtru
|
|||||||
// Now we are going to iterate through perimeters and infills and pick ones that are supposed to be printed
|
// Now we are going to iterate through perimeters and infills and pick ones that are supposed to be printed
|
||||||
// References are used so that we don't have to repeat the same code
|
// References are used so that we don't have to repeat the same code
|
||||||
for (int iter = 0; iter < 2; ++iter) {
|
for (int iter = 0; iter < 2; ++iter) {
|
||||||
const ExtrusionEntitiesPtr& entities = (iter ? reg.infills : reg.perimeters);
|
const std::vector<const ExtrusionEntity*>& entities = (iter ? reg.infills : reg.perimeters);
|
||||||
ExtrusionEntitiesPtr& target_eec = (iter ? by_region_per_copy_cache.back().infills : by_region_per_copy_cache.back().perimeters);
|
std::vector<const ExtrusionEntity*>& target_eec = (iter ? by_region_per_copy_cache.back().infills : by_region_per_copy_cache.back().perimeters);
|
||||||
const std::vector<const WipingExtrusions::ExtruderPerCopy*>& overrides = (iter ? reg.infills_overrides : reg.perimeters_overrides);
|
const std::vector<const WipingExtrusions::ExtruderPerCopy*>& overrides = (iter ? reg.infills_overrides : reg.perimeters_overrides);
|
||||||
|
|
||||||
// Now the most important thing - which extrusion should we print.
|
// Now the most important thing - which extrusion should we print.
|
||||||
@@ -10090,7 +10100,7 @@ const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtru
|
|||||||
void GCode::ObjectByExtruder::Island::Region::append(const Type type, const ExtrusionEntityCollection* eec, const WipingExtrusions::ExtruderPerCopy* copies_extruder)
|
void GCode::ObjectByExtruder::Island::Region::append(const Type type, const ExtrusionEntityCollection* eec, const WipingExtrusions::ExtruderPerCopy* copies_extruder)
|
||||||
{
|
{
|
||||||
// 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:
|
// 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:
|
||||||
ExtrusionEntitiesPtr* perimeters_or_infills;
|
std::vector<const ExtrusionEntity*>* perimeters_or_infills;
|
||||||
std::vector<const WipingExtrusions::ExtruderPerCopy*>* perimeters_or_infills_overrides;
|
std::vector<const WipingExtrusions::ExtruderPerCopy*>* perimeters_or_infills_overrides;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -10114,7 +10124,7 @@ void GCode::ObjectByExtruder::Island::Region::append(const Type type, const Extr
|
|||||||
for (auto* ee : eec->entities)
|
for (auto* ee : eec->entities)
|
||||||
perimeters_or_infills->emplace_back(ee);
|
perimeters_or_infills->emplace_back(ee);
|
||||||
} else
|
} else
|
||||||
perimeters_or_infills->emplace_back(const_cast<ExtrusionEntityCollection*>(eec));
|
perimeters_or_infills->emplace_back(eec);
|
||||||
|
|
||||||
if (copies_extruder != nullptr) {
|
if (copies_extruder != nullptr) {
|
||||||
// Don't reallocate overrides if not needed.
|
// Don't reallocate overrides if not needed.
|
||||||
|
|||||||
@@ -453,14 +453,14 @@ private:
|
|||||||
std::string extrude_entity(const ExtrusionEntity& entity,
|
std::string extrude_entity(const ExtrusionEntity& entity,
|
||||||
const std::string& description = "",
|
const std::string& description = "",
|
||||||
double speed = -1.,
|
double speed = -1.,
|
||||||
const ExtrusionEntitiesPtr& region_perimeters = ExtrusionEntitiesPtr(),
|
const std::vector<const ExtrusionEntity*>& region_perimeters = {},
|
||||||
const WipeInwardSupport* wipe_support = nullptr);
|
const WipeInwardSupport* wipe_support = nullptr);
|
||||||
// Orca: pass the complete collection of region perimeters to the extrude loop to check whether the wipe before external loop
|
// Orca: pass the complete collection of region perimeters to the extrude loop to check whether the wipe before external loop
|
||||||
// should be executed
|
// should be executed
|
||||||
std::string extrude_loop(const ExtrusionLoop& loop,
|
std::string extrude_loop(const ExtrusionLoop& loop,
|
||||||
const std::string& description,
|
const std::string& description,
|
||||||
double speed = -1.,
|
double speed = -1.,
|
||||||
const ExtrusionEntitiesPtr& region_perimeters = ExtrusionEntitiesPtr(),
|
const std::vector<const ExtrusionEntity*>& region_perimeters = {},
|
||||||
const Point* start_point = nullptr,
|
const Point* start_point = nullptr,
|
||||||
const WipeInwardSupport* wipe_support = nullptr);
|
const WipeInwardSupport* wipe_support = nullptr);
|
||||||
std::string extrude_multi_path(const ExtrusionMultiPath& multipath, const std::string& description = "", double speed = -1.);
|
std::string extrude_multi_path(const ExtrusionMultiPath& multipath, const std::string& description = "", double speed = -1.);
|
||||||
@@ -493,10 +493,9 @@ private:
|
|||||||
{
|
{
|
||||||
struct Region {
|
struct Region {
|
||||||
// Non-owned references to LayerRegion::perimeters::entities
|
// Non-owned references to LayerRegion::perimeters::entities
|
||||||
// std::vector<const ExtrusionEntity*> would be better here, but there is no way in C++ to convert from std::vector<T*> std::vector<const T*> without copying.
|
std::vector<const ExtrusionEntity*> perimeters;
|
||||||
ExtrusionEntitiesPtr perimeters;
|
|
||||||
// Non-owned references to LayerRegion::fills::entities
|
// Non-owned references to LayerRegion::fills::entities
|
||||||
ExtrusionEntitiesPtr infills;
|
std::vector<const ExtrusionEntity*> infills;
|
||||||
|
|
||||||
std::vector<const WipingExtrusions::ExtruderPerCopy*> infills_overrides;
|
std::vector<const WipingExtrusions::ExtruderPerCopy*> infills_overrides;
|
||||||
std::vector<const WipingExtrusions::ExtruderPerCopy*> perimeters_overrides;
|
std::vector<const WipingExtrusions::ExtruderPerCopy*> perimeters_overrides;
|
||||||
|
|||||||
@@ -180,6 +180,10 @@ enum class IroningType {
|
|||||||
Count,
|
Count,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Smallest usable ironing line spacing. Anything tighter yields an unprintable number of lines,
|
||||||
|
// and zero stops the fillers from making progress.
|
||||||
|
constexpr double IRONING_SPACING_MIN = 0.05;
|
||||||
|
|
||||||
//BBS
|
//BBS
|
||||||
enum class WallInfillOrder {
|
enum class WallInfillOrder {
|
||||||
InnerOuterInfill,
|
InnerOuterInfill,
|
||||||
|
|||||||
@@ -1024,13 +1024,14 @@ std::vector<std::pair<size_t, bool>> chain_segments_greedy2(SegmentEndPointFunc
|
|||||||
return chain_segments_greedy_constrained_reversals2_<PointType, SegmentEndPointFunc, false, decltype(could_reverse_func)>(end_point_func, could_reverse_func, num_segments, start_near);
|
return chain_segments_greedy_constrained_reversals2_<PointType, SegmentEndPointFunc, false, decltype(could_reverse_func)>(end_point_func, could_reverse_func, num_segments, start_near);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
|
template<typename EntityPtr>
|
||||||
|
static std::vector<std::pair<size_t, bool>> chain_extrusion_entities_impl(const std::vector<EntityPtr> &entities, const Point *start_near)
|
||||||
{
|
{
|
||||||
auto segment_end_point = [&entities](size_t idx, bool first_point) -> Point { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); };
|
auto segment_end_point = [&entities](size_t idx, bool first_point) -> Point { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); };
|
||||||
auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); };
|
auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); };
|
||||||
std::vector<std::pair<size_t, bool>> out = chain_segments_greedy_constrained_reversals<Point, decltype(segment_end_point), decltype(could_reverse)>(segment_end_point, could_reverse, entities.size(), start_near);
|
std::vector<std::pair<size_t, bool>> out = chain_segments_greedy_constrained_reversals<Point, decltype(segment_end_point), decltype(could_reverse)>(segment_end_point, could_reverse, entities.size(), start_near);
|
||||||
for (std::pair<size_t, bool> &segment : out) {
|
for (std::pair<size_t, bool> &segment : out) {
|
||||||
ExtrusionEntity *ee = entities[segment.first];
|
const ExtrusionEntity *ee = entities[segment.first];
|
||||||
if (ee->is_loop())
|
if (ee->is_loop())
|
||||||
// Ignore reversals for loops, as the start point equals the end point.
|
// Ignore reversals for loops, as the start point equals the end point.
|
||||||
segment.second = false;
|
segment.second = false;
|
||||||
@@ -1040,6 +1041,20 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
|
||||||
|
{
|
||||||
|
return chain_extrusion_entities_impl(entities, start_near);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Orca: Reordering queries first_point() / last_point(); drop entities that cannot provide valid endpoints.
|
||||||
|
template<typename EntityPtr>
|
||||||
|
static void remove_entities_without_endpoints(std::vector<EntityPtr> &entities)
|
||||||
|
{
|
||||||
|
entities.erase(std::remove_if(entities.begin(), entities.end(),
|
||||||
|
[](const ExtrusionEntity *entity) { return !extrusion_entity_has_endpoints(entity); }),
|
||||||
|
entities.end());
|
||||||
|
}
|
||||||
|
|
||||||
void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const std::vector<std::pair<size_t, bool>> &chain)
|
void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const std::vector<std::pair<size_t, bool>> &chain)
|
||||||
{
|
{
|
||||||
assert(entities.size() == chain.size());
|
assert(entities.size() == chain.size());
|
||||||
@@ -1061,14 +1076,27 @@ void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entitie
|
|||||||
|
|
||||||
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
|
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
|
||||||
{
|
{
|
||||||
// Orca: Reordering queries first_point() / last_point(); drop entities that cannot provide valid endpoints.
|
remove_entities_without_endpoints(entities);
|
||||||
entities.erase(std::remove_if(entities.begin(), entities.end(), [](ExtrusionEntity *entity) {
|
|
||||||
return !extrusion_entity_has_endpoints(entity);
|
|
||||||
}),
|
|
||||||
entities.end());
|
|
||||||
reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near));
|
reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void chain_and_reorder_extrusion_entities(std::vector<const ExtrusionEntity*> &entities, const Point &start_near,
|
||||||
|
std::vector<std::unique_ptr<ExtrusionEntity>> &reversed_clones)
|
||||||
|
{
|
||||||
|
remove_entities_without_endpoints(entities);
|
||||||
|
std::vector<const ExtrusionEntity*> out;
|
||||||
|
out.reserve(entities.size());
|
||||||
|
for (const auto &[idx, reverse] : chain_extrusion_entities_impl(entities, &start_near)) {
|
||||||
|
if (reverse) {
|
||||||
|
ExtrusionEntity *clone = reversed_clones.emplace_back(entities[idx]->clone()).get();
|
||||||
|
clone->reverse();
|
||||||
|
out.emplace_back(clone);
|
||||||
|
} else
|
||||||
|
out.emplace_back(entities[idx]);
|
||||||
|
}
|
||||||
|
entities.swap(out);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near)
|
std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near)
|
||||||
{
|
{
|
||||||
auto segment_end_point = [&extrusion_paths](size_t idx, bool first_point) -> Point { return first_point ? extrusion_paths[idx].first_point() : extrusion_paths[idx].last_point(); };
|
auto segment_end_point = [&extrusion_paths](size_t idx, bool first_point) -> Point { return first_point ? extrusion_paths[idx].first_point() : extrusion_paths[idx].last_point(); };
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "ExtrusionEntity.hpp"
|
#include "ExtrusionEntity.hpp"
|
||||||
#include "Point.hpp"
|
#include "Point.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -24,6 +25,9 @@ std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<Extrus
|
|||||||
void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const std::vector<std::pair<size_t, bool>> &chain);
|
void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const std::vector<std::pair<size_t, bool>> &chain);
|
||||||
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point &start_near);
|
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point &start_near);
|
||||||
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
|
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
|
||||||
|
// Each entity the chain reverses is replaced by a reversed clone that reversed_clones owns, so the originals stay unchanged.
|
||||||
|
void chain_and_reorder_extrusion_entities(std::vector<const ExtrusionEntity*> &entities, const Point &start_near,
|
||||||
|
std::vector<std::unique_ptr<ExtrusionEntity>> &reversed_clones);
|
||||||
|
|
||||||
std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near = nullptr);
|
std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near = nullptr);
|
||||||
void reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, std::vector<std::pair<size_t, bool>> &chain);
|
void reorder_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, std::vector<std::pair<size_t, bool>> &chain);
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ struct SupportParameters {
|
|||||||
|
|
||||||
this->ironing = object_config.support_ironing;
|
this->ironing = object_config.support_ironing;
|
||||||
this->ironing_flow = support_material_interface_flow.with_height(support_material_interface_flow.height() * 0.01 * object_config.support_ironing_flow.value);
|
this->ironing_flow = support_material_interface_flow.with_height(support_material_interface_flow.height() * 0.01 * object_config.support_ironing_flow.value);
|
||||||
this->ironing_spacing = object_config.support_ironing_spacing;
|
this->ironing_spacing = std::max(IRONING_SPACING_MIN, object_config.support_ironing_spacing.value);
|
||||||
this->ironing_pattern = object_config.support_ironing_pattern;
|
this->ironing_pattern = object_config.support_ironing_pattern;
|
||||||
|
|
||||||
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
|
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
|
||||||
|
|||||||
@@ -212,6 +212,30 @@ void ConfigManipulation::check_filament_max_volumetric_speed(DynamicPrintConfig
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigManipulation::check_filament_ironing_spacing(DynamicPrintConfig *config)
|
||||||
|
{
|
||||||
|
const auto *opt = config->option<ConfigOptionFloatsNullable>("filament_ironing_spacing");
|
||||||
|
if (opt == nullptr)
|
||||||
|
return;
|
||||||
|
std::vector<double> values = opt->values;
|
||||||
|
bool reset = false;
|
||||||
|
for (size_t i = 0; i < values.size(); ++i)
|
||||||
|
if (!opt->is_nil(i) && values[i] < IRONING_SPACING_MIN) {
|
||||||
|
values[i] = 0.1;
|
||||||
|
reset = true;
|
||||||
|
}
|
||||||
|
if (!reset)
|
||||||
|
return;
|
||||||
|
const wxString msg_text = _(L("Ironing spacing too small\nIt has been reset to 0.1"));
|
||||||
|
MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK);
|
||||||
|
DynamicPrintConfig new_conf = *config;
|
||||||
|
is_msg_dlg_already_exist = true;
|
||||||
|
dialog.ShowModal();
|
||||||
|
new_conf.set_key_value("filament_ironing_spacing", new ConfigOptionFloatsNullable(values));
|
||||||
|
apply(config, &new_conf);
|
||||||
|
is_msg_dlg_already_exist = false;
|
||||||
|
}
|
||||||
|
|
||||||
void ConfigManipulation::check_chamber_temperature(DynamicPrintConfig* config)
|
void ConfigManipulation::check_chamber_temperature(DynamicPrintConfig* config)
|
||||||
{
|
{
|
||||||
bool support_chamber_temp_control=GUI::wxGetApp().preset_bundle->printers.get_selected_preset().config.opt_bool("support_chamber_temp_control");
|
bool support_chamber_temp_control=GUI::wxGetApp().preset_bundle->printers.get_selected_preset().config.opt_bool("support_chamber_temp_control");
|
||||||
@@ -334,7 +358,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
|||||||
}
|
}
|
||||||
|
|
||||||
//BBS: ironing_spacing shouldn't be too small or equal to zero
|
//BBS: ironing_spacing shouldn't be too small or equal to zero
|
||||||
if (config->opt_float("ironing_spacing") < 0.05)
|
if (config->opt_float("ironing_spacing") < IRONING_SPACING_MIN)
|
||||||
{
|
{
|
||||||
const wxString msg_text = _(L("Ironing spacing too small\nIt has been reset to 0.1"));
|
const wxString msg_text = _(L("Ironing spacing too small\nIt has been reset to 0.1"));
|
||||||
MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK);
|
MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK);
|
||||||
@@ -345,7 +369,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
|||||||
apply(config, &new_conf);
|
apply(config, &new_conf);
|
||||||
is_msg_dlg_already_exist = false;
|
is_msg_dlg_already_exist = false;
|
||||||
}
|
}
|
||||||
if (config->opt_float("support_ironing_spacing") < 0.05)
|
if (config->opt_float("support_ironing_spacing") < IRONING_SPACING_MIN)
|
||||||
{
|
{
|
||||||
const wxString msg_text = _(L("Ironing spacing too small\nIt has been reset to 0.1"));
|
const wxString msg_text = _(L("Ironing spacing too small\nIt has been reset to 0.1"));
|
||||||
MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK);
|
MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK);
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ public:
|
|||||||
void check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config, unsigned int variant_index);
|
void check_nozzle_temperature_initial_layer_range(DynamicPrintConfig* config, unsigned int variant_index);
|
||||||
void check_adaptive_pressure_advance_model(DynamicPrintConfig* config);
|
void check_adaptive_pressure_advance_model(DynamicPrintConfig* config);
|
||||||
void check_filament_max_volumetric_speed(DynamicPrintConfig *config);
|
void check_filament_max_volumetric_speed(DynamicPrintConfig *config);
|
||||||
|
void check_filament_ironing_spacing(DynamicPrintConfig *config);
|
||||||
void check_chamber_temperature(DynamicPrintConfig* config);
|
void check_chamber_temperature(DynamicPrintConfig* config);
|
||||||
void check_chamber_minimal_temperature(DynamicPrintConfig* config);
|
void check_chamber_minimal_temperature(DynamicPrintConfig* config);
|
||||||
bool check_layer_height(DynamicPrintConfig* config);
|
bool check_layer_height(DynamicPrintConfig* config);
|
||||||
|
|||||||
@@ -4911,6 +4911,7 @@ void TabFilament::update()
|
|||||||
return; // ys_FIXME
|
return; // ys_FIXME
|
||||||
|
|
||||||
m_config_manipulation.check_filament_max_volumetric_speed(m_config);
|
m_config_manipulation.check_filament_max_volumetric_speed(m_config);
|
||||||
|
m_config_manipulation.check_filament_ironing_spacing(m_config);
|
||||||
|
|
||||||
m_update_cnt++;
|
m_update_cnt++;
|
||||||
|
|
||||||
|
|||||||
@@ -748,6 +748,61 @@ TEST_CASE("A region with ironing turned off is never ironed", "[Fill]")
|
|||||||
REQUIRE(Layer::choose_ironing_extruder(cfg, spiral_mode, /*is_topmost_layer=*/true) == -1);
|
REQUIRE(Layer::choose_ironing_extruder(cfg, spiral_mode, /*is_topmost_layer=*/true) == -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ironing path count and total length in mm, over the whole object.
|
||||||
|
static std::pair<size_t, double> ironing_extent(const Print &print)
|
||||||
|
{
|
||||||
|
size_t paths = 0;
|
||||||
|
double length = 0.;
|
||||||
|
for (const Layer *layer : print.objects().front()->layers())
|
||||||
|
for (const LayerRegion *region : layer->regions())
|
||||||
|
for (const ExtrusionEntity *entity : region->fills.flatten().entities)
|
||||||
|
if (ironing_role(entity->role())) {
|
||||||
|
++paths;
|
||||||
|
length += unscale<double>(entity->length());
|
||||||
|
}
|
||||||
|
return {paths, length};
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Ironing spacing below the minimum irons at the minimum spacing", "[Fill]")
|
||||||
|
{
|
||||||
|
const std::string pattern = GENERATE("rectilinear", "concentric");
|
||||||
|
const bool via_filament = GENERATE(false, true);
|
||||||
|
const double spacing = GENERATE(0., 0.001);
|
||||||
|
CAPTURE(pattern, via_filament, spacing);
|
||||||
|
|
||||||
|
auto ironing_for = [&pattern, via_filament](double spacing) {
|
||||||
|
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||||
|
config.set_deserialize_strict({{"ironing_type", "top"},
|
||||||
|
{"ironing_pattern", pattern},
|
||||||
|
{"layer_height", 0.2}});
|
||||||
|
// The filament override replaces the process spacing, which stays at a usable value.
|
||||||
|
if (via_filament)
|
||||||
|
config.set_deserialize_strict({{"ironing_spacing", 0.1}, {"filament_ironing_spacing", spacing}});
|
||||||
|
else
|
||||||
|
config.set_deserialize_strict({{"ironing_spacing", spacing}});
|
||||||
|
Print print;
|
||||||
|
Slic3r::Test::init_and_process_print({Slic3r::Test::cube(20)}, print, config);
|
||||||
|
return ironing_extent(print);
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::pair<size_t, double> clamped = ironing_for(spacing);
|
||||||
|
const std::pair<size_t, double> minimum = ironing_for(IRONING_SPACING_MIN);
|
||||||
|
REQUIRE(minimum.first > 0);
|
||||||
|
CHECK(clamped.first == minimum.first);
|
||||||
|
CHECK_THAT(clamped.second, Catch::Matchers::WithinRel(minimum.second, 1e-9));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Concentric fill at zero spacing returns without paths", "[Fill]")
|
||||||
|
{
|
||||||
|
std::unique_ptr<Fill> filler(Fill::new_from_type(ipConcentric));
|
||||||
|
filler->spacing = 0.;
|
||||||
|
filler->bounding_box = BoundingBox(Point(0, 0), Point::new_scale(10, 10));
|
||||||
|
FillParams params;
|
||||||
|
params.density = 1.f;
|
||||||
|
Surface surface(stTop, ExPolygon({Point(0, 0), Point::new_scale(10, 0), Point::new_scale(10, 10), Point::new_scale(0, 10)}));
|
||||||
|
CHECK(filler->fill_surface(&surface, params).empty());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Solid infill direction offsets every layer when no template is set", "[Fill]")
|
TEST_CASE("Solid infill direction offsets every layer when no template is set", "[Fill]")
|
||||||
{
|
{
|
||||||
auto angles_for = [](int direction) {
|
auto angles_for = [](int direction) {
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ DynamicPrintConfig multifilament_config(unsigned int filaments, std::initializer
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init_print(std::vector<TriangleMesh> &&meshes, Slic3r::Print &print, Slic3r::Model &model, const DynamicPrintConfig &config_in,
|
void init_print(std::vector<TriangleMesh> &&meshes, Slic3r::Print &print, Slic3r::Model &model, const DynamicPrintConfig &config_in,
|
||||||
const std::vector<std::vector<ConfigBase::SetDeserializeItem>> *per_object_overrides, bool arrange)
|
const std::vector<std::vector<ConfigBase::SetDeserializeItem>> *per_object_overrides, bool arrange, size_t instances)
|
||||||
{
|
{
|
||||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||||
config.apply(config_in);
|
config.apply(config_in);
|
||||||
@@ -236,6 +236,7 @@ void init_print(std::vector<TriangleMesh> &&meshes, Slic3r::Print &print, Slic3r
|
|||||||
ModelObject *object = model.add_object();
|
ModelObject *object = model.add_object();
|
||||||
object->name += "object.stl";
|
object->name += "object.stl";
|
||||||
object->add_volume(std::move(t));
|
object->add_volume(std::move(t));
|
||||||
|
for (size_t i = 0; i < instances; ++i)
|
||||||
object->add_instance();
|
object->add_instance();
|
||||||
|
|
||||||
if (per_object_overrides && object_idx < per_object_overrides->size() && !(*per_object_overrides)[object_idx].empty()) {
|
if (per_object_overrides && object_idx < per_object_overrides->size() && !(*per_object_overrides)[object_idx].empty()) {
|
||||||
|
|||||||
@@ -72,9 +72,11 @@ Slic3r::Model model(const std::string& model_name, TriangleMesh&& _mesh);
|
|||||||
DynamicPrintConfig multifilament_config(unsigned int filaments,
|
DynamicPrintConfig multifilament_config(unsigned int filaments,
|
||||||
std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> extra = {});
|
std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> extra = {});
|
||||||
|
|
||||||
// Apply `meshes` and config to `print`/`model`; optional per-object overrides, auto-arranged unless `arrange` is false.
|
// Apply `meshes` and config to `print`/`model`, each object with `instances` copies; optional per-object overrides,
|
||||||
|
// auto-arranged unless `arrange` is false.
|
||||||
void init_print(std::vector<TriangleMesh> &&meshes, Slic3r::Print &print, Slic3r::Model &model, const DynamicPrintConfig &config_in,
|
void init_print(std::vector<TriangleMesh> &&meshes, Slic3r::Print &print, Slic3r::Model &model, const DynamicPrintConfig &config_in,
|
||||||
const std::vector<std::vector<Slic3r::ConfigBase::SetDeserializeItem>> *per_object_overrides = nullptr, bool arrange = true);
|
const std::vector<std::vector<Slic3r::ConfigBase::SetDeserializeItem>> *per_object_overrides = nullptr, bool arrange = true,
|
||||||
|
size_t instances = 1);
|
||||||
void init_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, Slic3r::Model &model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config());
|
void init_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, Slic3r::Model &model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config());
|
||||||
void init_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Print &print, Slic3r::Model &model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config());
|
void init_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Print &print, Slic3r::Model &model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config());
|
||||||
void init_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, Slic3r::Model &model, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items);
|
void init_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, Slic3r::Model &model, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items);
|
||||||
|
|||||||
@@ -561,6 +561,40 @@ TEST_CASE("export_gcode writes G-code without a result pointer", "[Print][export
|
|||||||
REQUIRE_FALSE(gcode.empty());
|
REQUIRE_FALSE(gcode.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Exporting a sliced print again gives the same G-code", "[Print][export_gcode][Regression]")
|
||||||
|
{
|
||||||
|
const int instances = GENERATE(1, 3);
|
||||||
|
CAPTURE(instances);
|
||||||
|
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||||
|
TestMesh mesh = TestMesh::ipadstand;
|
||||||
|
SECTION("infill reversed by chaining") { config.set_deserialize_strict({{"sparse_infill_pattern", "gyroid"}}); }
|
||||||
|
SECTION("support reversed by chaining") {
|
||||||
|
mesh = TestMesh::overhang;
|
||||||
|
config.set_deserialize_strict({{"enable_support", true}, {"support_interface_pattern", "concentric"}});
|
||||||
|
}
|
||||||
|
Print print;
|
||||||
|
Model model;
|
||||||
|
Slic3r::Test::init_print({Slic3r::Test::mesh(mesh)}, print, model, config, nullptr, true, instances);
|
||||||
|
|
||||||
|
const auto export_without_timestamp = [&print]() {
|
||||||
|
std::string gcode = Slic3r::Test::gcode(print);
|
||||||
|
const size_t line = gcode.find("; generated by ");
|
||||||
|
REQUIRE(line != std::string::npos);
|
||||||
|
gcode.erase(line, gcode.find('\n', line) - line);
|
||||||
|
return gcode;
|
||||||
|
};
|
||||||
|
const std::string first = export_without_timestamp();
|
||||||
|
const std::string second = export_without_timestamp();
|
||||||
|
|
||||||
|
// Shows the first differing line on failure.
|
||||||
|
const size_t diff = std::mismatch(first.begin(), first.end(), second.begin(), second.end()).first - first.begin();
|
||||||
|
const size_t line_start = diff == 0 ? 0 : first.rfind('\n', diff - 1) + 1;
|
||||||
|
INFO("first export: " << first.substr(line_start, first.find('\n', diff) - line_start));
|
||||||
|
INFO("second export: " << second.substr(line_start, second.find('\n', diff) - line_start));
|
||||||
|
CHECK(diff == first.size());
|
||||||
|
CHECK(first.size() == second.size());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Sequential printing follows model order", "[Print]")
|
TEST_CASE("Sequential printing follows model order", "[Print]")
|
||||||
{
|
{
|
||||||
// Two objects of different heights, taller one added first. Orca prints
|
// Two objects of different heights, taller one added first. Orca prints
|
||||||
|
|||||||
Reference in New Issue
Block a user